feat(boards): add responsive layout system (#2271)
This commit is contained in:
@@ -4,7 +4,6 @@ import SuperJSON from "superjson";
|
||||
|
||||
import { revalidatePathActionAsync } from "@homarr/common/client";
|
||||
import { useModalAction } from "@homarr/modals";
|
||||
import { boardSizes } from "@homarr/old-schema";
|
||||
|
||||
// We don't have access to the API client here, so we need to import it from the API package
|
||||
// In the future we should consider having the used router also in this package
|
||||
@@ -13,7 +12,6 @@ import type { AnalyseResult } from "../analyse/analyse-oldmarr-import";
|
||||
import { prepareMultipleImports } from "../prepare/prepare-multiple";
|
||||
import type { InitialOldmarrImportSettings } from "../settings";
|
||||
import { defaultSidebarBehaviour } from "../settings";
|
||||
import type { BoardSelectionMap, BoardSizeRecord } from "./initial/board-selection-card";
|
||||
import { BoardSelectionCard } from "./initial/board-selection-card";
|
||||
import { ImportSettingsCard } from "./initial/import-settings-card";
|
||||
import { ImportSummaryCard } from "./initial/import-summary-card";
|
||||
@@ -25,8 +23,8 @@ interface InitialOldmarrImportProps {
|
||||
}
|
||||
|
||||
export const InitialOldmarrImport = ({ file, analyseResult }: InitialOldmarrImportProps) => {
|
||||
const [boardSelections, setBoardSelections] = useState<BoardSelectionMap>(
|
||||
new Map(createDefaultSelections(analyseResult.configs)),
|
||||
const [boardSelections, setBoardSelections] = useState<Map<string, boolean>>(
|
||||
new Map(analyseResult.configs.filter(({ config }) => config !== null).map(({ name }) => [name, true])),
|
||||
);
|
||||
const [settings, setSettings] = useState<InitialOldmarrImportSettings>({
|
||||
onlyImportApps: false,
|
||||
@@ -94,19 +92,3 @@ export const InitialOldmarrImport = ({ file, analyseResult }: InitialOldmarrImpo
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const createDefaultSelections = (configs: AnalyseResult["configs"]) => {
|
||||
return configs
|
||||
.map(({ name, config }) => {
|
||||
if (!config) return null;
|
||||
|
||||
const shapes = config.apps.flatMap((app) => app.shape).concat(config.widgets.flatMap((widget) => widget.shape));
|
||||
const boardSizeRecord = boardSizes.reduce<BoardSizeRecord>((acc, size) => {
|
||||
const allInclude = shapes.every((shape) => Boolean(shape[size]));
|
||||
acc[size] = allInclude ? true : null;
|
||||
return acc;
|
||||
}, {} as BoardSizeRecord);
|
||||
return [name, boardSizeRecord];
|
||||
})
|
||||
.filter((selection): selection is [string, BoardSizeRecord] => Boolean(selection));
|
||||
};
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import type { ChangeEvent } from "react";
|
||||
import { Anchor, Card, Checkbox, Group, Stack, Text } from "@mantine/core";
|
||||
|
||||
import { objectEntries, objectKeys } from "@homarr/common";
|
||||
import { boardSizes } from "@homarr/old-schema";
|
||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||
|
||||
type BoardSize = (typeof boardSizes)[number];
|
||||
|
||||
export type BoardSizeRecord = Record<BoardSize, boolean | null>;
|
||||
export type BoardSelectionMap = Map<string, BoardSizeRecord>;
|
||||
export type BoardSelectionMap = Map<string, boolean>;
|
||||
|
||||
interface BoardSelectionCardProps {
|
||||
selections: BoardSelectionMap;
|
||||
@@ -16,12 +11,9 @@ interface BoardSelectionCardProps {
|
||||
}
|
||||
|
||||
const allChecked = (map: BoardSelectionMap) => {
|
||||
return [...map.values()].every((selection) => groupChecked(selection));
|
||||
return [...map.values()].every((selection) => selection);
|
||||
};
|
||||
|
||||
const groupChecked = (selection: BoardSizeRecord) =>
|
||||
objectEntries(selection).every(([_, value]) => value === true || value === null);
|
||||
|
||||
export const BoardSelectionCard = ({ selections, updateSelections }: BoardSelectionCardProps) => {
|
||||
const tBoardSelection = useScopedI18n("init.step.import.boardSelection");
|
||||
const t = useI18n();
|
||||
@@ -29,50 +21,14 @@ export const BoardSelectionCard = ({ selections, updateSelections }: BoardSelect
|
||||
|
||||
const handleToggleAll = () => {
|
||||
updateSelections((selections) => {
|
||||
const updated = new Map(selections);
|
||||
|
||||
[...selections.entries()].forEach(([name, selection]) => {
|
||||
objectKeys(selection).forEach((size) => {
|
||||
if (selection[size] === null) return;
|
||||
selection[size] = !areAllChecked;
|
||||
});
|
||||
|
||||
updated.set(name, selection);
|
||||
});
|
||||
|
||||
return updated;
|
||||
return new Map([...selections.keys()].map((name) => [name, !areAllChecked] as const));
|
||||
});
|
||||
};
|
||||
|
||||
const registerToggleGroup = (name: string) => (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const registerToggle = (name: string) => (event: ChangeEvent<HTMLInputElement>) => {
|
||||
updateSelections((selections) => {
|
||||
const updated = new Map(selections);
|
||||
const selection = selections.get(name);
|
||||
|
||||
if (!selection) return updated;
|
||||
|
||||
objectKeys(selection).forEach((size) => {
|
||||
if (selection[size] === null) return;
|
||||
selection[size] = event.target.checked;
|
||||
});
|
||||
|
||||
updated.set(name, selection);
|
||||
|
||||
return updated;
|
||||
});
|
||||
};
|
||||
|
||||
const registerToggle = (name: string, size: BoardSize) => (event: ChangeEvent<HTMLInputElement>) => {
|
||||
updateSelections((selections) => {
|
||||
const updated = new Map(selections);
|
||||
const selection = selections.get(name);
|
||||
|
||||
if (!selection) return updated;
|
||||
|
||||
selection[size] = event.target.checked;
|
||||
|
||||
updated.set(name, selection);
|
||||
|
||||
updated.set(name, event.target.checked);
|
||||
return updated;
|
||||
});
|
||||
};
|
||||
@@ -100,53 +56,17 @@ export const BoardSelectionCard = ({ selections, updateSelections }: BoardSelect
|
||||
</Stack>
|
||||
|
||||
<Stack gap="sm">
|
||||
{[...selections.entries()].map(([name, selection]) => (
|
||||
{[...selections.entries()].map(([name, selected]) => (
|
||||
<Card key={name} withBorder>
|
||||
<Group justify="space-between" align="center" visibleFrom="md">
|
||||
<Checkbox
|
||||
checked={groupChecked(selection)}
|
||||
onChange={registerToggleGroup(name)}
|
||||
label={
|
||||
<Text fw={500} size="sm">
|
||||
{name}
|
||||
</Text>
|
||||
}
|
||||
/>
|
||||
<Group>
|
||||
{boardSizes.map((size) => (
|
||||
<Checkbox
|
||||
key={size}
|
||||
disabled={selection[size] === null}
|
||||
checked={selection[size] ?? undefined}
|
||||
onChange={registerToggle(name, size)}
|
||||
label={t(`board.action.oldImport.form.screenSize.option.${size}`)}
|
||||
/>
|
||||
))}
|
||||
</Group>
|
||||
</Group>
|
||||
<Stack hiddenFrom="md">
|
||||
<Checkbox
|
||||
checked={groupChecked(selection)}
|
||||
onChange={registerToggleGroup(name)}
|
||||
label={
|
||||
<Text fw={500} size="sm">
|
||||
{name}
|
||||
</Text>
|
||||
}
|
||||
/>
|
||||
<Stack gap="sm" ps="sm">
|
||||
{objectEntries(selection)
|
||||
.filter(([_, value]) => value !== null)
|
||||
.map(([size, value]) => (
|
||||
<Checkbox
|
||||
key={size}
|
||||
checked={value ?? undefined}
|
||||
onChange={registerToggle(name, size)}
|
||||
label={`screenSize.${size}`}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Checkbox
|
||||
checked={selected}
|
||||
onChange={registerToggle(name)}
|
||||
label={
|
||||
<Text fw={500} size="sm">
|
||||
{name}
|
||||
</Text>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
))}
|
||||
</Stack>
|
||||
|
||||
Reference in New Issue
Block a user