@godxjp/ui 18.5.0 → 18.7.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 (101) hide show
  1. package/dist/components/charts/compact-bar-trend.d.ts +2 -1
  2. package/dist/components/charts/index.d.ts +4 -1
  3. package/dist/components/data-display/data-table.d.ts +25 -4
  4. package/dist/components/data-display/data-table.js +13 -2
  5. package/dist/components/data-display/empty-state.js +1 -1
  6. package/dist/components/data-display/index.d.ts +1 -1
  7. package/dist/components/data-display/list-row.d.ts +19 -0
  8. package/dist/components/data-display/list-row.js +2 -0
  9. package/dist/components/data-display/qr-code.d.ts +1 -1
  10. package/dist/components/data-display/table.d.ts +47 -2
  11. package/dist/components/data-display/table.js +29 -4
  12. package/dist/components/data-entry/command.d.ts +10 -10
  13. package/dist/components/data-entry/index.d.ts +1 -0
  14. package/dist/components/data-entry/input-otp.d.ts +1 -0
  15. package/dist/components/feedback/alert.d.ts +20 -2
  16. package/dist/components/feedback/alert.js +11 -1
  17. package/dist/components/feedback/dialog.d.ts +14 -1
  18. package/dist/components/feedback/dialog.js +4 -0
  19. package/dist/components/feedback/index.d.ts +3 -3
  20. package/dist/components/feedback/index.js +5 -1
  21. package/dist/components/general/logo.d.ts +7 -1
  22. package/dist/components/general/logo.js +1 -1
  23. package/dist/components/layout/app-shell.d.ts +1 -1
  24. package/dist/components/layout/app-shell.js +55 -46
  25. package/dist/components/layout/auth-account-summary.d.ts +5 -0
  26. package/dist/components/layout/auth-account-summary.js +31 -0
  27. package/dist/components/layout/auth-shell.d.ts +5 -2
  28. package/dist/components/layout/centered-shell.d.ts +1 -1
  29. package/dist/components/layout/centered-shell.js +28 -19
  30. package/dist/components/layout/error-surface.d.ts +35 -0
  31. package/dist/components/layout/error-surface.js +155 -0
  32. package/dist/components/layout/flex.d.ts +1 -1
  33. package/dist/components/layout/flex.js +4 -0
  34. package/dist/components/layout/index.d.ts +7 -0
  35. package/dist/components/layout/index.js +6 -0
  36. package/dist/components/layout/page-container.d.ts +1 -1
  37. package/dist/components/layout/page-container.js +23 -41
  38. package/dist/components/layout/page-header.d.ts +22 -0
  39. package/dist/components/layout/page-header.js +89 -0
  40. package/dist/components/navigation/filter-bar.d.ts +2 -2
  41. package/dist/components/navigation/filter-bar.js +44 -1
  42. package/dist/components/navigation/index.d.ts +1 -1
  43. package/dist/components/navigation/steps.js +38 -19
  44. package/dist/components/navigation/tabs.js +12 -3
  45. package/dist/components/ui/avatar.d.ts +2 -1
  46. package/dist/components/ui/avatar.js +2 -1
  47. package/dist/components/ui/input-otp.d.ts +4 -1
  48. package/dist/components/ui/input-otp.js +2 -1
  49. package/dist/components/ui/toggle-group.d.ts +7 -4
  50. package/dist/components/ui/toggle-group.js +32 -21
  51. package/dist/email/brand-mark.d.ts +21 -5
  52. package/dist/email/brand-mark.js +5 -0
  53. package/dist/email/color.d.ts +2 -1
  54. package/dist/email/geometry.d.ts +9 -1
  55. package/dist/email/geometry.js +1 -0
  56. package/dist/email/index.d.ts +6 -1
  57. package/dist/email/index.js +6 -0
  58. package/dist/email/inline-style.d.ts +7 -5
  59. package/dist/email/inline-style.js +5 -0
  60. package/dist/email/tokens.generated.d.ts +40 -20
  61. package/dist/email/tokens.generated.js +40 -20
  62. package/dist/email/urgency.d.ts +22 -0
  63. package/dist/email/urgency.js +14 -0
  64. package/dist/i18n/messages/en.json +26 -1
  65. package/dist/i18n/messages/ja.json +23 -1
  66. package/dist/i18n/messages/vi.json +23 -1
  67. package/dist/props/components/data-display.prop.d.ts +63 -1
  68. package/dist/props/components/data-entry.prop.d.ts +6 -0
  69. package/dist/props/components/feedback.prop.d.ts +6 -0
  70. package/dist/props/components/index.d.ts +1 -1
  71. package/dist/props/components/layout.prop.d.ts +244 -4
  72. package/dist/props/components/navigation.prop.d.ts +57 -1
  73. package/dist/props/registry.d.ts +205 -5
  74. package/dist/props/registry.js +270 -6
  75. package/dist/props/vocabulary/data.prop.d.ts +27 -0
  76. package/dist/props/vocabulary/index.d.ts +3 -3
  77. package/dist/props/vocabulary/interaction.prop.d.ts +19 -2
  78. package/dist/props/vocabulary/layout.prop.d.ts +41 -7
  79. package/dist/styles/alert-layout.css +36 -0
  80. package/dist/styles/control.css +28 -0
  81. package/dist/styles/data-display-layout.css +26 -0
  82. package/dist/styles/layout.css +212 -0
  83. package/dist/styles/logo-layout.css +42 -9
  84. package/dist/styles/navigation-layout.css +109 -0
  85. package/dist/styles/shell-layout.css +336 -0
  86. package/dist/styles/table-layout.css +164 -1
  87. package/dist/theme/dxs.canonical.css +5 -4
  88. package/dist/tokens/axes.css +4 -3
  89. package/dist/tokens/base.css +1 -0
  90. package/dist/tokens/components/data-display.css +13 -0
  91. package/dist/tokens/components/email.css +43 -16
  92. package/dist/tokens/components/error-surface.css +36 -0
  93. package/dist/tokens/components/feedback.css +20 -0
  94. package/dist/tokens/components/list-row.css +20 -0
  95. package/dist/tokens/components/logo.css +52 -10
  96. package/dist/tokens/components/navigation.css +41 -0
  97. package/dist/tokens/components/shell.css +109 -0
  98. package/dist/tokens/components/table.css +45 -0
  99. package/dist/tokens/foundation.css +26 -0
  100. package/dist/tokens/semantic/layout.css +38 -0
  101. package/package.json +8 -2
