@zvk/composite 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # @zvk/composite Changelog
2
+
3
+ ## [0.1.1](https://github.com/brandon-schabel/zvk/compare/composite-v0.1.0...composite-v0.1.1) (2026-06-16)
4
+
5
+
6
+ ### Features
7
+
8
+ * add composite component package ([dc0920f](https://github.com/brandon-schabel/zvk/commit/dc0920fe77dd2bd63015a40b3d7675fe8f1d0067))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * harden popup positioning ([efb8c8a](https://github.com/brandon-schabel/zvk/commit/efb8c8a98c2b759f4e84a60f5ae292b1e8900907))
14
+
15
+ ## 0.1.0
16
+
17
+ - Initial package scaffold for composite workspace components.
package/LICENSE.md ADDED
@@ -0,0 +1,3 @@
1
+ # License
2
+
3
+ See the repository license for terms.
package/README.md ADDED
@@ -0,0 +1,12 @@
1
+ # @zvk/composite
2
+
3
+ Drop-in composite React components built from public `@zvk/ui` primitives.
4
+
5
+ Import `@zvk/ui/styles.css` once in the app root, then import `@zvk/composite/styles.css` after it:
6
+
7
+ ```tsx
8
+ import "@zvk/ui/styles.css";
9
+ import "@zvk/composite/styles.css";
10
+ ```
11
+
12
+ Components are UI-only and accept typed props, slots, and callbacks. They do not own routing, persistence, data fetching, authentication, billing, or business rules.
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ export interface DataTablePageFrameProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
3
+ bulkActions?: React.ReactNode;
4
+ description?: React.ReactNode;
5
+ empty?: React.ReactNode;
6
+ filters?: React.ReactNode;
7
+ footer?: React.ReactNode;
8
+ loading?: boolean;
9
+ loadingLabel?: React.ReactNode;
10
+ pagination?: React.ReactNode;
11
+ ref?: React.Ref<HTMLDivElement>;
12
+ search?: React.ReactNode;
13
+ table?: React.ReactNode;
14
+ title: React.ReactNode;
15
+ }
16
+ export declare function DataTablePageFrame({ bulkActions, className, description, empty, filters, footer, loading, loadingLabel, pagination, ref, search, table, title, ...props }: DataTablePageFrameProps): React.JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Card } from "@zvk/ui/card";
4
+ import { Table } from "@zvk/ui/table";
5
+ import { cn } from "../utils/cn.js";
6
+ export function DataTablePageFrame({ bulkActions, className, description, empty, filters, footer, loading = false, loadingLabel = "Loading data", pagination, ref, search, table, title, ...props }) {
7
+ const hasToolbar = Boolean(search || filters || bulkActions);
8
+ const hasFooter = Boolean(pagination || footer);
9
+ return (_jsxs("section", { ...props, ref: ref, className: cn("zvk-composite-data-table-page-frame", className), children: [_jsx("div", { className: "zvk-composite-data-table-page-frame__header", children: _jsxs("div", { className: "zvk-composite-data-table-page-frame__heading", children: [_jsx("h2", { className: "zvk-composite-data-table-page-frame__title", children: title }), description ? _jsx("p", { className: "zvk-composite-data-table-page-frame__description", children: description }) : null] }) }), _jsxs(Card, { className: "zvk-composite-data-table-page-frame__card", padding: "none", variant: "outline", children: [hasToolbar ? (_jsxs("div", { className: "zvk-composite-data-table-page-frame__toolbar", children: [search ? _jsx("div", { className: "zvk-composite-data-table-page-frame__search", children: search }) : null, filters ? _jsx("div", { className: "zvk-composite-data-table-page-frame__filters", children: filters }) : null, bulkActions ? (_jsx("div", { className: "zvk-composite-data-table-page-frame__bulk-actions", children: bulkActions })) : null] })) : null, _jsx(Card.Content, { className: "zvk-composite-data-table-page-frame__content", children: loading ? (_jsx("div", { className: "zvk-composite-data-table-page-frame__state", role: "status", children: loadingLabel })) : table ? (_jsx(Table.Container, { className: "zvk-composite-data-table-page-frame__table", children: table })) : empty ? (_jsx("div", { className: "zvk-composite-data-table-page-frame__state", children: empty })) : null }), hasFooter ? (_jsxs(Card.Footer, { className: "zvk-composite-data-table-page-frame__footer", children: [footer ? _jsx("div", { className: "zvk-composite-data-table-page-frame__footer-content", children: footer }) : null, pagination ? (_jsx("div", { className: "zvk-composite-data-table-page-frame__pagination", children: pagination })) : null] })) : null] })] }));
10
+ }
@@ -0,0 +1,17 @@
1
+ import * as React from "react";
2
+ export type ConfirmActionDialogTone = "destructive" | "warning" | "primary";
3
+ export interface ConfirmActionDialogProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
4
+ cancelLabel?: React.ReactNode;
5
+ confirmLabel: React.ReactNode;
6
+ defaultOpen?: boolean;
7
+ description: React.ReactNode;
8
+ disabled?: boolean;
9
+ onConfirm: () => void;
10
+ onOpenChange?: (open: boolean) => void;
11
+ open?: boolean;
12
+ ref?: React.Ref<HTMLDivElement>;
13
+ title: React.ReactNode;
14
+ tone?: ConfirmActionDialogTone;
15
+ trigger: React.ReactNode;
16
+ }
17
+ export declare function ConfirmActionDialog({ cancelLabel, className, confirmLabel, defaultOpen, description, disabled, onConfirm, onOpenChange, open, ref, title, tone, trigger, ...props }: ConfirmActionDialogProps): React.JSX.Element;
@@ -0,0 +1,21 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { AlertDialog } from "@zvk/ui/alert-dialog";
5
+ import { Button } from "@zvk/ui/button";
6
+ import { cn } from "../utils/cn.js";
7
+ function getConfirmVariant(tone) {
8
+ if (tone === "destructive") {
9
+ return "destructive";
10
+ }
11
+ return "primary";
12
+ }
13
+ export function ConfirmActionDialog({ cancelLabel = "Cancel", className, confirmLabel, defaultOpen, description, disabled = false, onConfirm, onOpenChange, open, ref, title, tone = "destructive", trigger, ...props }) {
14
+ const rootProps = {
15
+ ...(defaultOpen !== undefined ? { defaultOpen } : {}),
16
+ ...(onOpenChange ? { onOpenChange } : {}),
17
+ ...(open !== undefined ? { open } : {})
18
+ };
19
+ const triggerContent = React.isValidElement(trigger) ? (trigger) : (_jsx(Button, { className: "zvk-composite-confirm-action-dialog__trigger", disabled: disabled, type: "button", variant: tone === "destructive" ? "destructive" : "primary", children: trigger }));
20
+ return (_jsxs(AlertDialog, { ...props, ...rootProps, ...(ref !== undefined ? { ref } : {}), className: cn("zvk-composite-confirm-action-dialog", className), "data-tone": tone, children: [_jsx(AlertDialog.Trigger, { asChild: true, disabled: disabled, children: triggerContent }), _jsxs(AlertDialog.Content, { className: "zvk-composite-confirm-action-dialog__content", children: [_jsx(AlertDialog.Title, { className: "zvk-composite-confirm-action-dialog__title", children: title }), _jsx(AlertDialog.Description, { className: "zvk-composite-confirm-action-dialog__description", children: description }), _jsxs(AlertDialog.Footer, { className: "zvk-composite-confirm-action-dialog__footer", children: [_jsx(AlertDialog.Cancel, { asChild: true, children: _jsx(Button, { type: "button", variant: "secondary", children: cancelLabel }) }), _jsx(AlertDialog.Action, { asChild: true, disabled: disabled, onClick: onConfirm, children: _jsx(Button, { className: "zvk-composite-confirm-action-dialog__confirm", disabled: disabled, type: "button", variant: getConfirmVariant(tone), children: confirmLabel }) })] })] })] }));
21
+ }
@@ -0,0 +1,11 @@
1
+ import * as React from "react";
2
+ export interface FormSurfaceProps extends Omit<React.FormHTMLAttributes<HTMLFormElement>, "children" | "title"> {
3
+ actions?: React.ReactNode;
4
+ alert?: React.ReactNode;
5
+ children?: React.ReactNode;
6
+ description?: React.ReactNode;
7
+ footer?: React.ReactNode;
8
+ ref?: React.Ref<HTMLFormElement>;
9
+ title: React.ReactNode;
10
+ }
11
+ export declare function FormSurface({ actions, alert, children, className, description, footer, ref, title, ...props }: FormSurfaceProps): React.JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Card } from "@zvk/ui/card";
4
+ import { cn } from "../utils/cn.js";
5
+ export function FormSurface({ actions, alert, children, className, description, footer, ref, title, ...props }) {
6
+ return (_jsx("form", { ...props, ref: ref, className: cn("zvk-composite-form-surface", className), children: _jsxs(Card, { className: "zvk-composite-form-surface__card", padding: "none", variant: "outline", children: [_jsxs(Card.Header, { className: "zvk-composite-form-surface__header", children: [_jsxs("div", { className: "zvk-composite-form-surface__heading", children: [_jsx(Card.Title, { className: "zvk-composite-form-surface__title", children: title }), description ? (_jsx(Card.Description, { className: "zvk-composite-form-surface__description", children: description })) : null] }), actions ? _jsx(Card.Action, { className: "zvk-composite-form-surface__actions", children: actions }) : null] }), alert ? _jsx("div", { className: "zvk-composite-form-surface__alert", children: alert }) : null, _jsx(Card.Content, { className: "zvk-composite-form-surface__content", children: children }), footer ? _jsx(Card.Footer, { className: "zvk-composite-form-surface__footer", children: footer }) : null] }) }));
7
+ }
@@ -0,0 +1,24 @@
1
+ export { PageScaffold } from "./layout/page-scaffold.js";
2
+ export type { PageScaffoldMaxWidth, PageScaffoldProps } from "./layout/page-scaffold.js";
3
+ export { AppWorkspaceShell } from "./navigation/app-workspace-shell.js";
4
+ export type { AppWorkspaceShellProps } from "./navigation/app-workspace-shell.js";
5
+ export { BreadcrumbPageHeader, WorkspaceHeader } from "./navigation/workspace-header.js";
6
+ export type { BreadcrumbPageHeaderProps, BreadcrumbPageHeaderItem, WorkspaceHeaderAlign, WorkspaceHeaderHeadingLevel, WorkspaceHeaderProps } from "./navigation/workspace-header.js";
7
+ export { SectionedWorkspaceShell } from "./navigation/sectioned-workspace-shell.js";
8
+ export type { SectionedWorkspaceItem, SectionedWorkspaceSection, SectionedWorkspaceShellProps } from "./navigation/sectioned-workspace-shell.js";
9
+ export { StateSurface } from "./state/state-surface.js";
10
+ export type { StateSurfaceAlign, StateSurfaceProps, StateSurfaceSize, StateSurfaceState } from "./state/state-surface.js";
11
+ export { EntityCard } from "./lists/entity-card.js";
12
+ export type { EntityCardProps } from "./lists/entity-card.js";
13
+ export { EntityListSection } from "./lists/entity-list-section.js";
14
+ export type { EntityListSectionProps } from "./lists/entity-list-section.js";
15
+ export { SummaryMetricGrid } from "./lists/summary-metric-grid.js";
16
+ export type { SummaryMetric, SummaryMetricGridActionPosition, SummaryMetricGridColumns, SummaryMetricGridProps } from "./lists/summary-metric-grid.js";
17
+ export { SettingsHubList } from "./lists/settings-hub-list.js";
18
+ export type { SettingsHubItem, SettingsHubListProps } from "./lists/settings-hub-list.js";
19
+ export { ConfirmActionDialog } from "./forms/confirm-action-dialog.js";
20
+ export type { ConfirmActionDialogProps, ConfirmActionDialogTone } from "./forms/confirm-action-dialog.js";
21
+ export { FormSurface } from "./forms/form-surface.js";
22
+ export type { FormSurfaceProps } from "./forms/form-surface.js";
23
+ export { DataTablePageFrame } from "./data/data-table-page-frame.js";
24
+ export type { DataTablePageFrameProps } from "./data/data-table-page-frame.js";
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ export { PageScaffold } from "./layout/page-scaffold.js";
2
+ export { AppWorkspaceShell } from "./navigation/app-workspace-shell.js";
3
+ export { BreadcrumbPageHeader, WorkspaceHeader } from "./navigation/workspace-header.js";
4
+ export { SectionedWorkspaceShell } from "./navigation/sectioned-workspace-shell.js";
5
+ export { StateSurface } from "./state/state-surface.js";
6
+ export { EntityCard } from "./lists/entity-card.js";
7
+ export { EntityListSection } from "./lists/entity-list-section.js";
8
+ export { SummaryMetricGrid } from "./lists/summary-metric-grid.js";
9
+ export { SettingsHubList } from "./lists/settings-hub-list.js";
10
+ export { ConfirmActionDialog } from "./forms/confirm-action-dialog.js";
11
+ export { FormSurface } from "./forms/form-surface.js";
12
+ export { DataTablePageFrame } from "./data/data-table-page-frame.js";
@@ -0,0 +1,20 @@
1
+ import * as React from "react";
2
+ import type { WorkspaceHeaderAlign, WorkspaceHeaderHeadingLevel } from "../navigation/workspace-header.js";
3
+ export type PageScaffoldMaxWidth = "sm" | "md" | "lg" | "xl" | "full";
4
+ export interface PageScaffoldProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
5
+ actions?: React.ReactNode | undefined;
6
+ aside?: React.ReactNode | undefined;
7
+ children?: React.ReactNode | undefined;
8
+ description?: React.ReactNode | undefined;
9
+ eyebrow?: React.ReactNode | undefined;
10
+ footer?: React.ReactNode | undefined;
11
+ headerAlign?: WorkspaceHeaderAlign | undefined;
12
+ headingLevel?: WorkspaceHeaderHeadingLevel | undefined;
13
+ maxWidth?: PageScaffoldMaxWidth | undefined;
14
+ ref?: React.Ref<HTMLDivElement> | undefined;
15
+ state?: React.ReactNode | undefined;
16
+ status?: React.ReactNode | undefined;
17
+ title?: React.ReactNode | undefined;
18
+ toolbar?: React.ReactNode | undefined;
19
+ }
20
+ export declare function PageScaffold({ actions, aside, children, className, description, eyebrow, footer, headerAlign, headingLevel, maxWidth, ref, state, status, title, toolbar, ...props }: PageScaffoldProps): React.JSX.Element;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "../utils/cn.js";
4
+ import { WorkspaceHeader } from "../navigation/workspace-header.js";
5
+ export function PageScaffold({ actions, aside, children, className, description, eyebrow, footer, headerAlign, headingLevel, maxWidth = "xl", ref, state, status, title, toolbar, ...props }) {
6
+ const hasTitle = title !== undefined && title !== null && title !== false;
7
+ const hasHeader = Boolean(hasTitle || actions || toolbar);
8
+ const hasAside = Boolean(aside);
9
+ const headerProps = {
10
+ ...(actions === undefined ? {} : { actions }),
11
+ ...(description === undefined ? {} : { description }),
12
+ ...(eyebrow === undefined ? {} : { eyebrow }),
13
+ ...(headerAlign === undefined ? {} : { align: headerAlign }),
14
+ ...(headingLevel === undefined ? {} : { headingLevel }),
15
+ ...(status === undefined ? {} : { status }),
16
+ ...(toolbar === undefined ? {} : { toolbar })
17
+ };
18
+ return (_jsxs("div", { ...props, ref: ref, className: cn("zvk-composite-page-scaffold", className), "data-max-width": maxWidth, "data-with-aside": hasAside ? "true" : "false", children: [hasTitle ? (_jsx(WorkspaceHeader, { ...headerProps, className: "zvk-composite-page-scaffold__header", title: title })) : hasHeader ? (_jsxs("div", { className: "zvk-composite-page-scaffold__header zvk-composite-page-scaffold__header--controls-only", children: [toolbar ? _jsx("div", { className: "zvk-composite-page-scaffold__toolbar", children: toolbar }) : null, actions ? _jsx("div", { className: "zvk-composite-page-scaffold__actions", children: actions }) : null] })) : null, state ? _jsx("div", { className: "zvk-composite-page-scaffold__state", children: state }) : null, _jsxs("div", { className: "zvk-composite-page-scaffold__grid", children: [_jsx("main", { className: "zvk-composite-page-scaffold__main", children: children }), hasAside ? _jsx("aside", { className: "zvk-composite-page-scaffold__aside", children: aside }) : null] }), footer ? _jsx("footer", { className: "zvk-composite-page-scaffold__footer", children: footer }) : null] }));
19
+ }
@@ -0,0 +1,17 @@
1
+ import * as React from "react";
2
+ export interface EntityCardProps extends Omit<React.HTMLAttributes<HTMLElement>, "title" | "onSelect"> {
3
+ title: React.ReactNode;
4
+ description?: React.ReactNode;
5
+ icon?: React.ReactNode;
6
+ badges?: React.ReactNode;
7
+ meta?: React.ReactNode;
8
+ actions?: React.ReactNode;
9
+ href?: string;
10
+ onSelect?: () => void;
11
+ selected?: boolean;
12
+ disabled?: boolean;
13
+ loading?: boolean;
14
+ error?: React.ReactNode;
15
+ ref?: React.Ref<HTMLElement>;
16
+ }
17
+ export declare function EntityCard({ actions, badges, className, description, disabled, error, href, icon, loading, meta, onSelect, ref, selected, title, ...props }: EntityCardProps): React.JSX.Element;
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Spinner } from "@zvk/ui/spinner";
4
+ import { cn } from "../utils/cn.js";
5
+ function hasValue(value) {
6
+ return value !== null && value !== undefined && value !== false;
7
+ }
8
+ export function EntityCard({ actions, badges, className, description, disabled = false, error, href, icon, loading = false, meta, onSelect, ref, selected = false, title, ...props }) {
9
+ const interactive = Boolean(href || onSelect);
10
+ const classNames = cn("zvk-composite-entity-card", className);
11
+ const { onClick, ...restProps } = props;
12
+ const content = (_jsxs(_Fragment, { children: [hasValue(icon) ? _jsx("div", { className: "zvk-composite-entity-card__icon", children: icon }) : null, _jsxs("div", { className: "zvk-composite-entity-card__body", children: [_jsxs("div", { className: "zvk-composite-entity-card__heading", children: [_jsx("h3", { className: "zvk-composite-entity-card__title", children: title }), hasValue(badges) ? _jsx("div", { className: "zvk-composite-entity-card__badges", children: badges }) : null] }), hasValue(description) ? _jsx("p", { className: "zvk-composite-entity-card__description", children: description }) : null, hasValue(meta) ? _jsx("div", { className: "zvk-composite-entity-card__meta", children: meta }) : null, hasValue(error) ? _jsx("div", { className: "zvk-composite-entity-card__error", children: error }) : null] }), loading ? (_jsx("div", { className: "zvk-composite-entity-card__status", children: _jsx(Spinner, { label: "Loading", size: "sm", tone: "muted" }) })) : null, hasValue(actions) ? _jsx("div", { className: "zvk-composite-entity-card__actions", children: actions }) : null] }));
13
+ const sharedProps = {
14
+ ...restProps,
15
+ "aria-busy": loading ? true : props["aria-busy"],
16
+ "aria-current": selected ? "true" : props["aria-current"],
17
+ "aria-disabled": disabled ? true : props["aria-disabled"],
18
+ className: classNames
19
+ };
20
+ if (href) {
21
+ const handleAnchorClick = (event) => {
22
+ onClick?.(event);
23
+ if (disabled) {
24
+ event.preventDefault();
25
+ }
26
+ };
27
+ return (_jsx("a", { ...sharedProps, "data-disabled": disabled ? "" : undefined, "data-interactive": interactive ? "" : undefined, "data-loading": loading ? "" : undefined, "data-selected": selected ? "" : undefined, href: href, onClick: handleAnchorClick, ref: ref, children: content }));
28
+ }
29
+ if (onSelect) {
30
+ const handleButtonClick = (event) => {
31
+ onClick?.(event);
32
+ if (!disabled) {
33
+ onSelect();
34
+ }
35
+ };
36
+ return (_jsx("button", { ...sharedProps, "data-disabled": disabled ? "" : undefined, "data-interactive": interactive ? "" : undefined, "data-loading": loading ? "" : undefined, "data-selected": selected ? "" : undefined, disabled: disabled, onClick: handleButtonClick, ref: ref, type: "button", children: content }));
37
+ }
38
+ return (_jsx("article", { ...sharedProps, "data-disabled": disabled ? "" : undefined, "data-interactive": interactive ? "" : undefined, "data-loading": loading ? "" : undefined, "data-selected": selected ? "" : undefined, ref: ref, children: content }));
39
+ }
@@ -0,0 +1,28 @@
1
+ import * as React from "react";
2
+ export interface EntityListSectionProps<T> extends Omit<React.ComponentPropsWithoutRef<"section">, "title" | "children"> {
3
+ title: React.ReactNode;
4
+ description?: React.ReactNode;
5
+ toolbar?: React.ReactNode;
6
+ actions?: React.ReactNode;
7
+ empty?: React.ReactNode;
8
+ loading?: boolean;
9
+ loadingLabel?: React.ReactNode;
10
+ footer?: React.ReactNode;
11
+ classNames?: {
12
+ header?: string;
13
+ copy?: string;
14
+ title?: string;
15
+ description?: string;
16
+ actions?: string;
17
+ toolbar?: string;
18
+ list?: string;
19
+ item?: string;
20
+ state?: string;
21
+ footer?: string;
22
+ };
23
+ items: readonly T[];
24
+ getItemKey: (item: T, index: number) => React.Key;
25
+ renderItem: (item: T, index: number) => React.ReactNode;
26
+ ref?: React.Ref<HTMLElement>;
27
+ }
28
+ export declare function EntityListSection<T>({ actions, className, classNames, description, empty, footer, getItemKey, items, loading, loadingLabel, ref, renderItem, title, toolbar, ...props }: EntityListSectionProps<T>): React.JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { StateSurface } from "../state/state-surface.js";
4
+ import { cn } from "../utils/cn.js";
5
+ export function EntityListSection({ actions, className, classNames, description, empty, footer, getItemKey, items, loading = false, loadingLabel = "Loading", ref, renderItem, title, toolbar, ...props }) {
6
+ const hasItems = items.length > 0;
7
+ return (_jsxs("section", { ...props, ref: ref, className: cn("zvk-composite-entity-list-section", className), children: [_jsxs("div", { className: cn("zvk-composite-entity-list-section__header", classNames?.header), children: [_jsxs("div", { className: cn("zvk-composite-entity-list-section__copy", classNames?.copy), children: [_jsx("h2", { className: cn("zvk-composite-entity-list-section__title", classNames?.title), children: title }), description ? _jsx("p", { className: cn("zvk-composite-entity-list-section__description", classNames?.description), children: description }) : null] }), actions ? _jsx("div", { className: cn("zvk-composite-entity-list-section__actions", classNames?.actions), children: actions }) : null] }), toolbar ? _jsx("div", { className: cn("zvk-composite-entity-list-section__toolbar", classNames?.toolbar), children: toolbar }) : null, loading ? (_jsx(StateSurface, { align: "center", className: cn("zvk-composite-entity-list-section__state", classNames?.state), size: "sm", state: "loading", title: loadingLabel })) : hasItems ? (_jsx("ul", { className: cn("zvk-composite-entity-list-section__list", classNames?.list), children: items.map((item, index) => (_jsx("li", { className: cn("zvk-composite-entity-list-section__item", classNames?.item), children: renderItem(item, index) }, getItemKey(item, index)))) })) : (empty ?? (_jsx(StateSurface, { align: "center", className: cn("zvk-composite-entity-list-section__state", classNames?.state), size: "sm", state: "empty", title: "No items" }))), footer ? _jsx("div", { className: cn("zvk-composite-entity-list-section__footer", classNames?.footer), children: footer }) : null] }));
8
+ }
@@ -0,0 +1,22 @@
1
+ import * as React from "react";
2
+ export interface SettingsHubListItem {
3
+ id: React.Key;
4
+ title: React.ReactNode;
5
+ description?: React.ReactNode;
6
+ icon?: React.ReactNode;
7
+ badge?: React.ReactNode;
8
+ meta?: React.ReactNode;
9
+ href?: string;
10
+ onSelect?: () => void;
11
+ disabled?: boolean;
12
+ }
13
+ export type SettingsHubItem = SettingsHubListItem;
14
+ export interface SettingsHubListProps extends Omit<React.ComponentPropsWithoutRef<"section">, "title" | "children"> {
15
+ items: readonly SettingsHubListItem[];
16
+ title?: React.ReactNode;
17
+ description?: React.ReactNode;
18
+ actions?: React.ReactNode;
19
+ empty?: React.ReactNode;
20
+ ref?: React.Ref<HTMLElement>;
21
+ }
22
+ export declare function SettingsHubList({ actions, className, description, empty, items, ref, title, ...props }: SettingsHubListProps): React.JSX.Element;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Badge } from "@zvk/ui/badge";
4
+ import { EntityCard } from "./entity-card.js";
5
+ import { StateSurface } from "../state/state-surface.js";
6
+ import { cn } from "../utils/cn.js";
7
+ function renderBadge(badge) {
8
+ if (badge === null || badge === undefined || badge === false) {
9
+ return null;
10
+ }
11
+ if (React.isValidElement(badge)) {
12
+ return badge;
13
+ }
14
+ return _jsx(Badge, { children: badge });
15
+ }
16
+ export function SettingsHubList({ actions, className, description, empty, items, ref, title, ...props }) {
17
+ const showHeader = title !== undefined || description !== undefined || actions !== undefined;
18
+ return (_jsxs("section", { ...props, ref: ref, className: cn("zvk-composite-settings-hub-list", className), children: [showHeader ? (_jsxs("div", { className: "zvk-composite-settings-hub-list__header", children: [_jsxs("div", { className: "zvk-composite-settings-hub-list__copy", children: [title ? _jsx("h2", { className: "zvk-composite-settings-hub-list__title", children: title }) : null, description ? _jsx("p", { className: "zvk-composite-settings-hub-list__description", children: description }) : null] }), actions ? _jsx("div", { className: "zvk-composite-settings-hub-list__actions", children: actions }) : null] })) : null, items.length > 0 ? (_jsx("ul", { className: "zvk-composite-settings-hub-list__list", children: items.map((item) => (_jsx("li", { className: "zvk-composite-settings-hub-list__item", children: _jsx(EntityCard, { badges: renderBadge(item.badge), description: item.description, icon: item.icon, meta: item.meta, title: item.title, ...(item.disabled === undefined ? {} : { disabled: item.disabled }), ...(item.href === undefined ? {} : { href: item.href }), ...(item.onSelect === undefined ? {} : { onSelect: item.onSelect }) }) }, item.id))) })) : (empty ?? (_jsx(StateSurface, { align: "center", className: "zvk-composite-settings-hub-list__empty", size: "sm", state: "empty", title: "No settings" })))] }));
19
+ }
@@ -0,0 +1,34 @@
1
+ import * as React from "react";
2
+ import { type StatTone } from "@zvk/ui/stat";
3
+ export type SummaryMetricGridColumns = 1 | 2 | 3 | 4;
4
+ export type SummaryMetricGridActionPosition = "footer" | "top";
5
+ export interface SummaryMetric {
6
+ id: React.Key;
7
+ label: React.ReactNode;
8
+ value: React.ReactNode;
9
+ description?: React.ReactNode;
10
+ trend?: React.ReactNode;
11
+ tone?: StatTone;
12
+ action?: React.ReactNode;
13
+ }
14
+ export interface SummaryMetricGridProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
15
+ metrics: readonly SummaryMetric[];
16
+ title?: React.ReactNode;
17
+ description?: React.ReactNode;
18
+ actions?: React.ReactNode;
19
+ columns?: SummaryMetricGridColumns;
20
+ actionPosition?: SummaryMetricGridActionPosition;
21
+ classNames?: {
22
+ header?: string;
23
+ copy?: string;
24
+ title?: string;
25
+ description?: string;
26
+ actions?: string;
27
+ metrics?: string;
28
+ card?: string;
29
+ stat?: string;
30
+ action?: string;
31
+ };
32
+ ref?: React.Ref<HTMLDivElement>;
33
+ }
34
+ export declare function SummaryMetricGrid({ actionPosition, actions, className, classNames, columns, description, metrics, ref, title, ...props }: SummaryMetricGridProps): React.JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Card } from "@zvk/ui/card";
4
+ import { Stat } from "@zvk/ui/stat";
5
+ import { cn } from "../utils/cn.js";
6
+ export function SummaryMetricGrid({ actionPosition = "footer", actions, className, classNames, columns = 4, description, metrics, ref, title, ...props }) {
7
+ const showHeader = title !== undefined || description !== undefined || actions !== undefined;
8
+ return (_jsxs("div", { ...props, ref: ref, className: cn("zvk-composite-summary-metric-grid", className), "data-columns": columns, children: [showHeader ? (_jsxs("div", { className: cn("zvk-composite-summary-metric-grid__header", classNames?.header), children: [_jsxs("div", { className: cn("zvk-composite-summary-metric-grid__copy", classNames?.copy), children: [title ? _jsx("h2", { className: cn("zvk-composite-summary-metric-grid__title", classNames?.title), children: title }) : null, description ? _jsx("p", { className: cn("zvk-composite-summary-metric-grid__description", classNames?.description), children: description }) : null] }), actions ? _jsx("div", { className: cn("zvk-composite-summary-metric-grid__actions", classNames?.actions), children: actions }) : null] })) : null, _jsx("div", { className: cn("zvk-composite-summary-metric-grid__metrics", classNames?.metrics), children: metrics.map((metric) => (_jsxs(Card, { className: cn("zvk-composite-summary-metric-grid__card", classNames?.card), "data-action-position": actionPosition, padding: "none", variant: "outline", children: [metric.action && actionPosition === "top" ? (_jsx("div", { className: cn("zvk-composite-summary-metric-grid__action", classNames?.action), "data-position": "top", children: metric.action })) : null, _jsx(Stat, { className: cn("zvk-composite-summary-metric-grid__stat", classNames?.stat), description: metric.description, label: metric.label, trend: metric.trend, value: metric.value, ...(metric.tone === undefined ? {} : { tone: metric.tone }) }), metric.action && actionPosition === "footer" ? (_jsx("div", { className: cn("zvk-composite-summary-metric-grid__action", classNames?.action), "data-position": "footer", children: metric.action })) : null] }, metric.id))) })] }));
9
+ }
@@ -0,0 +1,14 @@
1
+ import * as React from "react";
2
+ export interface AppWorkspaceShellProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ accountMenu?: React.ReactNode | undefined;
4
+ brand: React.ReactNode;
5
+ children?: React.ReactNode | undefined;
6
+ commandPalette?: React.ReactNode | undefined;
7
+ footer?: React.ReactNode | undefined;
8
+ header?: React.ReactNode | undefined;
9
+ mobileNavigationLabel?: string | undefined;
10
+ navigation: React.ReactNode;
11
+ ref?: React.Ref<HTMLDivElement> | undefined;
12
+ workspaceSwitcher?: React.ReactNode | undefined;
13
+ }
14
+ export declare function AppWorkspaceShell({ accountMenu, brand, children, className, commandPalette, footer, header, mobileNavigationLabel, navigation, ref, workspaceSwitcher, ...props }: AppWorkspaceShellProps): React.JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { SidebarShell } from "@zvk/ui/sidebar-shell";
4
+ import { cn } from "../utils/cn.js";
5
+ export function AppWorkspaceShell({ accountMenu, brand, children, className, commandPalette, footer, header, mobileNavigationLabel = "Navigation", navigation, ref, workspaceSwitcher, ...props }) {
6
+ const hasTopbar = Boolean(header || commandPalette || workspaceSwitcher || accountMenu);
7
+ const shellProps = {
8
+ ...props,
9
+ ...(ref === undefined ? {} : { ref })
10
+ };
11
+ return (_jsxs(SidebarShell, { ...shellProps, className: cn("zvk-composite-app-workspace-shell", className), width: "md", children: [_jsxs(SidebarShell.Sidebar, { className: "zvk-composite-app-workspace-shell__sidebar", "aria-label": "Workspace", children: [_jsx("div", { className: "zvk-composite-app-workspace-shell__brand", children: brand }), workspaceSwitcher ? (_jsx("div", { className: "zvk-composite-app-workspace-shell__workspace-switcher", children: workspaceSwitcher })) : null, _jsx("div", { className: "zvk-composite-app-workspace-shell__navigation", children: navigation })] }), _jsxs(SidebarShell.Main, { className: "zvk-composite-app-workspace-shell__main", children: [_jsxs("details", { className: "zvk-composite-app-workspace-shell__mobile-nav", children: [_jsx("summary", { className: "zvk-composite-app-workspace-shell__mobile-nav-summary", children: mobileNavigationLabel }), _jsxs("div", { className: "zvk-composite-app-workspace-shell__mobile-nav-content", children: [_jsxs("div", { className: "zvk-composite-app-workspace-shell__mobile-nav-context", children: [_jsx("div", { className: "zvk-composite-app-workspace-shell__brand", children: brand }), workspaceSwitcher ? (_jsx("div", { className: "zvk-composite-app-workspace-shell__workspace-switcher", children: workspaceSwitcher })) : null] }), navigation] })] }), hasTopbar ? (_jsxs(SidebarShell.Header, { className: "zvk-composite-app-workspace-shell__header", children: [header ? _jsx("div", { className: "zvk-composite-app-workspace-shell__header-content", children: header }) : null, commandPalette ? (_jsx("div", { className: "zvk-composite-app-workspace-shell__command-palette", children: commandPalette })) : null, accountMenu ? _jsx("div", { className: "zvk-composite-app-workspace-shell__account-menu", children: accountMenu }) : null] })) : null, _jsx(SidebarShell.Content, { className: "zvk-composite-app-workspace-shell__content", children: children }), footer ? (_jsx(SidebarShell.Footer, { className: "zvk-composite-app-workspace-shell__footer", children: footer })) : null] })] }));
12
+ }
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+ import type { WorkspaceHeaderHeadingLevel } from "./workspace-header.js";
3
+ export interface SectionedWorkspaceShellItem {
4
+ badge?: React.ReactNode | undefined;
5
+ description?: React.ReactNode | undefined;
6
+ disabled?: boolean | undefined;
7
+ href?: string | undefined;
8
+ icon?: React.ReactNode | undefined;
9
+ id: string;
10
+ label: React.ReactNode;
11
+ }
12
+ export interface SectionedWorkspaceShellSection {
13
+ id: string;
14
+ items: SectionedWorkspaceShellItem[];
15
+ label: React.ReactNode;
16
+ }
17
+ export interface SectionedWorkspaceShellProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
18
+ actions?: React.ReactNode | undefined;
19
+ activeItemId?: string | undefined;
20
+ children?: React.ReactNode | undefined;
21
+ description?: React.ReactNode | undefined;
22
+ footer?: React.ReactNode | undefined;
23
+ headingLevel?: WorkspaceHeaderHeadingLevel | undefined;
24
+ navLabel?: string | undefined;
25
+ onItemSelect?: ((itemId: string) => void) | undefined;
26
+ ref?: React.Ref<HTMLDivElement> | undefined;
27
+ sections: SectionedWorkspaceShellSection[];
28
+ title?: React.ReactNode | undefined;
29
+ toolbar?: React.ReactNode | undefined;
30
+ }
31
+ export type SectionedWorkspaceItem = SectionedWorkspaceShellItem;
32
+ export type SectionedWorkspaceSection = SectionedWorkspaceShellSection;
33
+ export declare function SectionedWorkspaceShell({ actions, activeItemId, children, className, description, footer, headingLevel, navLabel, onItemSelect, ref, sections, title, toolbar, ...props }: SectionedWorkspaceShellProps): React.JSX.Element;
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { SectionedSidebarNav } from "@zvk/ui/sectioned-sidebar-nav";
4
+ import { cn } from "../utils/cn.js";
5
+ import { WorkspaceHeader } from "./workspace-header.js";
6
+ export function SectionedWorkspaceShell({ actions, activeItemId, children, className, description, footer, headingLevel, navLabel = "Workspace navigation", onItemSelect, ref, sections, title, toolbar, ...props }) {
7
+ const hasHeader = Boolean(title || description || actions || toolbar);
8
+ const navigationProps = {
9
+ ...(activeItemId === undefined ? {} : { activeItemId }),
10
+ ...(onItemSelect === undefined ? {} : { onItemSelect })
11
+ };
12
+ const headerProps = {
13
+ ...(actions === undefined ? {} : { actions }),
14
+ ...(description === undefined ? {} : { description }),
15
+ ...(headingLevel === undefined ? {} : { headingLevel }),
16
+ ...(toolbar === undefined ? {} : { toolbar })
17
+ };
18
+ return (_jsxs("div", { ...props, ref: ref, className: cn("zvk-composite-sectioned-workspace-shell", className), children: [_jsx("aside", { className: "zvk-composite-sectioned-workspace-shell__sidebar", children: _jsx(SectionedWorkspaceNavigation, { ...navigationProps, navLabel: navLabel, sections: sections }) }), _jsxs("div", { className: "zvk-composite-sectioned-workspace-shell__body", children: [hasHeader ? (_jsx(WorkspaceHeader, { ...headerProps, className: "zvk-composite-sectioned-workspace-shell__header", title: title ?? "" })) : null, _jsx("main", { className: "zvk-composite-sectioned-workspace-shell__content", children: children }), footer ? _jsx("footer", { className: "zvk-composite-sectioned-workspace-shell__footer", children: footer }) : null] })] }));
19
+ }
20
+ function SectionedWorkspaceNavigation({ activeItemId, navLabel, onItemSelect, sections }) {
21
+ return (_jsx(SectionedSidebarNav, { className: "zvk-composite-sectioned-workspace-shell__nav", label: navLabel, children: sections.map((section) => (_jsxs(SectionedSidebarNav.Section, { children: [_jsx(SectionedSidebarNav.SectionTitle, { children: section.label }), _jsx(SectionedSidebarNav.List, { children: section.items.map((item) => (_jsx(SectionedWorkspaceNavigationItem, { active: item.id === activeItemId, item: item, onItemSelect: onItemSelect }, item.id))) })] }, section.id))) }));
22
+ }
23
+ function SectionedWorkspaceNavigationItem({ active, item, onItemSelect }) {
24
+ const content = (_jsxs("span", { className: "zvk-composite-sectioned-workspace-shell__nav-item-content", children: [_jsx("span", { className: "zvk-composite-sectioned-workspace-shell__nav-item-label", children: item.label }), item.description ? (_jsx("span", { className: "zvk-composite-sectioned-workspace-shell__nav-item-description", children: item.description })) : null] }));
25
+ const handleSelect = () => onItemSelect?.(item.id);
26
+ const itemProps = {
27
+ ...(item.badge === undefined ? {} : { badge: item.badge }),
28
+ ...(item.disabled === undefined ? {} : { disabled: item.disabled }),
29
+ ...(item.icon === undefined ? {} : { icon: item.icon }),
30
+ onClick: handleSelect
31
+ };
32
+ if (item.href) {
33
+ return (_jsx(SectionedSidebarNav.Item, { ...itemProps, active: active, href: item.href, children: content }));
34
+ }
35
+ return (_jsx(SectionedSidebarNav.Item, { ...itemProps, active: active, children: content }));
36
+ }
@@ -0,0 +1,25 @@
1
+ import * as React from "react";
2
+ export type WorkspaceHeaderAlign = "start" | "center";
3
+ export type WorkspaceHeaderHeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
4
+ export interface WorkspaceHeaderProps extends Omit<React.HTMLAttributes<HTMLElement>, "title"> {
5
+ actions?: React.ReactNode | undefined;
6
+ align?: WorkspaceHeaderAlign | undefined;
7
+ description?: React.ReactNode | undefined;
8
+ eyebrow?: React.ReactNode | undefined;
9
+ headingLevel?: WorkspaceHeaderHeadingLevel | undefined;
10
+ meta?: React.ReactNode | undefined;
11
+ ref?: React.Ref<HTMLElement> | undefined;
12
+ status?: React.ReactNode | undefined;
13
+ title: React.ReactNode;
14
+ toolbar?: React.ReactNode | undefined;
15
+ }
16
+ export interface BreadcrumbPageHeaderItem {
17
+ current?: boolean | undefined;
18
+ href?: string | undefined;
19
+ label: React.ReactNode;
20
+ }
21
+ export interface BreadcrumbPageHeaderProps extends Omit<WorkspaceHeaderProps, "eyebrow"> {
22
+ breadcrumbs: BreadcrumbPageHeaderItem[];
23
+ }
24
+ export declare function WorkspaceHeader({ actions, align, className, description, eyebrow, headingLevel, meta, ref, status, title, toolbar, ...props }: WorkspaceHeaderProps): React.JSX.Element;
25
+ export declare function BreadcrumbPageHeader({ breadcrumbs, className, ...headerProps }: BreadcrumbPageHeaderProps): React.JSX.Element;
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Breadcrumbs } from "@zvk/ui/breadcrumbs";
4
+ import { cn } from "../utils/cn.js";
5
+ function getHeadingTag(headingLevel) {
6
+ switch (headingLevel) {
7
+ case 1:
8
+ return "h1";
9
+ case 2:
10
+ return "h2";
11
+ case 3:
12
+ return "h3";
13
+ case 4:
14
+ return "h4";
15
+ case 5:
16
+ return "h5";
17
+ case 6:
18
+ return "h6";
19
+ }
20
+ }
21
+ export function WorkspaceHeader({ actions, align = "start", className, description, eyebrow, headingLevel = 1, meta, ref, status, title, toolbar, ...props }) {
22
+ const hasKicker = Boolean(eyebrow || status);
23
+ const hasBody = Boolean(description || meta);
24
+ const hasControls = Boolean(actions || toolbar);
25
+ const Heading = getHeadingTag(headingLevel);
26
+ return (_jsxs("header", { ...props, ref: ref, className: cn("zvk-composite-workspace-header", className), "data-align": align, children: [_jsxs("div", { className: "zvk-composite-workspace-header__content", children: [hasKicker ? (_jsxs("div", { className: "zvk-composite-workspace-header__kicker", children: [eyebrow ? _jsx("span", { className: "zvk-composite-workspace-header__eyebrow", children: eyebrow }) : null, status ? _jsx("span", { className: "zvk-composite-workspace-header__status", children: status }) : null] })) : null, _jsx(Heading, { className: "zvk-composite-workspace-header__title", children: title }), hasBody ? (_jsxs("div", { className: "zvk-composite-workspace-header__body", children: [description ? _jsx("p", { className: "zvk-composite-workspace-header__description", children: description }) : null, meta ? _jsx("div", { className: "zvk-composite-workspace-header__meta", children: meta }) : null] })) : null] }), hasControls ? (_jsxs("div", { className: "zvk-composite-workspace-header__controls", children: [toolbar ? _jsx("div", { className: "zvk-composite-workspace-header__toolbar", children: toolbar }) : null, actions ? _jsx("div", { className: "zvk-composite-workspace-header__actions", children: actions }) : null] })) : null] }));
27
+ }
28
+ export function BreadcrumbPageHeader({ breadcrumbs, className, ...headerProps }) {
29
+ return (_jsxs("div", { className: cn("zvk-composite-breadcrumb-page-header", className), children: [_jsx(Breadcrumbs, { children: breadcrumbs.map((breadcrumb, index) => {
30
+ const breadcrumbProps = {
31
+ ...(breadcrumb.current === undefined ? {} : { current: breadcrumb.current }),
32
+ ...(breadcrumb.href === undefined ? {} : { href: breadcrumb.href })
33
+ };
34
+ return (_jsx(Breadcrumbs.Item, { ...breadcrumbProps, children: breadcrumb.label }, `${String(breadcrumb.label)}-${index}`));
35
+ }) }), _jsx(WorkspaceHeader, { ...headerProps, className: "zvk-composite-breadcrumb-page-header__header" })] }));
36
+ }
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+ export type StateSurfaceState = "loading" | "empty" | "error" | "not-found" | "permission" | "search-empty" | "success";
3
+ export type StateSurfaceSize = "sm" | "md" | "lg";
4
+ export type StateSurfaceAlign = "start" | "center";
5
+ export interface StateSurfaceClassNames {
6
+ emptyState?: string;
7
+ footer?: string;
8
+ }
9
+ export interface StateSurfaceProps extends Omit<React.ComponentPropsWithoutRef<"section">, "title"> {
10
+ state: StateSurfaceState;
11
+ title: React.ReactNode;
12
+ description?: React.ReactNode;
13
+ icon?: React.ReactNode;
14
+ actions?: React.ReactNode;
15
+ footer?: React.ReactNode;
16
+ classNames?: StateSurfaceClassNames;
17
+ size?: StateSurfaceSize;
18
+ align?: StateSurfaceAlign;
19
+ ref?: React.Ref<HTMLElement>;
20
+ }
21
+ export declare function StateSurface({ actions, align, className, classNames, description, footer, icon, ref, size, state, title, ...props }: StateSurfaceProps): React.JSX.Element;