@godxjp/ui 18.6.0 → 18.8.0

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.
Files changed (53) hide show
  1. package/dist/components/data-display/card.d.ts +27 -3
  2. package/dist/components/data-display/card.js +4 -1
  3. package/dist/components/data-entry/input-otp.d.ts +1 -1
  4. package/dist/components/feedback/alert.d.ts +20 -2
  5. package/dist/components/feedback/alert.js +11 -1
  6. package/dist/components/feedback/index.d.ts +2 -2
  7. package/dist/components/feedback/index.js +3 -1
  8. package/dist/components/layout/auth-shell.d.ts +4 -2
  9. package/dist/components/layout/index.d.ts +2 -0
  10. package/dist/components/layout/index.js +2 -0
  11. package/dist/components/layout/page-container.d.ts +1 -1
  12. package/dist/components/layout/page-container.js +19 -41
  13. package/dist/components/layout/page-header.d.ts +22 -0
  14. package/dist/components/layout/page-header.js +89 -0
  15. package/dist/components/navigation/filter-bar.d.ts +2 -2
  16. package/dist/components/navigation/filter-bar.js +44 -1
  17. package/dist/components/navigation/index.d.ts +1 -1
  18. package/dist/components/navigation/steps.d.ts +2 -2
  19. package/dist/components/navigation/steps.js +4 -2
  20. package/dist/components/ui/avatar.d.ts +1 -1
  21. package/dist/components/ui/avatar.js +2 -1
  22. package/dist/components/ui/input-otp.d.ts +15 -3
  23. package/dist/components/ui/input-otp.js +2 -1
  24. package/dist/email/tokens.generated.d.ts +2 -2
  25. package/dist/email/tokens.generated.js +2 -2
  26. package/dist/i18n/messages/en.json +11 -0
  27. package/dist/i18n/messages/ja.json +8 -0
  28. package/dist/i18n/messages/vi.json +8 -0
  29. package/dist/props/components/data-display.prop.d.ts +16 -0
  30. package/dist/props/components/data-entry.prop.d.ts +7 -0
  31. package/dist/props/components/feedback.prop.d.ts +6 -0
  32. package/dist/props/components/layout.prop.d.ts +53 -0
  33. package/dist/props/components/navigation.prop.d.ts +61 -0
  34. package/dist/props/registry.d.ts +75 -1
  35. package/dist/props/registry.js +96 -1
  36. package/dist/props/vocabulary/interaction.prop.d.ts +10 -2
  37. package/dist/props/vocabulary/layout.prop.d.ts +5 -2
  38. package/dist/styles/alert-layout.css +36 -0
  39. package/dist/styles/card-layout.css +49 -13
  40. package/dist/styles/control.css +12 -2
  41. package/dist/styles/data-display-layout.css +16 -0
  42. package/dist/styles/layout.css +113 -0
  43. package/dist/styles/navigation-layout.css +8 -3
  44. package/dist/styles/shell-layout.css +44 -1
  45. package/dist/tokens/components/card.css +12 -0
  46. package/dist/tokens/components/control.css +16 -0
  47. package/dist/tokens/components/data-display.css +12 -0
  48. package/dist/tokens/components/email.css +8 -2
  49. package/dist/tokens/components/feedback.css +20 -0
  50. package/dist/tokens/components/navigation.css +35 -0
  51. package/dist/tokens/components/shell.css +64 -4
  52. package/dist/tokens/semantic/layout.css +13 -0
  53. package/package.json +2 -2
@@ -3,9 +3,21 @@ import { type VariantProps } from "class-variance-authority";
3
3
  import type { LucideIcon } from "lucide-react";
4
4
  import type { HeadingLevelProp } from "../../props/vocabulary/index.js";
5
5
  type CardSize = "md" | "compact";
6
- /** Semantic leading-edge accent stripe (border-inline-start; width via the
7
- * --card-accent-rail-width token, default 6px). */
6
+ /** Semantic accent tone. `accentPlacement` decides WHERE it is drawn — a leading-edge
7
+ * stripe (default) or the full perimeter. */
8
8
  type CardAccent = "primary" | "success" | "warning" | "info" | "attention" | "destructive";
9
+ /**
10
+ * Where the semantic `accent` tone is drawn.
11
+ *
12
+ * - `"edge"` (default) — the leading-edge stripe on `border-inline-start` only, at the
13
+ * `--card-accent-rail-width` measure (6px). The classic "this row needs a look" rail.
14
+ * - `"perimeter"` — a full attention border around the WHOLE card in the same semantic
15
+ * tone, measured by `--card-accent-perimeter-width` + `--card-accent-perimeter-ring-width`.
16
+ * This is what `variant="featured"` does, except the tone is yours instead of `--primary`,
17
+ * so a card can shout "action required" (`accent="attention"`) or "this failed"
18
+ * (`accent="destructive"`) without borrowing the brand colour.
19
+ */
20
+ type CardAccentPlacement = "edge" | "perimeter";
9
21
  /** Surface fill — plain card, muted band, borderless outline, or emphasized featured ring. */
10
22
  type CardVariant = "default" | "muted" | "outline" | "featured";
11
23
  /** Padding density — base 16px · tight 12px · cozy 20px. */