@@ -15,6 +15,7 @@ function AppShell({
15
15
  footer,
16
16
  children,
17
17
  sidebarCollapsed = false,
18
+ responsiveNavigation = "drawer",
18
19
  mobileNav,
19
20
  mobileNavLabel,
20
21
  mobileNavOpen,
@@ -22,7 +23,7 @@ function AppShell({
22
23
  }) {
23
24
  const { t } = useTranslation();
24
25
  const drawerNav = mobileNav !== void 0 ? mobileNav : sidebar;
25
- const hasDrawer = drawerNav != null;
26
+ const hasDrawer = responsiveNavigation === "drawer" && drawerNav != null;
26
27
  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);
27
28
  const drawerOpen = mobileNavOpen ?? uncontrolledOpen;
28
29
  const setDrawerOpen = onMobileNavOpenChange ?? setUncontrolledOpen;
@@ -39,51 +40,59 @@ function AppShell({
39
40
  /* @__PURE__ */ jsx("div", { className: "app-topbar-spacer" }),
40
41
  topbarRight !== void 0 && /* @__PURE__ */ jsx("div", { className: "app-topbar-right", children: topbarRight })
41
42
  ] });
42
- return /* @__PURE__ */ jsxs("div", { className: "app-root", "data-collapsed": sidebarCollapsed ? "true" : void 0, children: [
43
- /* @__PURE__ */ jsx("aside", { className: "app-sidebar", "aria-label": t("layout.appShell.sidebarLabel"), children: sidebar }),
44
- /* @__PURE__ */ jsxs("header", { className: "app-topbar ui-scale-fixed", "aria-label": t("layout.appShell.headerLabel"), children: [
45
- hasDrawer && /* @__PURE__ */ jsxs(Sheet, { open: drawerOpen, onOpenChange: setDrawerOpen, children: [
46
- /* @__PURE__ */ jsx(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
47
- Button,
48
- {
49
- type: "button",
50
- variant: "ghost",
51
- size: "sm",
52
- className: "app-mobile-nav-trigger hidden max-[900px]:inline-flex",
53
- "aria-label": t("layout.appShell.openNav"),
54
- "aria-haspopup": "dialog",
55
- children: /* @__PURE__ */ jsx(Menu, { className: "size-5", "aria-hidden": "true" })
56
- }
57
- ) }),
58
- /* @__PURE__ */ jsxs(
59
- SheetContent,
60
- {
61
- side: "left",
62
- width: "var(--app-shell-mobile-nav-width)",
63
- className: "app-mobile-nav-drawer",
64
- overlayClassName: "app-mobile-nav-overlay",
65
- children: [
66
- /* @__PURE__ */ jsx(SheetHeader, { title: mobileNavLabel ?? t("layout.appShell.navLabel") }),
67
- /* @__PURE__ */ jsx(
68
- SheetBody,
69
- {
70
- className: "app-mobile-nav-body px-[var(--app-shell-mobile-nav-inset)]",
71
- onClick: handleDrawerClick,
72
- children: drawerNav
73
- }
74
- )
75
- ]
76
- }
77
- )
78
- ] }),
79
- resolvedTopbar
80
- ] }),
81
- /* @__PURE__ */ jsxs("main", { className: "app-main", "aria-label": t("layout.appShell.mainLabel"), tabIndex: 0, children: [
82
- breadcrumb !== void 0 && /* @__PURE__ */ jsx("div", { className: "app-breadcrumb", children: breadcrumb }),
83
- children
84
- ] }),
85
- footer !== void 0 && /* @__PURE__ */ jsx("footer", { className: "app-footer", "aria-label": t("layout.appShell.footerLabel"), children: footer })
86
- ] });
43
+ return /* @__PURE__ */ jsxs(
44
+ "div",
45
+ {
46
+ className: "app-root",
47
+ "data-collapsed": sidebarCollapsed ? "true" : void 0,
48
+ "data-responsive-navigation": responsiveNavigation,
49
+ children: [
50
+ /* @__PURE__ */ jsx("aside", { className: "app-sidebar", "aria-label": t("layout.appShell.sidebarLabel"), children: sidebar }),
51
+ /* @__PURE__ */ jsxs("header", { className: "app-topbar ui-scale-fixed", "aria-label": t("layout.appShell.headerLabel"), children: [
52
+ hasDrawer && /* @__PURE__ */ jsxs(Sheet, { open: drawerOpen, onOpenChange: setDrawerOpen, children: [
53
+ /* @__PURE__ */ jsx(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
54
+ Button,
55
+ {
56
+ type: "button",
57
+ variant: "ghost",
58
+ size: "sm",
59
+ className: "app-mobile-nav-trigger hidden max-[900px]:inline-flex",
60
+ "aria-label": t("layout.appShell.openNav"),
61
+ "aria-haspopup": "dialog",
62
+ children: /* @__PURE__ */ jsx(Menu, { className: "size-5", "aria-hidden": "true" })
63
+ }
64
+ ) }),
65
+ /* @__PURE__ */ jsxs(
66
+ SheetContent,
67
+ {
68
+ side: "left",
69
+ width: "var(--app-shell-mobile-nav-width)",
70
+ className: "app-mobile-nav-drawer",
71
+ overlayClassName: "app-mobile-nav-overlay",
72
+ children: [
73
+ /* @__PURE__ */ jsx(SheetHeader, { title: mobileNavLabel ?? t("layout.appShell.navLabel") }),
74
+ /* @__PURE__ */ jsx(
75
+ SheetBody,
76
+ {
77
+ className: "app-mobile-nav-body px-[var(--app-shell-mobile-nav-inset)]",
78
+ onClick: handleDrawerClick,
79
+ children: drawerNav
80
+ }
81
+ )
82
+ ]
83
+ }
84
+ )
85
+ ] }),
86
+ resolvedTopbar
87
+ ] }),
88
+ /* @__PURE__ */ jsxs("main", { className: "app-main", "aria-label": t("layout.appShell.mainLabel"), tabIndex: 0, children: [
89
+ breadcrumb !== void 0 && /* @__PURE__ */ jsx("div", { className: "app-breadcrumb", children: breadcrumb }),
90
+ children
91
+ ] }),
92
+ footer !== void 0 && /* @__PURE__ */ jsx("footer", { className: "app-footer", "aria-label": t("layout.appShell.footerLabel"), children: footer })
93
+ ]
94
+ }
95
+ );
87
96
  }
