Add shared WebUI layout primitives

This commit is contained in:
2026-08-18 10:42:55 +02:00
parent dd7ad4d9c7
commit 6814a41ae4
22 changed files with 574 additions and 111 deletions
+109
View File
@@ -0,0 +1,109 @@
import type { FormHTMLAttributes, HTMLAttributes, ReactNode } from "react";
export type ContentGridColumns = 1 | 2 | 3 | 4;
export type ContentGridGap = "compact" | "small" | "default" | "loose";
export type ContentGridCollapseAt = "narrow" | "workspace" | "standard" | "wide" | "never";
export type ContentGridProps = HTMLAttributes<HTMLDivElement> & {
children: ReactNode;
columns?: ContentGridColumns;
gap?: ContentGridGap;
collapseAt?: ContentGridCollapseAt;
align?: "start" | "stretch";
};
export default function ContentGrid({
children,
columns = 2,
gap = "default",
collapseAt = "workspace",
align = "start",
className = "",
...props
}: ContentGridProps) {
return (
<div
{...props}
className={[
"content-grid-layout",
`content-grid-columns-${columns}`,
`content-grid-gap-${gap}`,
`content-grid-collapse-${collapseAt}`,
`content-grid-align-${align}`,
className
].filter(Boolean).join(" ")}
>
{children}
</div>
);
}
export type FormGridProps = Omit<ContentGridProps, "align">;
export function FormGrid({
children,
columns = 1,
gap = "default",
collapseAt = "standard",
className = "",
...props
}: FormGridProps) {
return (
<ContentGrid
{...props}
columns={columns}
gap={gap}
collapseAt={collapseAt}
align="start"
className={["form-grid-layout", className].filter(Boolean).join(" ")}
>
{children}
</ContentGrid>
);
}
export type FormLayoutProps = FormHTMLAttributes<HTMLFormElement> & {
children: ReactNode;
columns?: ContentGridColumns;
gap?: ContentGridGap;
collapseAt?: ContentGridCollapseAt;
};
export function FormLayout({
children,
columns = 1,
gap = "default",
collapseAt = "standard",
className = "",
...props
}: FormLayoutProps) {
return (
<form
{...props}
className={[
"content-grid-layout",
"form-grid-layout",
`content-grid-columns-${columns}`,
`content-grid-gap-${gap}`,
`content-grid-collapse-${collapseAt}`,
"content-grid-align-start",
className
].filter(Boolean).join(" ")}
>
{children}
</form>
);
}
export type GridItemProps = HTMLAttributes<HTMLDivElement> & {
children: ReactNode;
span?: "one" | "two" | "full";
};
export function GridItem({ children, span = "one", className = "", ...props }: GridItemProps) {
return (
<div {...props} className={["content-grid-item", `content-grid-item-${span}`, className].filter(Boolean).join(" ")}>
{children}
</div>
);
}