@@ -16,6 +28,11 @@ declare const cardVariants: (props?: ({
16
28
  export type CardProps = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof cardVariants> & {
17
29
  size?: CardSize;
18
30
  accent?: CardAccent;
31
+ /**
32
+ * Where `accent` is drawn — `"edge"` (default, the leading-edge stripe) or `"perimeter"`
33
+ * (a full attention border in the accent tone). Inert without `accent`.
34
+ */
35
+ accentPlacement?: CardAccentPlacement;
19
36
  variant?: CardVariant;
20
37
  density?: CardDensity;
21
38
  };
@@ -24,6 +41,11 @@ export declare const Card: React.ForwardRefExoticComponent<React.HTMLAttributes<
24
41
  } & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
25
42
  size?: CardSize;
26
43
  accent?: CardAccent;
44
+ /**
45
+ * Where `accent` is drawn — `"edge"` (default, the leading-edge stripe) or `"perimeter"`
46
+ * (a full attention border in the accent tone). Inert without `accent`.
47
+ */
48
+ accentPlacement?: CardAccentPlacement;
27
49
  variant?: CardVariant;
28
50
  density?: CardDensity;
29
51
  } & React.RefAttributes<HTMLDivElement>>;
@@ -136,9 +158,11 @@ export type StatCardProps = React.HTMLAttributes<HTMLDivElement> & VariantProps<
136
158
  inverse?: boolean;
137
159
  /** Semantic leading-edge rail (Card accent) — flags a KPI needing attention. */
138
160
  accent?: CardAccent;
161
+ /** Where `accent` is drawn — `"edge"` (default rail) or `"perimeter"` (full attention border). */
162
+ accentPlacement?: CardAccentPlacement;
139
163
  };
140
164
  /** KPI / stat tile — token-driven layout aligned to dashboard KPI cards. */
141
- export declare function StatCard({ label, value, hint, icon: Icon, delta, layout, align, inverse, accent, className, size, ...props }: StatCardProps): React.JSX.Element;
165
+ export declare function StatCard({ label, value, hint, icon: Icon, delta, layout, align, inverse, accent, accentPlacement, className, size, ...props }: StatCardProps): React.JSX.Element;
142
166
  /** Header actions slot — pair with `CardHeader className="flex flex-row …"`. */
143
167
  export declare const CardAction: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
144
168
  export {};
@@ -12,7 +12,7 @@ const cardVariants = cva("group/card", {
12
12
  defaultVariants: { size: "md" }
13
13
  });