88
97
  export {
89
98
  AppShell
@@ -0,0 +1,5 @@
1
+ import * as React from "react";
2
+ import type { AuthAccountSummaryProp } from "../../props/components/layout.prop.js";
3
+ export type { AuthAccountSummaryProp, AuthAccountSummaryProp as AuthAccountSummaryProps, } from "../../props/components/layout.prop.js";
4
+ /** Compact, token-owned signed-in identity row for hosted auth and device authorization. */
5
+ export declare const AuthAccountSummary: React.ForwardRefExoticComponent<AuthAccountSummaryProp & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,31 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { UserRound } from "lucide-react";
5
+ import { cn } from "../../lib/utils.js";
6
+ import { Avatar, AvatarFallback, AvatarImage } from "../data-display/avatar.js";
7
+ import { Button } from "../general/button.js";
8
+ const AuthAccountSummary = React.forwardRef(
9
+ ({ email, avatarSrc, avatarFallback, actionLabel, onAction, disabled, className }, ref) => /* @__PURE__ */ jsxs(
10
+ "div",
11
+ {
12
+ ref,
13
+ "data-slot": "auth-account-summary",
14
+ className: cn("ui-auth-account-summary", className),
15
+ children: [
16
+ /* @__PURE__ */ jsxs("div", { "data-slot": "auth-account-identity", className: "ui-auth-account-identity", children: [
17
+ /* @__PURE__ */ jsxs(Avatar, { className: "ui-auth-account-avatar", "aria-hidden": "true", children: [
18
+ avatarSrc ? /* @__PURE__ */ jsx(AvatarImage, { src: avatarSrc, alt: "" }) : null,
19
+ /* @__PURE__ */ jsx(AvatarFallback, { children: avatarFallback ?? /* @__PURE__ */ jsx(UserRound, { className: "ui-auth-account-fallback-icon", "aria-hidden": "true" }) })
20
+ ] }),
21
+ /* @__PURE__ */ jsx("span", { className: "ui-auth-account-email", dir: "auto", title: email, children: email })
22
+ ] }),
23
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", disabled, onClick: onAction, children: actionLabel })
24
+ ]
25
+ }
26
+ )
27
+ );
28
+ AuthAccountSummary.displayName = "AuthAccountSummary";
29
+ export {
30
+ AuthAccountSummary
31
+ };
@@ -9,8 +9,11 @@ export type { AuthShellProp, AuthShellProp as AuthShellProps, } from "../../prop
9
9
  * card) so `prefers-reduced-motion` is honoured in one place.
