feat: add board (#15)
* wip: Add gridstack board * wip: Centralize board pages, Add board settings page * fix: remove cyclic dependency and rename widget-sort to kind * improve: Add header actions as parallel route * feat: add item select modal, add category edit modal, * feat: add edit item modal * feat: add remove item modal * wip: add category actions * feat: add saving of board, wip: add app widget * Merge branch 'main' into add-board * chore: update turbo dependencies * chore: update mantine dependencies * chore: fix typescript errors, lint and format * feat: add confirm modal to category removal, move items of removed category to above wrapper * feat: remove app widget to continue in another branch * feat: add loading spinner until board is initialized * fix: issue with cellheight of gridstack items * feat: add translations for board * fix: issue with translation for settings page * chore: address pull request feedback
This commit is contained in:
40
apps/nextjs/src/components/layout/logo/board-logo.tsx
Normal file
40
apps/nextjs/src/components/layout/logo/board-logo.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
"use client";
|
||||
|
||||
import { useRequiredBoard } from "~/app/[locale]/boards/_context";
|
||||
import { homarrLogoPath, homarrPageTitle } from "./homarr-logo";
|
||||
import type { LogoWithTitleProps } from "./logo";
|
||||
import { Logo, LogoWithTitle } from "./logo";
|
||||
|
||||
interface LogoProps {
|
||||
size: number;
|
||||
}
|
||||
|
||||
const useImageOptions = () => {
|
||||
const board = useRequiredBoard();
|
||||
return {
|
||||
src: board.logoImageUrl ?? homarrLogoPath,
|
||||
alt: "Board logo",
|
||||
shouldUseNextImage: false,
|
||||
};
|
||||
};
|
||||
|
||||
export const BoardLogo = ({ size }: LogoProps) => {
|
||||
const imageOptions = useImageOptions();
|
||||
return <Logo size={size} {...imageOptions} />;
|
||||
};
|
||||
|
||||
interface CommonLogoWithTitleProps {
|
||||
size: LogoWithTitleProps["size"];
|
||||
}
|
||||
|
||||
export const BoardLogoWithTitle = ({ size }: CommonLogoWithTitleProps) => {
|
||||
const board = useRequiredBoard();
|
||||
const imageOptions = useImageOptions();
|
||||
return (
|
||||
<LogoWithTitle
|
||||
size={size}
|
||||
title={board.pageTitle ?? homarrPageTitle}
|
||||
image={imageOptions}
|
||||
/>
|
||||
);
|
||||
};
|
||||
29
apps/nextjs/src/components/layout/logo/homarr-logo.tsx
Normal file
29
apps/nextjs/src/components/layout/logo/homarr-logo.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { LogoWithTitleProps } from "./logo";
|
||||
import { Logo, LogoWithTitle } from "./logo";
|
||||
|
||||
interface LogoProps {
|
||||
size: number;
|
||||
}
|
||||
|
||||
export const homarrLogoPath = "/logo/homarr.png";
|
||||
export const homarrPageTitle = "Homarr";
|
||||
|
||||
const imageOptions = {
|
||||
src: homarrLogoPath,
|
||||
alt: "Homarr logo",
|
||||
shouldUseNextImage: true,
|
||||
};
|
||||
|
||||
export const HomarrLogo = ({ size }: LogoProps) => (
|
||||
<Logo size={size} {...imageOptions} />
|
||||
);
|
||||
|
||||
interface CommonLogoWithTitleProps {
|
||||
size: LogoWithTitleProps["size"];
|
||||
}
|
||||
|
||||
export const HomarrLogoWithTitle = ({ size }: CommonLogoWithTitleProps) => {
|
||||
return (
|
||||
<LogoWithTitle size={size} title={homarrPageTitle} image={imageOptions} />
|
||||
);
|
||||
};
|
||||
48
apps/nextjs/src/components/layout/logo/logo.tsx
Normal file
48
apps/nextjs/src/components/layout/logo/logo.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import Image from "next/image";
|
||||
|
||||
import type { TitleOrder } from "@homarr/ui";
|
||||
import { Group, Title } from "@homarr/ui";
|
||||
|
||||
interface LogoProps {
|
||||
size: number;
|
||||
src: string;
|
||||
alt: string;
|
||||
shouldUseNextImage?: boolean;
|
||||
}
|
||||
|
||||
export const Logo = ({
|
||||
size = 60,
|
||||
shouldUseNextImage = false,
|
||||
src,
|
||||
alt,
|
||||
}: LogoProps) =>
|
||||
shouldUseNextImage ? (
|
||||
<Image src={src} alt={alt} width={size} height={size} />
|
||||
) : (
|
||||
// we only want to use next/image for logos that we are sure will be preloaded and are allowed
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img src={src} alt={alt} width={size} height={size} />
|
||||
);
|
||||
|
||||
const logoWithTitleSizes = {
|
||||
lg: { logoSize: 48, titleOrder: 1 },
|
||||
md: { logoSize: 32, titleOrder: 2 },
|
||||
sm: { logoSize: 24, titleOrder: 3 },
|
||||
} satisfies Record<string, { logoSize: number; titleOrder: TitleOrder }>;
|
||||
|
||||
export interface LogoWithTitleProps {
|
||||
size: keyof typeof logoWithTitleSizes;
|
||||
title: string;
|
||||
image: Omit<LogoProps, "size">;
|
||||
}
|
||||
|
||||
export const LogoWithTitle = ({ size, title, image }: LogoWithTitleProps) => {
|
||||
const { logoSize, titleOrder } = logoWithTitleSizes[size];
|
||||
|
||||
return (
|
||||
<Group gap="xs" wrap="nowrap">
|
||||
<Logo {...image} size={logoSize} />
|
||||
<Title order={titleOrder}>{title}</Title>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user