feat(boards): add responsive layout system (#2271)
This commit is contained in:
@@ -4,7 +4,6 @@ import type { boards } from "@homarr/db/schema";
|
||||
|
||||
import type { prepareMultipleImports } from "../prepare/prepare-multiple";
|
||||
import { mapColor } from "./map-colors";
|
||||
import { mapColumnCount } from "./map-column-count";
|
||||
|
||||
type PreparedBoard = ReturnType<typeof prepareMultipleImports>["preparedBoards"][number];
|
||||
|
||||
@@ -15,7 +14,6 @@ export const mapBoard = (preparedBoard: PreparedBoard): InferInsertModel<typeof
|
||||
backgroundImageUrl: preparedBoard.config.settings.customization.backgroundImageUrl,
|
||||
backgroundImageRepeat: preparedBoard.config.settings.customization.backgroundImageRepeat,
|
||||
backgroundImageSize: preparedBoard.config.settings.customization.backgroundImageSize,
|
||||
columnCount: mapColumnCount(preparedBoard.config, preparedBoard.size),
|
||||
faviconImageUrl: preparedBoard.config.settings.customization.faviconUrl,
|
||||
isPublic: preparedBoard.config.settings.access.allowGuests,
|
||||
logoImageUrl: preparedBoard.config.settings.customization.logoImageUrl,
|
||||
|
||||
18
packages/old-import/src/mappers/map-breakpoint.ts
Normal file
18
packages/old-import/src/mappers/map-breakpoint.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { BoardSize } from "@homarr/old-schema";
|
||||
|
||||
/**
|
||||
* Copied from https://github.com/ajnart/homarr/blob/274eaa92084a8be4d04a69a87f9920860a229128/src/components/Dashboard/Wrappers/gridstack/store.tsx#L21-L30
|
||||
* @param screenSize board size
|
||||
* @returns layout breakpoint for the board
|
||||
*/
|
||||
export const mapBreakpoint = (screenSize: BoardSize) => {
|
||||
switch (screenSize) {
|
||||
case "lg":
|
||||
return 1400;
|
||||
case "md":
|
||||
return 800;
|
||||
case "sm":
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
@@ -1,8 +1,6 @@
|
||||
import type { OldmarrConfig } from "@homarr/old-schema";
|
||||
import type { BoardSize, OldmarrConfig } from "@homarr/old-schema";
|
||||
|
||||
import type { OldmarrImportConfiguration } from "../settings";
|
||||
|
||||
export const mapColumnCount = (old: OldmarrConfig, screenSize: OldmarrImportConfiguration["screenSize"]) => {
|
||||
export const mapColumnCount = (old: OldmarrConfig, screenSize: BoardSize) => {
|
||||
switch (screenSize) {
|
||||
case "lg":
|
||||
return old.settings.customization.gridstack.columnCountLarge;
|
||||
|
||||
@@ -2,9 +2,10 @@ import SuperJSON from "superjson";
|
||||
|
||||
import type { InferInsertModel } from "@homarr/db";
|
||||
import { createId } from "@homarr/db";
|
||||
import type { items } from "@homarr/db/schema";
|
||||
import type { itemLayouts, items } from "@homarr/db/schema";
|
||||
import { logger } from "@homarr/log";
|
||||
import type { BoardSize, OldmarrApp, OldmarrWidget } from "@homarr/old-schema";
|
||||
import { boardSizes } from "@homarr/old-schema";
|
||||
|
||||
import type { WidgetComponentProps } from "../../../widgets/src/definition";
|
||||
import { mapKind } from "../widgets/definitions";
|
||||
@@ -12,30 +13,23 @@ import { mapOptions } from "../widgets/options";
|
||||
|
||||
export const mapApp = (
|
||||
app: OldmarrApp,
|
||||
boardSize: BoardSize,
|
||||
appsMap: Map<string, { id: string }>,
|
||||
sectionMap: Map<string, { id: string }>,
|
||||
): InferInsertModel<typeof items> | null => {
|
||||
layoutMap: Record<BoardSize, string>,
|
||||
boardId: string,
|
||||
): (InferInsertModel<typeof items> & { layouts: InferInsertModel<typeof itemLayouts>[] }) | null => {
|
||||
if (app.area.type === "sidebar") throw new Error("Mapping app in sidebar is not supported");
|
||||
|
||||
const shapeForSize = app.shape[boardSize];
|
||||
if (!shapeForSize) {
|
||||
throw new Error(`Failed to find a shape for appId='${app.id}' screenSize='${boardSize}'`);
|
||||
}
|
||||
|
||||
const sectionId = sectionMap.get(app.area.properties.id)?.id;
|
||||
if (!sectionId) {
|
||||
logger.warn(`Failed to find section for app appId='${app.id}' sectionId='${app.area.properties.id}'. Removing app`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const itemId = createId();
|
||||
return {
|
||||
id: createId(),
|
||||
sectionId,
|
||||
height: shapeForSize.size.height,
|
||||
width: shapeForSize.size.width,
|
||||
xOffset: shapeForSize.location.x,
|
||||
yOffset: shapeForSize.location.y,
|
||||
id: itemId,
|
||||
boardId,
|
||||
kind: "app",
|
||||
options: SuperJSON.stringify({
|
||||
// it's safe to assume that the app exists in the map
|
||||
@@ -46,22 +40,34 @@ export const mapApp = (
|
||||
showDescriptionTooltip: app.behaviour.tooltipDescription !== "",
|
||||
showTitle: app.appearance.appNameStatus === "normal",
|
||||
} satisfies WidgetComponentProps<"app">["options"]),
|
||||
layouts: boardSizes.map((size) => {
|
||||
const shapeForSize = app.shape[size];
|
||||
if (!shapeForSize) {
|
||||
throw new Error(`Failed to find a shape for appId='${app.id}' screenSize='${size}'`);
|
||||
}
|
||||
|
||||
return {
|
||||
itemId,
|
||||
height: shapeForSize.size.height,
|
||||
width: shapeForSize.size.width,
|
||||
xOffset: shapeForSize.location.x,
|
||||
yOffset: shapeForSize.location.y,
|
||||
sectionId,
|
||||
layoutId: layoutMap[size],
|
||||
};
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
export const mapWidget = (
|
||||
widget: OldmarrWidget,
|
||||
boardSize: BoardSize,
|
||||
appsMap: Map<string, { id: string }>,
|
||||
sectionMap: Map<string, { id: string }>,
|
||||
): InferInsertModel<typeof items> | null => {
|
||||
layoutMap: Record<BoardSize, string>,
|
||||
boardId: string,
|
||||
): (InferInsertModel<typeof items> & { layouts: InferInsertModel<typeof itemLayouts>[] }) | null => {
|
||||
if (widget.area.type === "sidebar") throw new Error("Mapping widget in sidebar is not supported");
|
||||
|
||||
const shapeForSize = widget.shape[boardSize];
|
||||
if (!shapeForSize) {
|
||||
throw new Error(`Failed to find a shape for widgetId='${widget.id}' screenSize='${boardSize}'`);
|
||||
}
|
||||
|
||||
const kind = mapKind(widget.type);
|
||||
if (!kind) {
|
||||
logger.warn(`Failed to map widget type='${widget.type}'. It's no longer supported`);
|
||||
@@ -76,13 +82,10 @@ export const mapWidget = (
|
||||
return null;
|
||||
}
|
||||
|
||||
const itemId = createId();
|
||||
return {
|
||||
id: createId(),
|
||||
sectionId,
|
||||
height: shapeForSize.size.height,
|
||||
width: shapeForSize.size.width,
|
||||
xOffset: shapeForSize.location.x,
|
||||
yOffset: shapeForSize.location.y,
|
||||
id: itemId,
|
||||
boardId,
|
||||
kind,
|
||||
options: SuperJSON.stringify(
|
||||
mapOptions(
|
||||
@@ -91,5 +94,21 @@ export const mapWidget = (
|
||||
new Map([...appsMap.entries()].map(([key, value]) => [key, value.id])),
|
||||
),
|
||||
),
|
||||
layouts: boardSizes.map((size) => {
|
||||
const shapeForSize = widget.shape[size];
|
||||
if (!shapeForSize) {
|
||||
throw new Error(`Failed to find a shape for widgetId='${widget.id}' screenSize='${size}'`);
|
||||
}
|
||||
|
||||
return {
|
||||
itemId,
|
||||
height: shapeForSize.size.height,
|
||||
width: shapeForSize.size.width,
|
||||
xOffset: shapeForSize.location.x,
|
||||
yOffset: shapeForSize.location.y,
|
||||
sectionId,
|
||||
layoutId: layoutMap[size],
|
||||
};
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user