10
10
  *
11
11
  * `preset` names the flow MEASURE (card max-width + desktop/mobile page gutters) so a consumer
12
- * never overrides `--auth-shell-card-max-width` by hand: `"device-authorization"` = 380px card with
13
- * a 5px inline gutter at 390px; `"context-selection"` = 25rem card, edge-to-edge on mobile;
12
+ * never overrides page geometry by hand: `"login"` = SCR-001's stable identity/card/footer anchor
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;
14
17
  * `"account-recovery"` = the 432px SCR-008 panel measure shared by password recovery and the
15
18
  * sign-in MFA challenge, with a 15px inline gutter at 390px. It is orthogonal to `variant` —
16
19
  * combine `variant="canonical"` with any preset.
@@ -22,4 +22,4 @@ export type { CenteredShellProp, CenteredShellProp as CenteredShellProps, } from
22
22
  * footgun). Layout-only: delegate motion to `Reveal` (wrap a section) so `prefers-reduced-motion`
23
23
  * is honoured in one place.
24
24
  */
25
- export declare function CenteredShell({ topbar, footer, children, width, align, className, }: CenteredShellProp): import("react").JSX.Element;
25
+ export declare function CenteredShell({ topbar, footer, children, width, align, preset, className, }: CenteredShellProp): import("react").JSX.Element;
@@ -8,28 +8,37 @@ function CenteredShell({
8
8
  children,
9
9
  width = "md",
10
10
  align = "start",
11
+ preset = "default",
11
12
  className
12
13
  }) {
13
14
  const { t } = useTranslation();
14
- return /* @__PURE__ */ jsxs("div", { "data-slot": "centered-shell", className: cn("ui-centered-shell", className), children: [
15
- topbar !== void 0 && /* @__PURE__ */ jsx(
16
- "header",
17
- {
18
- className: "ui-centered-shell-bar ui-scale-fixed",
19
- "aria-label": t("layout.centeredShell.headerLabel"),
20
- children: topbar
21
- }
22
- ),
23
- /* @__PURE__ */ jsx("main", { className: "ui-centered-shell-main", "aria-label": t("layout.centeredShell.mainLabel"), children: /* @__PURE__ */ jsx("div", { className: "ui-centered-shell-column", "data-width": width, "data-align": align, children }) }),
24
- footer !== void 0 && /* @__PURE__ */ jsx(
25
- "footer",
26
- {
27
- className: "ui-centered-shell-footer",
28
- "aria-label": t("layout.centeredShell.footerLabel"),
29
- children: footer
30
- }
31
- )
32
- ] });
15
+ return /* @__PURE__ */ jsxs(
16
+ "div",
17
+ {
18
+ "data-slot": "centered-shell",
19
+ "data-preset": preset === "default" ? void 0 : preset,
20
+ className: cn("ui-centered-shell", className),
21
+ children: [
22
+ topbar !== void 0 && /* @__PURE__ */ jsx(
23
+ "header",
24
+ {
25
+ className: "ui-centered-shell-bar ui-scale-fixed",
26
+ "aria-label": t("layout.centeredShell.headerLabel"),
27
+ children: topbar
28
+ }
29
+ ),
30
+ /* @__PURE__ */ jsx("main", { className: "ui-centered-shell-main", "aria-label": t("layout.centeredShell.mainLabel"), children: /* @__PURE__ */ jsx("div", { className: "ui-centered-shell-column", "data-width": width, "data-align": align, children }) }),
31
+ footer !== void 0 && /* @__PURE__ */ jsx(
32
+ "footer",
33
+ {
34
+ className: "ui-centered-shell-footer",
35
+ "aria-label": t("layout.centeredShell.footerLabel"),
36
+ children: footer
37
+ }
38
+ )
39
+ ]
40
+ }
41
+ );
33
42
  }
