feat: improve mobile board experience

This commit is contained in:
ajnart committed 2026-08-13 19:55:07 +02:00
1 parent 7d5ec15017
commit 2e3dca3e87
28 files changed
+979 -258

No files matched your search

@@ -38,6 +38,7 @@ export const createBoardContentPage = <TParams extends Record<string, unknown>>(
headerActions: <BoardContentHeaderActions demoReadOnly={env.DEMO_MODE && env.DEMO_READ_ONLY} />,
getInitialBoardAsync: getInitialBoard,
withTour: true,
mobileProfileInActions: true,
}),
// eslint-disable-next-line no-restricted-syntax
page: async ({ params }: { params: Promise<TParams> }) => {
@@ -0,0 +1,57 @@
"use client";
import { Group, Menu, ScrollArea, Text, UnstyledButton } from "@mantine/core";
import { IconCheck, IconChevronDown, IconLayoutBoard } from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
import { useRequiredBoard } from "@homarr/boards/context";
import { useI18n } from "@homarr/translation/client";
import { Link } from "@homarr/ui";
import { BoardLogo } from "~/components/layout/logo/board-logo";
export const BoardSwitcherLogo = () => {
const board = useRequiredBoard();
const { data: boards = [] } = clientApi.board.getAllBoards.useQuery();
const t = useI18n();
return (
<Menu position="bottom-start" width={280}>
<Menu.Target>
<UnstyledButton
aria-label={`${t("board.mobile.currentBoard")}: ${board.name}`}
maw="min(42vw, 20rem)"
mih={44}
px={4}
>
<Group gap="xs" wrap="nowrap">
<BoardLogo size={28} />
<Text fw={600} truncate>
{board.name}
</Text>
<IconChevronDown size={16} aria-hidden />
</Group>
</UnstyledButton>
</Menu.Target>
<Menu.Dropdown>
<Menu.Label>{t("board.mobile.currentBoard")}</Menu.Label>
<ScrollArea.Autosize mah={320}>
{boards.map((availableBoard) => {
const isCurrent = availableBoard.id === board.id;
return (
<Menu.Item
key={availableBoard.id}
component={Link}
href={`/boards/${availableBoard.name}`}
leftSection={isCurrent ? <IconCheck size={20} /> : <IconLayoutBoard size={20} />}
aria-current={isCurrent ? "page" : undefined}
>
{availableBoard.name}
</Menu.Item>
);
})}
</ScrollArea.Autosize>
</Menu.Dropdown>
</Menu>
);
};
@@ -3,14 +3,16 @@
import { IconLayoutBoard } from "@tabler/icons-react";
import { useRequiredBoard } from "@homarr/boards/context";
import { useI18n } from "@homarr/translation/client";
import { HeaderButton } from "~/components/layout/header/button";
export const BoardOtherHeaderActions = () => {
const board = useRequiredBoard();
const t = useI18n();
return (
<HeaderButton href={`/boards/${board.name}`}>
<HeaderButton href={`/boards/${board.name}`} aria-label={t("common.userAvatar.menu.homeBoard")}>
<IconLayoutBoard stroke={1.5} />
</HeaderButton>
);
@@ -0,0 +1,10 @@
import type { PropsWithChildren } from "react";
import type { Viewport } from "next";
export const viewport: Viewport = {
viewportFit: "cover",
};
export default function BoardsLayout({ children }: PropsWithChildren) {
return children;
}