Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified docs/management/boards/img/create-board-modal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/management/boards/img/layout-settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions docs/management/boards/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import itemRadiusImage from './img/item-radius.png'
import layoutLargeImage from './img/layout-large.png'
import layoutMediumImage from './img/layout-medium.png'
import layoutSmallImage from './img/layout-small.png'
import {GridLayout} from '@site/src/components/grid-layout';

Each dashboard is called a "board".
You can create as many boards as you want.
Expand All @@ -32,6 +33,14 @@ When creating a board, the availability of the board name will be checked for yo
The name must not include any special characters and must be at least one character long.
Underscores and hyphen-minus are allowed.

### Layout mode

There are two layout modes available. They define how the board will behave on different screen sizes.
- **Auto:** The board items will adjust for different screen sizes automatically based on the base layout.
- **Custom:** The board items have to be adjusted manually for different screen sizes.

For visualization of the two modes, see [here](#visualizations).

### Column count
Homarr has a complex drag and drop system that can be used from the browser.
The column count defines how many horizontal columns are available to drag and drop elements to.
Expand Down Expand Up @@ -84,6 +93,59 @@ On each element the position and size of items can be changed seperately.
When adding a new layout the position and sizes will automatically be generated depending on the column count.
When adding a new item / dynamic section that will always be 1x1 on each layout and placed on the first available position.

#### Layout mode

There are two layout modes available. They define how the board will behave on different screen sizes.
- **Auto:** The board items will adjust for different screen sizes automatically based on the base layout
- **Custom:** The board items have to be adjusted manually for different screen sizes.

##### Visualizations

This is how the different layout modes behave on different screen sizes with the 8 column layout as base:

<div class="flex items-start">
<GridLayout gap={8} columns={8} width={256} items={[
{ xOffset: 0, yOffset: 0, content: '1'},
{ xOffset: 1, yOffset: 0, width: 2, content: '2'},
{ xOffset: 3, yOffset: 0, width: 3, height: 2, content: '3'},
{ xOffset: 6, yOffset: 0, width: 2, content: '4'},
{ xOffset: 0, yOffset: 1, width: 2, content: '5'},
{ xOffset: 2, yOffset: 1, content: '6'},
{ xOffset: 6, yOffset: 1, width: 2, height: 2, content: '7'},
]} />

<GridLayout gap={8} columns={4} width={128} items={[
{ xOffset: 0, yOffset: 0, content: '1'},
{ xOffset: 1, yOffset: 0, width: 2, content: '2'},
{ xOffset: 3, yOffset: 0, content: '6'},
{ xOffset: 0, yOffset: 1, width: 3, height: 2, content: '3'},
{ xOffset: 0, yOffset: 3, width: 2, content: '4'},
{ xOffset: 2, yOffset: 3, width: 2, content: '5'},
{ xOffset: 0, yOffset: 4, width: 2, height: 2, content: '7'},
]} />

<GridLayout gap={8} columns={2} width={64} items={[
{ xOffset: 0, yOffset: 0, content: '1'},
{ xOffset: 1, yOffset: 0, content: '6'},
{ xOffset: 0, yOffset: 1, width: 2, content: '2'},
{ xOffset: 0, yOffset: 2, width: 2, height: 2, content: '3'},
{ xOffset: 0, yOffset: 4, width: 2, content: '4'},
{ xOffset: 0, yOffset: 5, width: 2, content: '5'},
{ xOffset: 0, yOffset: 6, width: 2, height: 2, content: '7'},
]} />
</div>

Order of items is defined by their position (top left to bottom right, row for row).
If the next item does not fit in the current row, it will be placed in the next row (see 4 in medium layout).
If an item is small enough to fit into one of the gaps, it will be placed there (see 6 in medium layout).
If the size of an item is to large to fix into the amount of columns available, it will automatically be resized to fit the entire width (see 3 in last layout).

#### Base layout

When using the layout mode **Auto** one of the layouts has to be defined as the base layout.
It will be used to calculate position and size of items for other responsive layouts automatically.


#### Name

The name of the layout can be choosen freely. It is only used for the management and will not be visible on the board.
Expand Down
59 changes: 59 additions & 0 deletions src/components/grid-layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
interface GridItem {
xOffset: number;
yOffset: number;
width?: number;
height?: number;
content: string;
}

interface GridLayoutProps {
gap: number;
width: number;
columns: number;
items: GridItem[];
}

export const GridLayout = (props: GridLayoutProps) => {
const size = (props.width - props.gap * (props.columns - 1)) / props.columns;
const rows = Math.max(...props.items.map((item) => item.yOffset + (item.height ?? 1)));

return (
<div
className="bg-[#fa5252] grid p-1 m-2 h-min"
style={{
gridTemplateColumns: `repeat(${props.columns}, ${size}px)`,
gridTemplateRows: `repeat(${rows}, ${size}px)`,
gap: props.gap,
width: props.width + 8, // padding
}}
>
{props.items.map((item) => (
<GridItem key={item.content} item={item} size={size} gap={props.gap} />
))}
</div>
);
};

interface GridItemProps {
item: GridItem;
size: number;
gap: number;
}

const GridItem = (props: GridItemProps) => {
return (
<div
className="bg-gray-600 flex justify-center items-center rounded-md"
style={{
width: (props.item.width ?? 1) * props.size + ((props.item.width ?? 1) - 1) * props.gap,
height: (props.item.height ?? 1) * props.size + ((props.item.height ?? 1) - 1) * props.gap,
gridColumnStart: props.item.xOffset + 1,
gridColumnEnd: props.item.xOffset + (props.item.width ?? 1) + 1,
gridRowStart: props.item.yOffset + 1,
gridRowEnd: props.item.yOffset + (props.item.height ?? 1) + 1,
}}
>
{props.item.content}
</div>
);
};