14
14
  const Card = React.forwardRef(
15
- ({ className, size = "md", accent, variant, density, ...props }, ref) => /* @__PURE__ */ jsx(
15
+ ({ className, size = "md", accent, accentPlacement, variant, density, ...props }, ref) => /* @__PURE__ */ jsx(
16
16
  "div",
17
17
  {
18
18
  ref,
@@ -20,6 +20,7 @@ const Card = React.forwardRef(
20
20
  "data-slot": "card",
21
21
  "data-size": size === "compact" ? "compact" : void 0,
22
22
  "data-accent": accent,
23
+ "data-accent-placement": accentPlacement === "perimeter" ? "perimeter" : void 0,
23
24
  "data-variant": variant && variant !== "default" ? variant : void 0,
24
25
  "data-density": density,
25
26
  ...props
@@ -108,6 +109,7 @@ function StatCard({
108
109
  align = "start",
109
110
  inverse = false,
110
111
  accent,
112
+ accentPlacement,
111
113
  className,
112
114
  size = "compact",
113
115
  ...props
@@ -118,6 +120,7 @@ function StatCard({
118
120
  {
119
121
  size: size ?? "compact",
120
122
  accent,
123
+ accentPlacement,
121
124
  className: cn("ui-stat-card", className),
122
125
  "data-stat-card": "",
123
126
  "data-stat-layout": layout,
@@ -1,2 +1,2 @@
1
1
  export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from "../ui/input-otp.js";
2
- export type { InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProps, } from "../ui/input-otp.js";
2
+ export type { InputOTPAlignProp, InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProps, } from "../ui/input-otp.js";
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
2
  import type { ToneProp } from "../../props/vocabulary/index.js";
3
- import type { AlertQueryErrorProp } from "../../props/components/feedback.prop.js";
4
- export type { AlertProp, AlertProp as AlertProps, AlertTitleProp, AlertTitleProp as AlertTitleProps, AlertContentProp, AlertContentProp as AlertContentProps, AlertDescriptionProp, AlertDescriptionProp as AlertDescriptionProps, AlertActionsProp, AlertActionsProp as AlertActionsProps, AlertQueryErrorProp, AlertQueryErrorProp as AlertQueryErrorProps, } from "../../props/components/feedback.prop.js";
3
+ import type { AlertQueryErrorProp, BannerProp } from "../../props/components/feedback.prop.js";
4
+ export type { BannerProp, BannerProp as BannerProps, AlertProp, AlertProp as AlertProps, AlertTitleProp, AlertTitleProp as AlertTitleProps, AlertContentProp, AlertContentProp as AlertContentProps, AlertDescriptionProp, AlertDescriptionProp as AlertDescriptionProps, AlertActionsProp, AlertActionsProp as AlertActionsProps, AlertQueryErrorProp, AlertQueryErrorProp as AlertQueryErrorProps, } from "../../props/components/feedback.prop.js";
5
5
  declare const AlertBase: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
6
6
  variant?: import("../../props/index.js").AlertVariantProp;
7
7
  tone?: ToneProp;
@@ -63,4 +63,22 @@ export declare const Alert: React.ForwardRefExoticComponent<React.HTMLAttributes
63
63
  } & React.RefAttributes<HTMLDivElement>>;
64
64
  QueryError: typeof AlertQueryError;
65
65
  };
66
+ export declare const Banner: React.ForwardRefExoticComponent<BannerProp & React.RefAttributes<HTMLDivElement>> & {
67
+ Title: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & {
68
+ className?: import("../../props/index.js").ClassNameProp;
69
+ children?: import("../../props/index.js").ChildrenProp;
70
+ } & React.RefAttributes<HTMLParagraphElement>>;
71
+ Content: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
72
+ className?: import("../../props/index.js").ClassNameProp;
73
+ children?: import("../../props/index.js").ChildrenProp;
74
+ } & React.RefAttributes<HTMLDivElement>>;
75
+ Description: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & {
76
+ className?: import("../../props/index.js").ClassNameProp;
77
+ children?: import("../../props/index.js").ChildrenProp;
78
+ } & React.RefAttributes<HTMLParagraphElement>>;
79
+ Actions: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
80
+ className?: import("../../props/index.js").ClassNameProp;
81
+ children?: import("../../props/index.js").ChildrenProp;
82
+ } & React.RefAttributes<HTMLDivElement>>;
83
+ };
66
84
  export { AlertBase };
@@ -159,6 +159,15 @@ const Alert = Object.assign(AlertBase, {
159
159
  Actions: AlertActions,
160
160
  QueryError: AlertQueryError
161
161
  });
162
+ const BannerBase = React.forwardRef(function Banner(props, ref) {
163
+ return /* @__PURE__ */ jsx(AlertBase, { ...props, ref, variant: "banner" });
164
+ });
165
+ const Banner2 = Object.assign(BannerBase, {
166
+ Title: AlertTitle,
167
+ Content: AlertContent,
168
+ Description: AlertDescription,
169
+ Actions: AlertActions
170
+ });
162
171
  export {
163
172
  Alert,
164
173
  AlertActions,
@@ -166,5 +175,6 @@ export {
166
175
  AlertContent,
167
176
  AlertDescription,
168
177
  AlertQueryError,
169
- AlertTitle
178
+ AlertTitle,
179
+ Banner2 as Banner
170
180
  };
@@ -8,6 +8,6 @@ export { Toaster } from "./sonner.js";
8
8
  export { toast } from "./use-toast.js";
9
9
  export { Skeleton, SkeletonRows, SkeletonTable, SkeletonDetail, SkeletonStat } from "./skeleton.js";
10
10
  export type { SkeletonProps } from "./skeleton.js";
11
- export { Alert, AlertTitle, AlertContent, AlertDescription, AlertActions, AlertQueryError, } from "./alert.js";
12
- export type { AlertProp, AlertProps, AlertTitleProp, AlertTitleProps, AlertContentProp, AlertContentProps, AlertDescriptionProp, AlertDescriptionProps, AlertActionsProp, AlertActionsProps, AlertQueryErrorProp, AlertQueryErrorProps, } from "./alert.js";
11
+ export { Alert, AlertTitle, AlertContent, AlertDescription, AlertActions, AlertQueryError, Banner, } from "./alert.js";
12
+ export type { BannerProp, BannerProps, AlertProp, AlertProps, AlertTitleProp, AlertTitleProps, AlertContentProp, AlertContentProps, AlertDescriptionProp, AlertDescriptionProps, AlertActionsProp, AlertActionsProps, AlertQueryErrorProp, AlertQueryErrorProps, } from "./alert.js";
13
13
  export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "./tooltip.js";
@@ -49,7 +49,8 @@ import {
49
49
  AlertContent,
50
50
  AlertDescription,
51
51
  AlertActions,
52
- AlertQueryError
52
+ AlertQueryError,
53
+ Banner
53
54
  } from "./alert.js";
54
55
  import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "./tooltip.js";
55
56
  export {
@@ -71,6 +72,7 @@ export {
71
72
  AlertDialogTrigger,
72
73
  AlertQueryError,
73
74
  AlertTitle,
75
+ Banner,
74
76
  Dialog,
75
77
  DialogAction,
76
78
  DialogBody,
@@ -10,8 +10,10 @@ export type { AuthShellProp, AuthShellProp as AuthShellProps, } from "../../prop
10
10
  *
11
11
  * `preset` names the flow MEASURE (card max-width + desktop/mobile page gutters) so a consumer
12
12
  * never overrides page geometry by hand: `"login"` = SCR-001's stable identity/card/footer anchor
13
- * for standalone and real requester states; `"device-authorization"` = 380px card with a 5px
14
- * inline gutter at 390px; `"context-selection"` = 25rem card, edge-to-edge on mobile;
13
+ * for standalone and real requester states; `"registration"` = the 360px sign-up measure, the only
14
+ * START-aligned preset (a tall sign-up card must scroll, not clip its own top above the scroll
15
+ * origin) and the only one with its own footer clearance; `"device-authorization"` = 380px card
16
+ * with a 5px inline gutter at 390px; `"context-selection"` = 25rem card, edge-to-edge on mobile;
15
17
  * `"account-recovery"` = the 432px SCR-008 panel measure shared by password recovery and the
16
18
  * sign-in MFA challenge, with a 15px inline gutter at 390px. It is orthogonal to `variant` —
17
19
  * combine `variant="canonical"` with any preset.
@@ -1,5 +1,7 @@
1
1
  export { PageContainer } from "./page-container.js";
2
2
  export type { PageContainerProp, PageContainerProps, BreadcrumbItem, BreadcrumbItemProp, } from "./page-container.js";
3
+ export { PageHeader } from "./page-header.js";
4
+ export type { PageHeaderProp, PageHeaderProps } from "./page-header.js";
3
5
  export { Flex } from "./flex.js";
4
6
  export type { FlexAlignProp, FlexDirectionProp, FlexJustifyProp, FlexProp, FlexProps, } from "./flex.js";
5
7
  export { ResizablePanel, ResizablePanelGroup, ResizableHandle } from "./resizable.js";
@@ -1,4 +1,5 @@
1
1
  import { PageContainer } from "./page-container.js";
2
+ import { PageHeader } from "./page-header.js";
2
3
  import { Flex } from "./flex.js";
3
4
  import { ResizablePanel, ResizablePanelGroup, ResizableHandle } from "./resizable.js";
4
5
  import { AppShell } from "./app-shell.js";
@@ -37,6 +38,7 @@ export {
37
38
  MasterDetail,
38
39
  OrgSwitcher,
39
40
  PageContainer,
41
+ PageHeader,
40
42
  ResizableHandle,
41
43
  ResizablePanel,
42
44
  ResizablePanelGroup,
@@ -2,7 +2,7 @@ import type { PageContainerProp, PageInsetProp } from "../../props/components/la
2
2
  export type { PageContainerProp, PageContainerProp as PageContainerProps, } from "../../props/components/layout.prop.js";
3
3
  export type { BreadcrumbItemProp, BreadcrumbItemProp as BreadcrumbItem, } from "../../props/vocabulary/navigation.prop.js";
4
4
  export declare function PageContainerInset({ className, children, ...props }: PageInsetProp): import("react").JSX.Element;
5
- declare function PageContainerRoot({ title, subtitle, extra, footer, breadcrumb, breadcrumbLabel, breadcrumbAriaLabel, linkComponent: LinkComponent, density, variant, preset, headerLayout, measure, stickyFooter, footerReveal, fill, children, className, }: PageContainerProp): import("react").JSX.Element;
5
+ declare function PageContainerRoot({ title, subtitle, meta, headerLoading, extra, footer, breadcrumb, breadcrumbLabel, breadcrumbAriaLabel, linkComponent: LinkComponent, density, variant, preset, headerLayout, measure, stickyFooter, footerReveal, fill, children, className, }: PageContainerProp): import("react").JSX.Element;
6
6
  export declare const PageContainer: typeof PageContainerRoot & {
7
7
  Inset: typeof PageContainerInset;
8
8
  };
@@ -1,10 +1,9 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from "react";
4
- import { ChevronRight } from "lucide-react";
5
- import { useTranslation } from "../../i18n/use-translation.js";
6
4
  import { cn } from "../../lib/utils.js";
7
5
  import { densityClass, pageContainerVariantClass } from "../../lib/variants.js";
6
+ import { PageHeader } from "./page-header.js";
8
7
  function scrollParent(el) {
9
8
  let node = el?.parentElement ?? null;
10
9
  while (node) {
@@ -36,6 +35,8 @@ function PageContainerInset({ className, children, ...props }) {
36
35
  function PageContainerRoot({
37
36
  title,
38
37
  subtitle,
38
+ meta,
39
+ headerLoading,
39
40
  extra,
40
41
  footer,
41
42
  breadcrumb,
@@ -55,7 +56,6 @@ function PageContainerRoot({
55
56
  }) {
56
57
  const reveal = stickyFooter && footer != null && footerReveal === "onScroll";
57
58
  const { headerRef, revealed } = useFooterReveal(reveal);
58
- const { t } = useTranslation();
59
59
  return /* @__PURE__ */ jsxs(
60
60
  "div",
61
61
  {
@@ -74,44 +74,22 @@ function PageContainerRoot({
74
74
  className
75
75
  ),
76
76
  children: [
77
- /* @__PURE__ */ jsxs("header", { ref: headerRef, className: "ui-page-header", "data-layout": headerLayout, children: [
78
- breadcrumb && breadcrumb.length > 0 && /* @__PURE__ */ jsx(
79
- "nav",
80
- {
81
- "aria-label": breadcrumbLabel ?? breadcrumbAriaLabel ?? t("navigation.breadcrumb.ariaLabel"),
82
- className: "ui-breadcrumb",
83
- children: /* @__PURE__ */ jsx("ol", { className: "ui-breadcrumb-list", children: breadcrumb.map((item, i) => {
84
- const isLast = i === breadcrumb.length - 1;
85
- return /* @__PURE__ */ jsxs("li", { className: "ui-inline-xs", children: [
86
- item.to && !isLast ? /* @__PURE__ */ jsx(
87
- LinkComponent,
88
- {
89
- href: item.to,
90
- to: item.to,
91
- className: "hover:text-foreground hover:underline",
92
- children: item.label
93
- }
94
- ) : /* @__PURE__ */ jsx(
95
- "span",
96
- {
97
- className: isLast ? "text-foreground" : "",
98
- "aria-current": isLast ? "page" : void 0,
99
- children: item.label
100
- }
101
- ),
102
- !isLast && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3", "aria-hidden": "true" })
103
- ] }, i);
104
- }) })
105
- }
106
- ),
107
- /* @__PURE__ */ jsxs("div", { className: "ui-page-header-row", children: [
108
- /* @__PURE__ */ jsxs("div", { className: "ui-page-header-heading min-w-0", children: [
109
- /* @__PURE__ */ jsx("h1", { className: "ui-page-title", children: title }),
110
- subtitle && /* @__PURE__ */ jsx("p", { className: "ui-page-subtitle", children: subtitle })
111
- ] }),
112
- extra && /* @__PURE__ */ jsx("div", { className: "ui-page-header-extra", children: extra })
113
- ] })
114
- ] }),
77
+ /* @__PURE__ */ jsx(
78
+ PageHeader,
79
+ {
80
+ ref: headerRef,
81
+ title,
82
+ subtitle,
83
+ meta,
84
+ extra,
85
+ breadcrumb,
86
+ breadcrumbLabel,
87
+ breadcrumbAriaLabel,
88
+ linkComponent: LinkComponent,
89
+ layout: headerLayout,
90
+ loading: headerLoading
91
+ }
92
+ ),
115
93
  children != null && /* @__PURE__ */ jsx("div", { className: "ui-page-body", children }),
116
94
  footer && /* @__PURE__ */ jsx("footer", { className: "ui-page-footer", children: footer })
117
95
  ]
@@ -0,0 +1,22 @@
1
+ /**
2
+ * PageHeader — the canonical page title band (gh#255).
3
+ *
4
+ * This is the geometry `PageContainer` has always rendered, lifted into its own export so a
5
+ * consumer can mount it OUTSIDE a full page shell — a Sheet detail, a `MasterDetail` detail pane, a
6
+ * tab body, a nested route header — without re-authoring `.ui-page-header` CSS locally. There is
7
+ * exactly ONE implementation: `PageContainer` renders this component, so the two can never drift.
8
+ *
9
+ * The band owns: breadcrumbs (a named `<nav>` landmark with `aria-current="page"` on the leaf), the
10
+ * `<h1>` title, the subtitle/description, a status/meta slot, the `extra` action region, the
11
+ * responsive overflow arrangement (`layout`), and the pending state (`loading`). It owns no page
12
+ * chrome — padding, measure and body rhythm stay with `PageContainer`.
13
+ *
14
+ * DENIED / ERROR are deliberately NOT states of this component: a page the user may not see must
15
+ * not render its title, breadcrumb trail or actions at all (they leak the resource's existence and
16
+ * its name). That whole-surface contract belongs to `ErrorSurface`, which is the canonical public
17
+ * replacement — see `docs/CANONICAL-CONTRACTS.md`.
18
+ */
19
+ import * as React from "react";
20
+ import type { PageHeaderProp } from "../../props/components/layout.prop.js";
21
+ export type { PageHeaderProp, PageHeaderProp as PageHeaderProps, } from "../../props/components/layout.prop.js";
22
+ export declare const PageHeader: React.ForwardRefExoticComponent<PageHeaderProp & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,89 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { ChevronRight } from "lucide-react";
5
+ import { useTranslation } from "../../i18n/use-translation.js";
6
+ import { cn } from "../../lib/utils.js";
7
+ const PageHeader = React.forwardRef(function PageHeader2({
8
+ title,
9
+ subtitle,
10
+ meta,
11
+ extra,
12
+ breadcrumb,
13
+ breadcrumbLabel,
14
+ breadcrumbAriaLabel,
15
+ linkComponent: LinkComponent = "a",
16
+ layout = "stack",
17
+ loading = false,
18
+ className
19
+ }, ref) {
20
+ const { t } = useTranslation();
21
+ return (
22
+ // `data-layout` is the only hook the header arrangement needs: `stack` (the default)
23
+ // matches no rule, so the historical geometry is untouched, while `responsive-inline`
24
+ // selects the compact-range rules that keep `extra` beside the title band (gh#231).
25
+ /* @__PURE__ */ jsxs(
26
+ "header",
27
+ {
28
+ ref,
29
+ className: cn("ui-page-header", className),
30
+ "data-layout": layout,
31
+ "aria-busy": loading ? "true" : void 0,
32
+ children: [
33
+ breadcrumb && breadcrumb.length > 0 && /* @__PURE__ */ jsx(
34
+ "nav",
35
+ {
36
+ "aria-label": breadcrumbLabel ?? breadcrumbAriaLabel ?? t("navigation.breadcrumb.ariaLabel"),
37
+ className: "ui-breadcrumb",
38
+ children: /* @__PURE__ */ jsx("ol", { className: "ui-breadcrumb-list", children: breadcrumb.map((item, i) => {
39
+ const isLast = i === breadcrumb.length - 1;
40
+ return /* @__PURE__ */ jsxs("li", { className: "ui-inline-xs", children: [
41
+ item.to && !isLast ? /* @__PURE__ */ jsx(
42
+ LinkComponent,
43
+ {
44
+ href: item.to,
45
+ to: item.to,
46
+ className: "hover:text-foreground hover:underline",
47
+ children: item.label
48
+ }
49
+ ) : /* @__PURE__ */ jsx(
50
+ "span",
51
+ {
52
+ className: isLast ? "text-foreground" : "",
53
+ "aria-current": isLast ? "page" : void 0,
54
+ children: item.label
55
+ }
56
+ ),
57
+ !isLast && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3", "aria-hidden": "true" })
58
+ ] }, i);
59
+ }) })
60
+ }
61
+ ),
62
+ /* @__PURE__ */ jsxs("div", { className: "ui-page-header-row", children: [
63
+ /* @__PURE__ */ jsxs("div", { className: "ui-page-header-heading min-w-0", children: [
64
+ loading ? /* @__PURE__ */ jsx("h1", { className: "ui-page-title ui-skeleton-block ui-page-title-placeholder", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("layout.pageHeader.loading") }) }) : meta != null ? /* @__PURE__ */ jsxs("div", { className: "ui-page-title-band", children: [
65
+ /* @__PURE__ */ jsx("h1", { className: "ui-page-title", children: title }),
66
+ /* @__PURE__ */ jsx("div", { className: "ui-page-header-meta", children: meta })
67
+ ] }) : /* @__PURE__ */ jsx("h1", { className: "ui-page-title", children: title }),
68
+ loading ? (
69
+ // Decorative only — the pending state is already announced once by the heading above,
70
+ // so a second live placeholder here would double-announce it.
71
+ /* @__PURE__ */ jsx(
72
+ "p",
73
+ {
74
+ className: "ui-page-subtitle ui-skeleton-block ui-page-subtitle-placeholder",
75
+ "aria-hidden": "true"
76
+ }
77
+ )
78
+ ) : subtitle && /* @__PURE__ */ jsx("p", { className: "ui-page-subtitle", children: subtitle })
79
+ ] }),
80
+ extra && /* @__PURE__ */ jsx("div", { className: "ui-page-header-extra", children: extra })
81
+ ] })
82
+ ]
83
+ }
84
+ )
85
+ );
86
+ });
87
+ export {
88
+ PageHeader
89
+ };
@@ -1,6 +1,6 @@
1
1
  import type { ToolbarGroupProp, ToolbarProp } from "../../props/components/navigation.prop.js";
2
- export type { FilterBarOverflowProp, ToolbarGroupProp, ToolbarGroupProp as ToolbarGroupProps, ToolbarGroupProp as FilterBarGroupProp, ToolbarGroupProp as FilterBarGroupProps, ToolbarProp, ToolbarProp as ToolbarProps, ToolbarProp as FilterBarProp, ToolbarProp as FilterBarProps, } from "../../props/components/navigation.prop.js";
3
- export declare function Toolbar({ onClear, hasActiveFilters, sticky, overflow, className, children, }: ToolbarProp): import("react").JSX.Element;
2
+ export type { FilterBarChipProp, FilterBarChipProp as FilterBarChipProps, FilterBarOverflowProp, ToolbarGroupProp, ToolbarGroupProp as ToolbarGroupProps, ToolbarGroupProp as FilterBarGroupProp, ToolbarGroupProp as FilterBarGroupProps, ToolbarProp, ToolbarProp as ToolbarProps, ToolbarProp as FilterBarProp, ToolbarProp as FilterBarProps, } from "../../props/components/navigation.prop.js";
3
+ export declare function Toolbar({ onClear, hasActiveFilters, sticky, overflow, search, chips, onChipRemove, resultCount, actions, className, children, }: ToolbarProp): import("react").JSX.Element;
4
4
  export declare function ToolbarGroup({ label, controlId, className, children }: ToolbarGroupProp): import("react").JSX.Element;
5
5
  /**
6
6
  * Canonical list-page filter strip.
@@ -10,10 +10,16 @@ function Toolbar({
10
10
  hasActiveFilters = true,
11
11
  sticky = false,
12
12
  overflow = "wrap",
13
+ search,
14
+ chips,
15
+ onChipRemove,
16
+ resultCount,
17
+ actions,
13
18
  className,
14
19
  children
15
20
  }) {
16
21
  const { t } = useTranslation();
22
+ const hasChips = chips != null && chips.length > 0;
17
23
  return /* @__PURE__ */ jsxs(
18
24
  "div",
19
25
  {
@@ -22,11 +28,48 @@ function Toolbar({
22
28
  "data-overflow": overflow,
23
29
  className: cn("ui-toolbar", sticky && "ui-toolbar-sticky", className),
24
30
  children: [
31
+ search != null && // A region, not a bare child, so the search control gets ONE measure
32
+ // (--filter-bar-search-width) across every list page instead of whatever width the
33
+ // caller's control happened to have.
34
+ /* @__PURE__ */ jsx("div", { className: "ui-filter-bar-search", children: search }),
25
35
  children,
36
+ hasChips && // `group` (not a second toolbar) — a toolbar may own a group, and the chips are a labelled
37
+ // set of related controls rather than an independent toolbar competing for the same role.
38
+ /* @__PURE__ */ jsx(
39
+ "div",
40
+ {
41
+ role: "group",
42
+ "aria-label": t("navigation.filterBar.appliedFilters"),
43
+ className: "ui-filter-bar-chips",
44
+ children: chips.map((chip) => /* @__PURE__ */ jsxs("span", { className: "ui-filter-bar-chip", children: [
45
+ /* @__PURE__ */ jsx("span", { className: "ui-filter-bar-chip-label", children: chip.label }),
46
+ (chip.onRemove || onChipRemove) && /* @__PURE__ */ jsx(
47
+ Button,
48
+ {
49
+ variant: "ghost",
50
+ size: "xs",
51
+ className: "ui-filter-bar-chip-remove",
52
+ "aria-label": t("navigation.filterBar.removeFilter", { label: chip.label }),
53
+ onClick: () => {
54
+ chip.onRemove?.();
55
+ onChipRemove?.(chip.id);
56
+ },
57
+ children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
58
+ }
59
+ )
60
+ ] }, chip.id))
61
+ }
62
+ ),
63
+ resultCount !== void 0 && // The whole reason this is a prop rather than caller markup: a sighted user SEES the table
64
+ // change, and a polite live region is what tells everyone else that filtering happened.
65
+ // The number is grouped by `Intl.NumberFormat` and the noun selected by CLDR plural rules
66
+ // for the active locale — never a hand-built "N items".
67
+ /* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", className: "ui-filter-bar-count", children: t("navigation.filterBar.resultCount", { count: resultCount }) }),
26
68
  onClear && hasActiveFilters && /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: onClear, className: "ui-toolbar-clear", children: [
27
69
  /* @__PURE__ */ jsx(X, { "aria-hidden": "true" }),
28
70
  t("common.clearFilters")
29
- ] })
71
+ ] }),
72
+ actions != null && /* @__PURE__ */ jsx("div", { className: "ui-filter-bar-actions", children: actions })
30
73
  ]
31
74
  }
32
75
  );
@@ -5,7 +5,7 @@ export { Menubar, MenubarCheckboxItem, MenubarContent, MenubarItem, MenubarLabel
5
5
  export { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, } from "./navigation-menu.js";
6
6
  export { Steps } from "./steps.js";
7
7
  export { FilterBar, FilterBarGroup, Toolbar, ToolbarGroup } from "./filter-bar.js";
8
- export type { FilterBarGroupProp, FilterBarGroupProps, FilterBarProp, FilterBarProps, ToolbarGroupProps, ToolbarProps, } from "./filter-bar.js";
8
+ export type { FilterBarChipProp, FilterBarChipProps, FilterBarGroupProp, FilterBarGroupProps, FilterBarProp, FilterBarProps, ToolbarGroupProps, ToolbarProps, } from "./filter-bar.js";
9
9
  export { AppSettingPicker } from "./app-setting-picker.js";
10
10
  export type { AppSettingPickerProp, AppSettingPickerProps, AppSettingKind, } from "./app-setting-picker.js";
11
11
  export type { BreadcrumbItemProp as BreadcrumbItem } from "../../props/vocabulary/navigation.prop.js";
@@ -1,4 +1,4 @@
1
1
  import * as React from "react";
2
2
  import type { StepsProp } from "../../props/components/navigation.prop.js";
3
- export type { StepsProp, StepsProp as StepsProps, StepItemProp, StepStatusProp, } from "../../props/components/navigation.prop.js";
4
- export declare function Steps({ items, value: current, defaultValue, status: currentStatus, orientation, type, size, titlePlacement, onValueChange, className, }: StepsProp): React.JSX.Element;
3
+ export type { StepsProp, StepsProp as StepsProps, StepItemProp, StepStatusProp, StepsSeparatorProp, } from "../../props/components/navigation.prop.js";
4
+ export declare function Steps({ items, value: current, defaultValue, status: currentStatus, orientation, type, size, titlePlacement, separator, onValueChange, className, }: StepsProp): React.JSX.Element;
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { Check, ChevronRight, Circle, Loader2, X } from "lucide-react";
3
+ import { ArrowRight, Check, ChevronRight, Circle, Loader2, X } from "lucide-react";
4
4
  import { useTranslation } from "../../i18n/use-translation.js";
5
5
  import { cn } from "../../lib/utils.js";
6
6
  import { controlIconClass } from "../../lib/control-styles.js";
@@ -61,6 +61,7 @@ function Steps({
61
61
  type = "default",
62
62
  size = "md",
63
63
  titlePlacement = "horizontal",
64
+ separator = "chevron",
64
65
  onValueChange,
65
66
  className
66
67
  }) {
@@ -69,6 +70,7 @@ function Steps({
69
70
  const isVertical = orientation === "vertical";
70
71
  const compact = size === "sm";
71
72
  const inline = type === "inline";
73
+ const SeparatorIcon = separator === "arrow" ? ArrowRight : ChevronRight;
72
74
  return /* @__PURE__ */ jsx(
73
75
  "ol",
74
76
  {
@@ -151,7 +153,7 @@ function Steps({
151
153
  }
152
154
  ) : /* @__PURE__ */ jsx("span", { className: stepClassName, children: stepInner }),
153
155
  inline && index < items.length - 1 ? /* @__PURE__ */ jsx(
154
- ChevronRight,
156
+ SeparatorIcon,
155
157
  {
156
158
  className: "ui-steps-inline-separator rtl:rotate-180",
157
159
  "aria-hidden": "true"
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import * as AvatarPrimitive from "@radix-ui/react-avatar";
3
3
  import type { AvatarProp } from "../../props/components/data-display.prop.js";
4
- export declare const Avatar: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarProps & React.RefAttributes<HTMLSpanElement>, "ref"> & Pick<AvatarProp, "shape"> & React.RefAttributes<HTMLSpanElement>>;
4
+ export declare const Avatar: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarProps & React.RefAttributes<HTMLSpanElement>, "ref"> & Pick<AvatarProp, "shape" | "appearance"> & React.RefAttributes<HTMLSpanElement>>;
5
5
  export declare const AvatarImage: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarImageProps & React.RefAttributes<HTMLImageElement>, "ref"> & React.RefAttributes<HTMLImageElement>>;
6
6
  export declare const AvatarFallback: React.ForwardRefExoticComponent<Omit<AvatarPrimitive.AvatarFallbackProps & React.RefAttributes<HTMLSpanElement>, "ref"> & React.RefAttributes<HTMLSpanElement>>;
@@ -3,12 +3,13 @@ import { jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import * as AvatarPrimitive from "@radix-ui/react-avatar";
5
5
  import { cn } from "../../lib/utils.js";
6
- const Avatar = React.forwardRef(({ className, shape = "circle", ...props }, ref) => /* @__PURE__ */ jsx(
6
+ const Avatar = React.forwardRef(({ className, shape = "circle", appearance = "default", ...props }, ref) => /* @__PURE__ */ jsx(
7
7
  AvatarPrimitive.Root,
8
8
  {
9
9
  ref,
10
10
  "data-slot": "avatar",
11
11
  "data-shape": shape === "square" ? "square" : void 0,
12
+ "data-appearance": appearance === "tinted" ? "tinted" : void 0,
12
13
  className: cn("ui-avatar", className),
13
14
  ...props
14
15
  }
@@ -1,5 +1,15 @@
1
1
  import * as React from "react";
2
- export declare const InputOTP: React.ForwardRefExoticComponent<(Omit<Omit<React.InputHTMLAttributes<HTMLInputElement>, "onChange" | "value" | "maxLength" | "textAlign" | "onComplete" | "pushPasswordManagerStrategy" | "pasteTransformer" | "containerClassName" | "noScriptCSSFallback"> & {
2
+ import type { InputOTPAlignProp } from "../../props/components/data-entry.prop.js";
3
+ /**
4
+ * InputOTP — the code-challenge field.
5
+ *
6
+ * `align` positions the whole row (groups + separators) on the main axis: `start` (default,
7
+ * unchanged), `center` — the canonical auth challenge — or `end`. The attribute lands on the
8
+ * hidden input because `input-otp` owns the container element; `.ui-otp-container` reads it back
9
+ * through `:has()`, exactly as it already does for the invalid and disabled states. A service that
10
+ * wants every code field centred sets `--otp-container-align` once instead.
11
+ */
12
+ export declare const InputOTP: React.ForwardRefExoticComponent<((Omit<Omit<React.InputHTMLAttributes<HTMLInputElement>, "onChange" | "value" | "maxLength" | "textAlign" | "onComplete" | "pushPasswordManagerStrategy" | "pasteTransformer" | "containerClassName" | "noScriptCSSFallback"> & {
3
13
  value?: string;
4
14
  onChange?: (newValue: string) => unknown;
5
15
  maxLength: number;
@@ -25,8 +35,10 @@ export declare const InputOTP: React.ForwardRefExoticComponent<(Omit<Omit<React.
25
35
  } & {
26
36
  render?: never;
27
37
  children: React.ReactNode;
28
- } & React.RefAttributes<HTMLInputElement>, "ref">) & React.RefAttributes<HTMLInputElement>>;
29
- export type { InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProp as InputOTPGroupProps, } from "../../props/components/data-entry.prop.js";
38
+ } & React.RefAttributes<HTMLInputElement>, "ref">) & {
39
+ align?: InputOTPAlignProp;
40
+ }) & React.RefAttributes<HTMLInputElement>>;
41
+ export type { InputOTPAlignProp, InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProp as InputOTPGroupProps, } from "../../props/components/data-entry.prop.js";
30
42
  export declare const InputOTPGroup: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
31
43
  appearance?: import("./input-otp.js").InputOTPGroupAppearanceProp;
32
44
  } & React.RefAttributes<HTMLDivElement>>;