34
43
  export {
35
44
  CenteredShell
@@ -0,0 +1,35 @@
1
+ import * as React from "react";
2
+ import type { ErrorSurfaceProp } from "../../props/components/layout.prop.js";
3
+ export type { ErrorSurfaceMaintenanceProp, ErrorSurfaceProp, ErrorSurfaceProp as ErrorSurfaceProps, } from "../../props/components/layout.prop.js";
4
+ export type { ErrorSurfaceModeProp, ErrorSurfaceStatusProp } from "../../props/vocabulary/index.js";
5
+ /**
6
+ * ErrorSurface — the package-owned semantic exception surface for **403 · 404 · 500 · 503**
7
+ * (gh#221, gh#251).
8
+ *
9
+ * It exists because the alternative — every app composing `EmptyState` + `Flex` + `Text` inside a
10
+ * hand-picked shell — is unimportable: a consumer cannot `import` a docs page, so the four
11
+ * exception screens drifted per application (a generic `Card` in an `AuthShell`, a local
12
+ * `.canonical-auth-card`, a bespoke `min-h-dvh` class). What the surface owns is the CONTRACT the
13
+ * composition kept losing:
14
+ *
15
+ * - **`mode` is the shell contract.** `application` (403/404) renders the surface as the BODY you
16
+ * place inside the `AppShell` the route already provides, so the sidebar, topbar and breadcrumb
17
+ * are PRESERVED — it never reconstructs navigation chrome, because nav data and the user menu are
18
+ * consumer-owned and a component cannot manufacture them. `system` (500/503) owns the page and
19
+ * renders `CenteredShell align="center"`, so the viewport-centred geometry at 1440 / 1024 / 390
20
+ * is package-owned and the consumer writes no `min-h-dvh`, no flex-centring class, no media query.
21
+ * - **Exactly one recovery action**, structurally (see `singleAction`).
22
+ * - **Semantic metadata slots** instead of free-form paragraphs: `requestId` (mono/tabular so a
23
+ * support id is read out accurately), `permission` and `organization` (which of the two a 403 is
24
+ * actually about), and `maintenance` (ISO-8601 + IANA formatted through `Intl.DateTimeFormat`,
25
+ * with an optional labelled `Progress` meter). Each is a `<dt>`/`<dd>` pair, so the label↔value
26
+ * relationship survives for a screen reader instead of being a colon in a sentence.
27
+ * - **Valid heading order by default** — `h2` under a `PageContainer` `h1` in `application` mode,
28
+ * `h1` on a `system` page — and the status code announced as "HTTP status 403", not "403".
29
+ *
30
+ * PRODUCT COPY stays consumer-owned: `title` / `description` / `action` come from the app's own
31
+ * `t()`. The surface localizes only its OWN metadata labels. It holds no state, runs no effect and
32
+ * opens no portal, so it renders fully server-side — an exception page must be readable before
33
+ * hydration.
34
+ */
35
+ export declare const ErrorSurface: React.ForwardRefExoticComponent<ErrorSurfaceProp & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,155 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { SearchX, ServerCrash, ShieldAlert, Wrench } from "lucide-react";
5
+ import { useTranslation } from "../../i18n/use-translation.js";
6
+ import { cn } from "../../lib/utils.js";
7
+ import { EmptyState } from "../data-display/empty-state.js";
8
+ import { Progress } from "../data-display/progress.js";
9
+ import { Text } from "../general/typography.js";
10
+ import { CenteredShell } from "./centered-shell.js";
11
+ const STATUS_META = {
12
+ 403: { icon: ShieldAlert, tone: "warning" },
13
+ 404: { icon: SearchX, tone: "muted" },
14
+ 500: { icon: ServerCrash, tone: "destructive" },
15
+ 503: { icon: Wrench, tone: "warning" }
16
+ };
17
+ function singleAction(action) {
18
+ const flattened = React.Children.toArray(action).flatMap(
19
+ (child) => React.isValidElement(child) && child.type === React.Fragment ? React.Children.toArray(child.props.children) : [child]
20
+ );
21
+ if (flattened.length > 1 && process.env.NODE_ENV !== "production") {
22
+ console.error(
23
+ "[@godxjp/ui] ErrorSurface: `action` takes EXACTLY ONE recovery action. The extra elements were dropped. Put support contact or secondary guidance in `description`."
24
+ );
25
+ }
26
+ return flattened[0] ?? null;
27
+ }
28
+ function formatMaintenanceWindow(maintenance, locale) {
29
+ const options = {
30
+ dateStyle: "medium",
31
+ timeStyle: "short",
32
+ ...maintenance.timeZone ? { timeZone: maintenance.timeZone } : {}
33
+ };
34
+ const start = new Date(maintenance.start);
35
+ if (Number.isNaN(start.getTime())) return maintenance.start;
36
+ const formatter = new Intl.DateTimeFormat(locale, options);
37
+ if (maintenance.end === void 0) return formatter.format(start);
38
+ const end = new Date(maintenance.end);
39
+ if (Number.isNaN(end.getTime())) return formatter.format(start);
40
+ return formatter.formatRange(start, end);
41
+ }
42
+ const ErrorSurface = React.forwardRef(
43
+ function ErrorSurface2({
44
+ mode,
45
+ status,
46
+ title,
47
+ description,
48
+ action,
49
+ icon,
50
+ tone,
51
+ titleLevel,
52
+ requestId,
53
+ permission,
54
+ organization,
55
+ maintenance,
56
+ brand,
57
+ footer,
58
+ width = "sm",
59
+ id,
60
+ className
61
+ }, ref) {
62
+ const { t, locale } = useTranslation();
63
+ const meta = STATUS_META[status];
64
+ const resolvedIcon = icon ?? meta.icon;
65
+ const resolvedTone = tone ?? meta.tone;
66
+ const resolvedTitleLevel = titleLevel ?? (mode === "system" ? 1 : 2);
67
+ const maintenanceWindow = maintenance === void 0 ? void 0 : formatMaintenanceWindow(maintenance, locale);
68
+ const maintenanceProgress = maintenance?.progress;
69
+ const hasProgress = typeof maintenanceProgress === "number" && Number.isFinite(maintenanceProgress);
70
+ const progressLabel = hasProgress ? t("layout.errorSurface.maintenanceProgress", {
71
+ percent: new Intl.NumberFormat(locale, {
72
+ style: "percent",
73
+ maximumFractionDigits: 0
74
+ }).format(Math.max(0, Math.min(100, maintenanceProgress)) / 100)
75
+ }) : void 0;
76
+ const hasMetadata = requestId !== void 0 || permission !== void 0 || organization !== void 0 || maintenanceWindow !== void 0;
77
+ const surface = /* @__PURE__ */ jsxs(
78
+ "div",
79
+ {
80
+ ref,
81
+ id,
82
+ "data-slot": "error-surface",
83
+ "data-mode": mode,
84
+ "data-status": status,
85
+ className: cn("ui-error-surface", className),
86
+ children: [
87
+ brand !== void 0 && mode === "system" && /* @__PURE__ */ jsx("div", { className: "ui-error-surface-brand", children: brand }),
88
+ /* @__PURE__ */ jsxs(
89
+ Text,
90
+ {
91
+ as: "p",
92
+ size: "sm",
93
+ tone: "muted",
94
+ weight: "medium",
95
+ mono: true,
96
+ tabular: true,
97
+ className: "ui-error-surface-status",
98
+ children: [
99
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: status }),
100
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("layout.errorSurface.statusLabel", { status: String(status) }) })
101
+ ]
102
+ }
103
+ ),
104
+ /* @__PURE__ */ jsx(
105
+ EmptyState,
106
+ {
107
+ icon: resolvedIcon,
108
+ tone: resolvedTone,
109
+ titleLevel: resolvedTitleLevel,
110
+ title,
111
+ description,
112
+ action: singleAction(action),
113
+ className: "ui-error-surface-body"
114
+ }
115
+ ),
116
+ hasMetadata && /* A description list, not a sentence: each metadata row keeps a machine-readable
117
+ * label↔value pair, so "Request ID / 01J9Z…" is navigable instead of being one run-on
118
+ * paragraph. `<div>` wrappers around dt/dd are valid HTML5 and give each row its box. */
119
+ /* @__PURE__ */ jsxs("dl", { className: "ui-error-surface-meta", children: [
120
+ requestId !== void 0 && /* @__PURE__ */ jsxs("div", { className: "ui-error-surface-meta-row", children: [
121
+ /* @__PURE__ */ jsx(Text, { as: "dt", size: "xs", tone: "muted", children: t("layout.errorSurface.requestId") }),
122
+ /* @__PURE__ */ jsx(Text, { as: "dd", size: "xs", tone: "muted", mono: true, tabular: true, children: requestId })
123
+ ] }),
124
+ permission !== void 0 && /* @__PURE__ */ jsxs("div", { className: "ui-error-surface-meta-row", children: [
125
+ /* @__PURE__ */ jsx(Text, { as: "dt", size: "xs", tone: "muted", children: t("layout.errorSurface.permission") }),
126
+ /* @__PURE__ */ jsx(Text, { as: "dd", size: "xs", tone: "muted", mono: true, children: permission })
127
+ ] }),
128
+ organization !== void 0 && /* @__PURE__ */ jsxs("div", { className: "ui-error-surface-meta-row", children: [
129
+ /* @__PURE__ */ jsx(Text, { as: "dt", size: "xs", tone: "muted", children: t("layout.errorSurface.organization") }),
130
+ /* @__PURE__ */ jsx(Text, { as: "dd", size: "xs", tone: "muted", children: organization })
131
+ ] }),
132
+ maintenanceWindow !== void 0 && maintenance !== void 0 && /* @__PURE__ */ jsxs("div", { className: "ui-error-surface-meta-row", children: [
133
+ /* @__PURE__ */ jsx(Text, { as: "dt", size: "xs", tone: "muted", children: t("layout.errorSurface.maintenanceWindow") }),
134
+ /* @__PURE__ */ jsx(Text, { as: "dd", size: "xs", tone: "muted", children: /* @__PURE__ */ jsx("time", { dateTime: maintenance.start, children: maintenanceWindow }) })
135
+ ] })
136
+ ] }),
137
+ hasProgress && progressLabel !== void 0 && /* @__PURE__ */ jsx("div", { className: "ui-error-surface-progress", children: /* @__PURE__ */ jsx(
138
+ Progress,
139
+ {
140
+ value: Math.max(0, Math.min(100, maintenanceProgress)),
141
+ label: progressLabel,
142
+ tone: "warning"
143
+ }
144
+ ) })
145
+ ]
146
+ }
147
+ );
148
+ if (mode === "application") return surface;
149
+ return /* @__PURE__ */ jsx(CenteredShell, { align: "center", width, ...footer !== void 0 ? { footer } : {}, children: surface });
150
+ }
151
+ );
152
+ ErrorSurface.displayName = "ErrorSurface";
153
+ export {
154
+ ErrorSurface
155
+ };
@@ -1,3 +1,3 @@
1
1
  import type { FlexProp } from "../../props/components/layout.prop.js";
2
2
  export type { FlexAlignProp, FlexDirectionProp, FlexJustifyProp, FlexProp, FlexProp as FlexProps, } from "../../props/components/layout.prop.js";
3
- export declare function Flex({ direction, gap, align, justify, wrap, className, children, ...props }: FlexProp): import("react").JSX.Element;
3
+ export declare function Flex({ direction, gap, align, justify, wrap, hideBelow, hideFrom, className, children, ...props }: FlexProp): import("react").JSX.Element;
@@ -7,6 +7,8 @@ function Flex({
7
7
  align,
8
8
  justify,
9
9
  wrap = false,
10
+ hideBelow,
11
+ hideFrom,
10
12
  className,
11
13
  children,
12
14
  ...props
@@ -18,6 +20,8 @@ function Flex({
18
20
  "data-align": align,
19
21
  "data-justify": justify,
20
22
  "data-wrap": wrap ? "true" : void 0,
23
+ "data-hide-below": hideBelow,
24
+ "data-hide-from": hideFrom,
21
25
  className: cn("ui-flex", flexGapClass[gap], className),
22
26
  ...props,
23
27
  children
@@ -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";
@@ -14,6 +16,8 @@ export { AuthDivider } from "./auth-divider.js";
14
16
  export type { AuthDividerProp, AuthDividerProps } from "./auth-divider.js";
15
17
  export { AuthIdentity } from "./auth-identity.js";
16
18
  export type { AuthIdentityProp, AuthIdentityProps } from "./auth-identity.js";
19
+ export { AuthAccountSummary } from "./auth-account-summary.js";
20
+ export type { AuthAccountSummaryProp, AuthAccountSummaryProps } from "./auth-account-summary.js";
17
21
  export { AuthFooter } from "./auth-footer.js";
18
22
  export type { AuthFooterProp, AuthFooterProps } from "./auth-footer.js";
19
23
  export { AuthStack } from "./auth-stack.js";
@@ -21,6 +25,9 @@ export type { AuthStackProps } from "./auth-stack.js";
21
25
  export { CenteredShell } from "./centered-shell.js";
22
26
  export type { CenteredShellProp, CenteredShellProps } from "./centered-shell.js";
23
27
  export type { CenteredShellWidthProp, CenteredShellAlignProp } from "../../props/vocabulary/index.js";
28
+ export { ErrorSurface } from "./error-surface.js";
29
+ export type { ErrorSurfaceMaintenanceProp, ErrorSurfaceProp, ErrorSurfaceProps, } from "./error-surface.js";
30
+ export type { ErrorSurfaceModeProp, ErrorSurfaceStatusProp } from "../../props/vocabulary/index.js";
24
31
  export { Breadcrumb } from "./breadcrumb.js";
25
32
  export type { BreadcrumbProps } from "./breadcrumb.js";
26
33
  export { createSidebarLink, Sidebar, SidebarHeader, SidebarItem, SidebarSection } from "./sidebar.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";
@@ -6,9 +7,11 @@ import { OrgSwitcher } from "./org-switcher.js";
6
7
  import { AuthShell } from "./auth-shell.js";
7
8
  import { AuthDivider } from "./auth-divider.js";
8
9
  import { AuthIdentity } from "./auth-identity.js";
10
+ import { AuthAccountSummary } from "./auth-account-summary.js";
9
11
  import { AuthFooter } from "./auth-footer.js";
10
12
  import { AuthStack } from "./auth-stack.js";
11
13
  import { CenteredShell } from "./centered-shell.js";
14
+ import { ErrorSurface } from "./error-surface.js";
12
15
  import { Breadcrumb } from "./breadcrumb.js";
13
16
  import { createSidebarLink, Sidebar, SidebarHeader, SidebarItem, SidebarSection } from "./sidebar.js";
14
17
  import { Topbar } from "./topbar.js";
@@ -21,6 +24,7 @@ import { LegalDocumentShell } from "./legal-document-shell.js";
21
24
  export {
22
25
  AppShell,
23
26
  AspectRatio,
27
+ AuthAccountSummary,
24
28
  AuthDivider,
25
29
  AuthFooter,
26
30
  AuthIdentity,
@@ -28,11 +32,13 @@ export {
28
32
  AuthStack,
29
33
  Breadcrumb,
30
34
  CenteredShell,
35
+ ErrorSurface,
31
36
  Flex,
32
37
  LegalDocumentShell,
33
38
  MasterDetail,
34
39
  OrgSwitcher,
35
40
  PageContainer,
41
+ PageHeader,
36
42
  ResizableHandle,
37
43
  ResizablePanel,
38
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, headerLayout, 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,
@@ -44,7 +45,9 @@ function PageContainerRoot({
44
45
  linkComponent: LinkComponent = "a",
45
46
  density,
46
47
  variant = "default",
48
+ preset = "default",
47
49
  headerLayout = "stack",
50
+ measure = "default",
48
51
  stickyFooter = false,
49
52
  footerReveal = "always",
50
53
  fill = false,
@@ -53,10 +56,11 @@ function PageContainerRoot({
53
56
  }) {
54
57
  const reveal = stickyFooter && footer != null && footerReveal === "onScroll";
55
58
  const { headerRef, revealed } = useFooterReveal(reveal);
56
- const { t } = useTranslation();
57
59
  return /* @__PURE__ */ jsxs(
58
60
  "div",
59
61
  {
62
+ "data-preset": preset,
63
+ "data-measure": measure,
60
64
  "data-revealed": revealed ? "true" : void 0,
61
65
  className: cn(
62
66
  "ui-page-container",
@@ -70,44 +74,22 @@ function PageContainerRoot({
70
74
  className
71
75
  ),
72
76
  children: [
73
- /* @__PURE__ */ jsxs("header", { ref: headerRef, className: "ui-page-header", "data-layout": headerLayout, children: [
74
- breadcrumb && breadcrumb.length > 0 && /* @__PURE__ */ jsx(
75
- "nav",
76
- {
77
- "aria-label": breadcrumbLabel ?? breadcrumbAriaLabel ?? t("navigation.breadcrumb.ariaLabel"),
78
- className: "ui-breadcrumb",
79
- children: /* @__PURE__ */ jsx("ol", { className: "ui-breadcrumb-list", children: breadcrumb.map((item, i) => {
80
- const isLast = i === breadcrumb.length - 1;
81
- return /* @__PURE__ */ jsxs("li", { className: "ui-inline-xs", children: [
82
- item.to && !isLast ? /* @__PURE__ */ jsx(
83
- LinkComponent,
84
- {
85
- href: item.to,
86
- to: item.to,
87
- className: "hover:text-foreground hover:underline",
88
- children: item.label
89
- }
90
- ) : /* @__PURE__ */ jsx(
91
- "span",
92
- {
93
- className: isLast ? "text-foreground" : "",
94
- "aria-current": isLast ? "page" : void 0,
95
- children: item.label
96
- }
97
- ),
98
- !isLast && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3", "aria-hidden": "true" })
99
- ] }, i);
100
- }) })
101
- }
102
- ),
103
- /* @__PURE__ */ jsxs("div", { className: "ui-page-header-row", children: [
104
- /* @__PURE__ */ jsxs("div", { className: "ui-page-header-heading min-w-0", children: [
105
- /* @__PURE__ */ jsx("h1", { className: "ui-page-title", children: title }),
106
- subtitle && /* @__PURE__ */ jsx("p", { className: "ui-page-subtitle", children: subtitle })
107
- ] }),
108
- extra && /* @__PURE__ */ jsx("div", { className: "ui-page-header-extra", children: extra })
109
- ] })
110
- ] }),
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
+ ),
111
93
  children != null && /* @__PURE__ */ jsx("div", { className: "ui-page-body", children }),
112
94
  footer && /* @__PURE__ */ jsx("footer", { className: "ui-page-footer", children: footer })
113
95
  ]