@godxjp/ui 18.4.0 → 18.6.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 (159) hide show
  1. package/README.md +16 -1
  2. package/dist/app/theme-axes.d.ts +11 -3
  3. package/dist/app/theme-axes.js +2 -1
  4. package/dist/components/charts/chart-frame.d.ts +17 -3
  5. package/dist/components/charts/chart-frame.js +7 -1
  6. package/dist/components/charts/chart-summary.d.ts +21 -0
  7. package/dist/components/charts/chart-summary.js +32 -5
  8. package/dist/components/charts/compact-bar-trend.d.ts +30 -0
  9. package/dist/components/charts/compact-bar-trend.js +74 -0
  10. package/dist/components/charts/index.d.ts +6 -0
  11. package/dist/components/charts/index.js +2 -0
  12. package/dist/components/data-display/badge.d.ts +8 -0
  13. package/dist/components/data-display/badge.js +7 -1
  14. package/dist/components/data-display/data-table.d.ts +40 -4
  15. package/dist/components/data-display/data-table.js +69 -2
  16. package/dist/components/data-display/empty-state.js +1 -1
  17. package/dist/components/data-display/index.d.ts +4 -2
  18. package/dist/components/data-display/index.js +10 -1
  19. package/dist/components/data-display/list-row.d.ts +41 -4
  20. package/dist/components/data-display/list-row.js +23 -3
  21. package/dist/components/data-display/qr-code.d.ts +1 -1
  22. package/dist/components/data-display/service-launcher-card.d.ts +40 -0
  23. package/dist/components/data-display/service-launcher-card.js +97 -0
  24. package/dist/components/data-display/table.d.ts +47 -2
  25. package/dist/components/data-display/table.js +29 -4
  26. package/dist/components/data-entry/command-palette.d.ts +37 -0
  27. package/dist/components/data-entry/command-palette.js +125 -0
  28. package/dist/components/data-entry/command.d.ts +10 -10
  29. package/dist/components/data-entry/index.d.ts +3 -0
  30. package/dist/components/data-entry/index.js +2 -0
  31. package/dist/components/data-entry/input-otp.d.ts +1 -0
  32. package/dist/components/feedback/dialog.d.ts +16 -1
  33. package/dist/components/feedback/dialog.js +46 -33
  34. package/dist/components/feedback/index.d.ts +5 -2
  35. package/dist/components/feedback/index.js +8 -2
  36. package/dist/components/feedback/sheet.d.ts +26 -4
  37. package/dist/components/feedback/sheet.js +54 -7
  38. package/dist/components/feedback/two-factor-setup.d.ts +31 -0
  39. package/dist/components/feedback/two-factor-setup.js +97 -0
  40. package/dist/components/general/index.d.ts +1 -1
  41. package/dist/components/general/logo.d.ts +49 -9
  42. package/dist/components/general/logo.js +67 -14
  43. package/dist/components/layout/app-shell.d.ts +1 -1
  44. package/dist/components/layout/app-shell.js +55 -30
  45. package/dist/components/layout/auth-account-summary.d.ts +5 -0
  46. package/dist/components/layout/auth-account-summary.js +31 -0
  47. package/dist/components/layout/auth-divider.d.ts +7 -0
  48. package/dist/components/layout/auth-divider.js +21 -0
  49. package/dist/components/layout/auth-footer.d.ts +5 -0
  50. package/dist/components/layout/auth-footer.js +24 -0
  51. package/dist/components/layout/auth-identity.d.ts +5 -0
  52. package/dist/components/layout/auth-identity.js +17 -0
  53. package/dist/components/layout/auth-shell.d.ts +9 -1
  54. package/dist/components/layout/auth-shell.js +25 -6
  55. package/dist/components/layout/auth-stack.d.ts +4 -0
  56. package/dist/components/layout/auth-stack.js +8 -0
  57. package/dist/components/layout/centered-shell.d.ts +5 -2
  58. package/dist/components/layout/centered-shell.js +29 -19
  59. package/dist/components/layout/error-surface.d.ts +35 -0
  60. package/dist/components/layout/error-surface.js +155 -0
  61. package/dist/components/layout/flex.d.ts +1 -1
  62. package/dist/components/layout/flex.js +4 -0
  63. package/dist/components/layout/index.d.ts +23 -3
  64. package/dist/components/layout/index.js +21 -2
  65. package/dist/components/layout/legal-document-shell.d.ts +34 -0
  66. package/dist/components/layout/legal-document-shell.js +186 -0
  67. package/dist/components/layout/master-detail.d.ts +24 -0
  68. package/dist/components/layout/master-detail.js +48 -0
  69. package/dist/components/layout/org-switcher.d.ts +9 -0
  70. package/dist/components/layout/org-switcher.js +218 -0
  71. package/dist/components/layout/page-container.d.ts +1 -1
  72. package/dist/components/layout/page-container.js +7 -2
  73. package/dist/components/layout/sidebar-link.d.ts +43 -0
  74. package/dist/components/layout/sidebar-link.js +13 -0
  75. package/dist/components/layout/sidebar.d.ts +42 -5
  76. package/dist/components/layout/sidebar.js +170 -85
  77. package/dist/components/navigation/app-setting-picker.js +17 -6
  78. package/dist/components/navigation/filter-bar.d.ts +12 -3
  79. package/dist/components/navigation/filter-bar.js +8 -2
  80. package/dist/components/navigation/index.d.ts +2 -2
  81. package/dist/components/navigation/index.js +3 -1
  82. package/dist/components/navigation/steps.js +38 -19
  83. package/dist/components/navigation/tabs-scroll.d.ts +70 -0
  84. package/dist/components/navigation/tabs-scroll.js +76 -0
  85. package/dist/components/navigation/tabs.js +47 -23
  86. package/dist/components/ui/avatar.d.ts +2 -1
  87. package/dist/components/ui/avatar.js +2 -1
  88. package/dist/components/ui/input-otp.d.ts +4 -1
  89. package/dist/components/ui/input-otp.js +2 -1
  90. package/dist/components/ui/toggle-group.d.ts +7 -4
  91. package/dist/components/ui/toggle-group.js +32 -21
  92. package/dist/email/brand-mark.d.ts +108 -0
  93. package/dist/email/brand-mark.js +88 -0
  94. package/dist/email/color.d.ts +53 -0
  95. package/dist/email/color.js +37 -0
  96. package/dist/email/geometry.d.ts +120 -0
  97. package/dist/email/geometry.js +90 -0
  98. package/dist/email/index.d.ts +83 -0
  99. package/dist/email/index.js +82 -0
  100. package/dist/email/inline-style.d.ts +27 -0
  101. package/dist/email/inline-style.js +26 -0
  102. package/dist/email/tokens.generated.d.ts +167 -0
  103. package/dist/email/tokens.generated.js +162 -0
  104. package/dist/email/urgency.d.ts +22 -0
  105. package/dist/email/urgency.js +14 -0
  106. package/dist/i18n/messages/en.json +36 -3
  107. package/dist/i18n/messages/ja.json +36 -3
  108. package/dist/i18n/messages/vi.json +36 -3
  109. package/dist/inertia/index.d.ts +50 -0
  110. package/dist/inertia/index.js +5 -0
  111. package/dist/props/components/app.prop.d.ts +11 -1
  112. package/dist/props/components/charts.prop.d.ts +40 -1
  113. package/dist/props/components/data-display.prop.d.ts +75 -1
  114. package/dist/props/components/data-entry.prop.d.ts +6 -0
  115. package/dist/props/components/feedback.prop.d.ts +10 -0
  116. package/dist/props/components/index.d.ts +3 -3
  117. package/dist/props/components/layout.prop.d.ts +560 -10
  118. package/dist/props/components/layout.prop.js +1 -0
  119. package/dist/props/components/navigation.prop.d.ts +24 -2
  120. package/dist/props/registry.d.ts +433 -8
  121. package/dist/props/registry.js +527 -9
  122. package/dist/props/vocabulary/data.prop.d.ts +27 -0
  123. package/dist/props/vocabulary/index.d.ts +3 -3
  124. package/dist/props/vocabulary/interaction.prop.d.ts +10 -1
  125. package/dist/props/vocabulary/layout.prop.d.ts +49 -0
  126. package/dist/styles/base.css +8 -2
  127. package/dist/styles/card-layout.css +161 -7
  128. package/dist/styles/chart-layout.css +97 -0
  129. package/dist/styles/control.css +76 -2
  130. package/dist/styles/data-display-layout.css +78 -6
  131. package/dist/styles/dialog-layout.css +105 -4
  132. package/dist/styles/fonts.css +15 -4
  133. package/dist/styles/index.css +11 -3
  134. package/dist/styles/layout.css +466 -1
  135. package/dist/styles/logo-layout.css +95 -0
  136. package/dist/styles/navigation-layout.css +152 -0
  137. package/dist/styles/shell-layout.css +790 -40
  138. package/dist/styles/table-layout.css +164 -1
  139. package/dist/theme/dxs.canonical.css +77 -0
  140. package/dist/tokens/axes.css +40 -0
  141. package/dist/tokens/base.css +6 -0
  142. package/dist/tokens/components/card.css +48 -0
  143. package/dist/tokens/components/chart.css +47 -0
  144. package/dist/tokens/components/control.css +16 -0
  145. package/dist/tokens/components/data-display.css +13 -0
  146. package/dist/tokens/components/email.css +93 -0
  147. package/dist/tokens/components/error-surface.css +36 -0
  148. package/dist/tokens/components/feedback.css +17 -0
  149. package/dist/tokens/components/legal-document.css +67 -0
  150. package/dist/tokens/components/list-row.css +43 -2
  151. package/dist/tokens/components/logo.css +74 -0
  152. package/dist/tokens/components/navigation.css +28 -0
  153. package/dist/tokens/components/sheet.css +19 -0
  154. package/dist/tokens/components/shell.css +210 -0
  155. package/dist/tokens/components/sidebar.css +33 -0
  156. package/dist/tokens/components/table.css +45 -0
  157. package/dist/tokens/foundation.css +31 -2
  158. package/dist/tokens/semantic/layout.css +69 -2
  159. package/package.json +22 -5
@@ -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
@@ -5,20 +5,40 @@ export type { FlexAlignProp, FlexDirectionProp, FlexJustifyProp, FlexProp, FlexP
5
5
  export { ResizablePanel, ResizablePanelGroup, ResizableHandle } from "./resizable.js";
6
6
  export { AppShell } from "./app-shell.js";
7
7
  export type { AppShellProps } from "./app-shell.js";
8
+ export { OrgSwitcher } from "./org-switcher.js";
9
+ export type { OrgSwitcherLabels, OrgSwitcherOrganization, OrgSwitcherProp, OrgSwitcherProps, } from "./org-switcher.js";
8
10
  export { AuthShell } from "./auth-shell.js";
9
11
  export type { AuthShellProp, AuthShellProps } from "./auth-shell.js";
12
+ export type { AuthShellPresetProp } from "../../props/vocabulary/index.js";
13
+ export { AuthDivider } from "./auth-divider.js";
14
+ export type { AuthDividerProp, AuthDividerProps } from "./auth-divider.js";
15
+ export { AuthIdentity } from "./auth-identity.js";
16
+ export type { AuthIdentityProp, AuthIdentityProps } from "./auth-identity.js";
17
+ export { AuthAccountSummary } from "./auth-account-summary.js";
18
+ export type { AuthAccountSummaryProp, AuthAccountSummaryProps } from "./auth-account-summary.js";
19
+ export { AuthFooter } from "./auth-footer.js";
20
+ export type { AuthFooterProp, AuthFooterProps } from "./auth-footer.js";
21
+ export { AuthStack } from "./auth-stack.js";
22
+ export type { AuthStackProps } from "./auth-stack.js";
10
23
  export { CenteredShell } from "./centered-shell.js";
11
24
  export type { CenteredShellProp, CenteredShellProps } from "./centered-shell.js";
12
- export type { CenteredShellWidthProp } from "../../props/vocabulary/index.js";
25
+ export type { CenteredShellWidthProp, CenteredShellAlignProp } from "../../props/vocabulary/index.js";
26
+ export { ErrorSurface } from "./error-surface.js";
27
+ export type { ErrorSurfaceMaintenanceProp, ErrorSurfaceProp, ErrorSurfaceProps, } from "./error-surface.js";
28
+ export type { ErrorSurfaceModeProp, ErrorSurfaceStatusProp } from "../../props/vocabulary/index.js";
13
29
  export { Breadcrumb } from "./breadcrumb.js";
14
30
  export type { BreadcrumbProps } from "./breadcrumb.js";
15
- export { Sidebar, SidebarHeader, SidebarItem, SidebarSection } from "./sidebar.js";
16
- export type { SidebarItemData, SidebarProductProp as SidebarProduct, SidebarProp, SidebarProp as SidebarProps, SidebarSectionProp, } from "../../props/components/layout.prop.js";
31
+ export { createSidebarLink, Sidebar, SidebarHeader, SidebarItem, SidebarSection } from "./sidebar.js";
32
+ export type { SidebarItemData, SidebarLinkComponentProp, SidebarLinkProp, SidebarProductProp as SidebarProduct, SidebarProp, SidebarProp as SidebarProps, SidebarRenderItemProp, SidebarSectionProp, } from "../../props/components/layout.prop.js";
17
33
  export { Topbar } from "./topbar.js";
18
34
  export type { TopbarProp, TopbarProps } from "./topbar.js";
19
35
  export { ResponsiveGrid } from "./responsive-grid.js";
20
36
  export type { ResponsiveGridProps } from "./responsive-grid.js";
37
+ export { MasterDetail } from "./master-detail.js";
38
+ export type { MasterDetailProps } from "./master-detail.js";
21
39
  export { SplitPane } from "./split-pane.js";
22
40
  export type { SplitPaneProps } from "./split-pane.js";
23
41
  export { Separator } from "./separator.js";
24
42
  export { AspectRatio } from "./aspect-ratio.js";
43
+ export { LegalDocumentShell } from "./legal-document-shell.js";
44
+ export type { LegalDocumentSectionProp, LegalDocumentShellProp, LegalDocumentShellProps, } from "./legal-document-shell.js";
@@ -2,22 +2,40 @@ import { PageContainer } from "./page-container.js";
2
2
  import { Flex } from "./flex.js";
3
3
  import { ResizablePanel, ResizablePanelGroup, ResizableHandle } from "./resizable.js";
4
4
  import { AppShell } from "./app-shell.js";
5
+ import { OrgSwitcher } from "./org-switcher.js";
5
6
  import { AuthShell } from "./auth-shell.js";
7
+ import { AuthDivider } from "./auth-divider.js";
8
+ import { AuthIdentity } from "./auth-identity.js";
9
+ import { AuthAccountSummary } from "./auth-account-summary.js";
10
+ import { AuthFooter } from "./auth-footer.js";
11
+ import { AuthStack } from "./auth-stack.js";
6
12
  import { CenteredShell } from "./centered-shell.js";
13
+ import { ErrorSurface } from "./error-surface.js";
7
14
  import { Breadcrumb } from "./breadcrumb.js";
8
- import { Sidebar, SidebarHeader, SidebarItem, SidebarSection } from "./sidebar.js";
15
+ import { createSidebarLink, Sidebar, SidebarHeader, SidebarItem, SidebarSection } from "./sidebar.js";
9
16
  import { Topbar } from "./topbar.js";
10
17
  import { ResponsiveGrid } from "./responsive-grid.js";
18
+ import { MasterDetail } from "./master-detail.js";
11
19
  import { SplitPane } from "./split-pane.js";
12
20
  import { Separator } from "./separator.js";
13
21
  import { AspectRatio } from "./aspect-ratio.js";
22
+ import { LegalDocumentShell } from "./legal-document-shell.js";
14
23
  export {
15
24
  AppShell,
16
25
  AspectRatio,
26
+ AuthAccountSummary,
27
+ AuthDivider,
28
+ AuthFooter,
29
+ AuthIdentity,
17
30
  AuthShell,
31
+ AuthStack,
18
32
  Breadcrumb,
19
33
  CenteredShell,
34
+ ErrorSurface,
20
35
  Flex,
36
+ LegalDocumentShell,
37
+ MasterDetail,
38
+ OrgSwitcher,
21
39
  PageContainer,
22
40
  ResizableHandle,
23
41
  ResizablePanel,
@@ -29,5 +47,6 @@ export {
29
47
  SidebarItem,
30
48
  SidebarSection,
31
49
  SplitPane,
32
- Topbar
50
+ Topbar,
51
+ createSidebarLink
33
52
  };
@@ -0,0 +1,34 @@
1
+ import * as React from "react";
2
+ import type { LegalDocumentShellProp } from "../../props/components/layout.prop.js";
3
+ export type { LegalDocumentSectionProp, LegalDocumentShellProp, LegalDocumentShellProp as LegalDocumentShellProps, } from "../../props/components/layout.prop.js";
4
+ /**
5
+ * LegalDocumentShell — the long-form **legal / policy document** surface: terms of service, privacy
6
+ * policy, DPA, cookie policy, SLA, EULA, security policy.
7
+ *
8
+ * It exists because a legal page is NOT expressible as `CenteredShell` + `SplitPane` + tokens: the
9
+ * geometry is the easy half, and the half that keeps getting hand-rolled per app is the behaviour —
10
+ *
11
+ * - **scroll spy**: an `IntersectionObserver` marks the section at the reading line active, so the
12
+ * contents list tracks the reader (`aria-current="location"`, never colour-only);
13
+ * - **hash deep links**: `?#section-id` on load selects that section; activating a contents anchor
14
+ * rewrites the hash (`history.replaceState`, so the Back button is never hijacked);
15
+ * - **scroll offset**: `scroll-margin-block-start` from `--legal-document-scroll-offset`, so a jump
16
+ * lands below sticky chrome with no offset arithmetic;
17
+ * - **focus handoff**: the target `<section tabIndex={-1}>` takes focus (`preventScroll`), so a
18
+ * keyboard/screen-reader user actually arrives IN the section, with a visible ring;
19
+ * - **reduced motion**: the smooth scroll degrades to an instant jump under
20
+ * `prefers-reduced-motion: reduce`.
21
+ *
22
+ * Semantics: `<article>` labelled by the `<h1>` · a NAMED `<nav>` for the contents · REAL
23
+ * `<a href="#…">` anchors (middle-clickable, deep-linkable, work before JS boots) · one `<section>`
24
+ * per entry labelled by its `<h2>`.
25
+ *
26
+ * Layout: the shell owns its query container, so the one-column ⇄ two-column decision comes from
27
+ * its OWN width (the SplitPane precedent, gh#165). At ≥56rem the contents become a sticky rail
28
+ * beside a measure-capped column; below that (the 390×844 case) the shell is a single column and
29
+ * the contents are a static compact block between the header and the first section — never pinned,
30
+ * so a phone loses no vertical space.
31
+ *
32
+ * ALL legal text stays consumer-owned: the shell only receives `sections` and slots.
33
+ */
34
+ export declare const LegalDocumentShell: React.ForwardRefExoticComponent<LegalDocumentShellProp & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,186 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useTranslation } from "../../i18n/use-translation.js";
5
+ import { useControlledLatch } from "../../lib/hooks.js";
6
+ import { cn } from "../../lib/utils.js";
7
+ import { Heading } from "../general/typography.js";
8
+ const SPY_ROOT_MARGIN = "-10% 0px -75% 0px";
9
+ const ISO_CALENDAR_DATE = /^\d{4}-\d{2}-\d{2}$/;
10
+ function prefersReducedMotion() {
11
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
12
+ try {
13
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
14
+ } catch {
15
+ return false;
16
+ }
17
+ }
18
+ function formatEffectiveDate(value, locale) {
19
+ const iso = value.trim();
20
+ const isCalendarDate = ISO_CALENDAR_DATE.test(iso);
21
+ const date = new Date(isCalendarDate ? `${iso}T00:00:00Z` : iso);
22
+ if (Number.isNaN(date.getTime())) return value;
23
+ return new Intl.DateTimeFormat(locale, {
24
+ year: "numeric",
25
+ month: "long",
26
+ day: "numeric",
27
+ ...isCalendarDate ? { timeZone: "UTC" } : {}
28
+ }).format(date);
29
+ }
30
+ const LegalDocumentShell = React.forwardRef(
31
+ function LegalDocumentShell2({
32
+ title,
33
+ version,
34
+ effectiveDate,
35
+ summary,
36
+ contentsLabel,
37
+ sections,
38
+ activeSection,
39
+ defaultActiveSection,
40
+ onActiveSectionChange,
41
+ documentNavigation,
42
+ footerAction,
43
+ id,
44
+ className
45
+ }, ref) {
46
+ const { t, locale } = useTranslation();
47
+ const generatedId = React.useId();
48
+ const baseId = id ?? generatedId;
49
+ const titleId = `${baseId}-title`;
50
+ const contentsId = `${baseId}-contents`;
51
+ const rootRef = React.useRef(null);
52
+ const setRefs = React.useCallback(
53
+ (node) => {
54
+ rootRef.current = node;
55
+ if (typeof ref === "function") ref(node);
56
+ else if (ref) ref.current = node;
57
+ },
58
+ [ref]
59
+ );
60
+ const isControlled = useControlledLatch(activeSection !== void 0);
61
+ const [internalActive, setInternalActive] = React.useState(
62
+ () => defaultActiveSection ?? sections[0]?.id
63
+ );
64
+ const active = isControlled ? activeSection : internalActive;
65
+ const emit = React.useCallback(
66
+ (sectionId) => {
67
+ if (!isControlled) setInternalActive(sectionId);
68
+ onActiveSectionChange?.(sectionId);
69
+ },
70
+ [isControlled, onActiveSectionChange]
71
+ );
72
+ const activeRef = React.useRef(active);
73
+ const emitRef = React.useRef(emit);
74
+ React.useEffect(() => {
75
+ activeRef.current = active;
76
+ emitRef.current = emit;
77
+ });
78
+ const sectionIdsKey = sections.map((section) => section.id).join("\0");
79
+ const hashHandledRef = React.useRef(false);
80
+ React.useEffect(() => {
81
+ if (hashHandledRef.current || typeof window === "undefined") return;
82
+ hashHandledRef.current = true;
83
+ const hash = decodeURIComponent(window.location.hash.replace(/^#/, ""));
84
+ if (hash && sectionIdsKey.split("\0").includes(hash)) emitRef.current(hash);
85
+ }, [sectionIdsKey]);
86
+ React.useEffect(() => {
87
+ const root = rootRef.current;
88
+ if (!root || typeof IntersectionObserver === "undefined") return;
89
+ const ids = sectionIdsKey ? sectionIdsKey.split("\0") : [];
90
+ const intersecting = /* @__PURE__ */ new Map();
91
+ const observer = new IntersectionObserver(
92
+ (entries) => {
93
+ for (const entry of entries) {
94
+ const entryId = entry.target.dataset.legalSection;
95
+ if (entryId) intersecting.set(entryId, entry.isIntersecting);
96
+ }
97
+ const next = ids.find((sectionId) => intersecting.get(sectionId));
98
+ if (next && next !== activeRef.current) emitRef.current(next);
99
+ },
100
+ { rootMargin: SPY_ROOT_MARGIN, threshold: 0 }
101
+ );
102
+ for (const element of root.querySelectorAll("[data-legal-section]")) {
103
+ observer.observe(element);
104
+ }
105
+ return () => observer.disconnect();
106
+ }, [sectionIdsKey]);
107
+ const handleContentsClick = (event, sectionId) => {
108
+ if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
109
+ return;
110
+ }
111
+ emit(sectionId);
112
+ const target = typeof document === "undefined" ? null : document.getElementById(sectionId);
113
+ if (!target) return;
114
+ event.preventDefault();
115
+ window.history?.replaceState?.(null, "", `#${sectionId}`);
116
+ target.scrollIntoView?.({
117
+ behavior: prefersReducedMotion() ? "auto" : "smooth",
118
+ block: "start"
119
+ });
120
+ target.focus({ preventScroll: true });
121
+ };
122
+ const contentsName = contentsLabel ?? t("layout.legalDocumentShell.contents");
123
+ return /* @__PURE__ */ jsx(
124
+ "div",
125
+ {
126
+ ref: setRefs,
127
+ id,
128
+ "data-slot": "legal-document-shell",
129
+ className: cn("ui-legal-document-scope", className),
130
+ children: /* @__PURE__ */ jsxs("article", { className: "ui-legal-document", "aria-labelledby": titleId, children: [
131
+ /* @__PURE__ */ jsxs("header", { className: "ui-legal-document-header", children: [
132
+ /* @__PURE__ */ jsx(Heading, { level: 1, id: titleId, children: title }),
133
+ (version !== void 0 || effectiveDate !== void 0) && /* @__PURE__ */ jsxs("div", { className: "ui-legal-document-meta", children: [
134
+ version !== void 0 && /* @__PURE__ */ jsx("span", { children: t("layout.legalDocumentShell.version", { version }) }),
135
+ effectiveDate !== void 0 && // <time dateTime> keeps the MACHINE-readable value as the ISO 8601 input while
136
+ // the visible text is the Intl-formatted, locale-correct rendering.
137
+ /* @__PURE__ */ jsx("time", { dateTime: effectiveDate, children: t("layout.legalDocumentShell.effectiveDate", {
138
+ date: formatEffectiveDate(effectiveDate, locale)
139
+ }) })
140
+ ] }),
141
+ summary !== void 0 && // A <div> (not <p>): the summary slot may carry block content, and a <p> inside a <p>
142
+ // is invalid HTML the browser silently un-nests.
143
+ /* @__PURE__ */ jsx("div", { className: "ui-legal-document-summary", children: summary })
144
+ ] }),
145
+ /* @__PURE__ */ jsxs("div", { className: "ui-legal-document-rail", children: [
146
+ documentNavigation !== void 0 && /* @__PURE__ */ jsx("div", { "data-slot": "legal-document-navigation", children: documentNavigation }),
147
+ /* @__PURE__ */ jsxs("nav", { className: "ui-legal-document-toc", "aria-labelledby": contentsId, children: [
148
+ /* @__PURE__ */ jsx("p", { id: contentsId, className: "ui-legal-document-toc-title", children: contentsName }),
149
+ /* @__PURE__ */ jsx("ol", { className: "ui-legal-document-toc-list", children: sections.map((section) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
150
+ "a",
151
+ {
152
+ href: `#${section.id}`,
153
+ className: "ui-legal-document-toc-link",
154
+ "data-active": section.id === active ? "" : void 0,
155
+ "aria-current": section.id === active ? "location" : void 0,
156
+ onClick: (event) => handleContentsClick(event, section.id),
157
+ children: section.title
158
+ }
159
+ ) }, section.id)) })
160
+ ] })
161
+ ] }),
162
+ /* @__PURE__ */ jsx("div", { className: "ui-legal-document-content", children: sections.map((section) => /* @__PURE__ */ jsxs(
163
+ "section",
164
+ {
165
+ id: section.id,
166
+ tabIndex: -1,
167
+ "data-legal-section": section.id,
168
+ "aria-labelledby": `${section.id}-heading`,
169
+ className: "ui-legal-document-section",
170
+ children: [
171
+ /* @__PURE__ */ jsx(Heading, { level: 2, id: `${section.id}-heading`, children: section.title }),
172
+ /* @__PURE__ */ jsx("div", { className: "ui-legal-document-section-body", children: section.content })
173
+ ]
174
+ },
175
+ section.id
176
+ )) }),
177
+ footerAction !== void 0 && /* @__PURE__ */ jsx("footer", { className: "ui-legal-document-footer", children: footerAction })
178
+ ] })
179
+ }
180
+ );
181
+ }
182
+ );
183
+ LegalDocumentShell.displayName = "LegalDocumentShell";
184
+ export {
185
+ LegalDocumentShell
186
+ };
@@ -0,0 +1,24 @@
1
+ import type { MasterDetailProp } from "../../props/components/layout.prop.js";
2
+ export type MasterDetailProps = MasterDetailProp;
3
+ /**
4
+ * Token-owned master-detail composition: a selectable collection beside the detail surface it
5
+ * drives. Geometry is the component's only responsibility — the tracks (`--master-detail-rail-*`),
6
+ * the gap (`--master-detail-gap`) and the stacking threshold (`--master-detail-collapse-below`)
7
+ * are all tokens, so a consumer never authors grid tracks or per-screen spacing (gh#223).
8
+ *
9
+ * `rail` chooses which region keeps the fixed track: `detail` (default) is the canonical fluid
10
+ * list + fixed detail rail; `master` is the leading navigator rail. Either way `master` comes
11
+ * first in DOM order, so below the threshold the regions stack list-then-detail.
12
+ *
13
+ * `masterViewport` bounds the collection (gh#231): `auto` (default) lets it grow with its content
14
+ * exactly as before, while `compact`/`standard` cap its block size with a token and scroll it
15
+ * INSIDE the region — the only thing that stops a long real collection from pushing the detail
16
+ * thousands of pixels below the fold once the layout stacks.
17
+ *
18
+ * SELECTION AND KEYBOARD STAY WITH THE CALLER — deliberately. The controls inside `master` may be
19
+ * a listbox, a tablist, a link list or toggle buttons, and only the caller knows which APG pattern
20
+ * applies; a layout that imposed one would fight it. What the layout DOES own is the wiring those
21
+ * patterns need: two named landmark regions, a stable `detailId` for `aria-controls`, and a
22
+ * `tabIndex={-1}` detail region so the app can move focus to the new detail after a selection.
23
+ */
24
+ export declare function MasterDetail({ master, children, rail, railWidth, masterViewport, collapseBelow, masterLabel, detailLabel, detailId, }: MasterDetailProps): import("react").JSX.Element;
@@ -0,0 +1,48 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ function MasterDetail({
3
+ master,
4
+ children,
5
+ rail = "detail",
6
+ railWidth = "standard",
7
+ masterViewport = "auto",
8
+ collapseBelow,
9
+ masterLabel,
10
+ detailLabel,
11
+ detailId
12
+ }) {
13
+ const bounded = masterViewport !== "auto";
14
+ return /* @__PURE__ */ jsxs(
15
+ "div",
16
+ {
17
+ className: "ui-master-detail",
18
+ "data-rail": rail,
19
+ "data-rail-width": railWidth,
20
+ "data-master-viewport": masterViewport,
21
+ "data-collapse-below": collapseBelow === void 0 ? void 0 : String(collapseBelow),
22
+ children: [
23
+ /* @__PURE__ */ jsx(
24
+ "section",
25
+ {
26
+ className: "ui-master-detail-master",
27
+ "aria-label": masterLabel,
28
+ tabIndex: bounded ? 0 : void 0,
29
+ children: master
30
+ }
31
+ ),
32
+ /* @__PURE__ */ jsx(
33
+ "section",
34
+ {
35
+ className: "ui-master-detail-detail",
36
+ id: detailId,
37
+ "aria-label": detailLabel,
38
+ tabIndex: -1,
39
+ children
40
+ }
41
+ )
42
+ ]
43
+ }
44
+ );
45
+ }
46
+ export {
47
+ MasterDetail
48
+ };
@@ -0,0 +1,9 @@
1
+ import * as React from "react";
2
+ import type { OrgSwitcherProp } from "../../props/components/layout.prop.js";
3
+ export type { OrgSwitcherLabels, OrgSwitcherOrganization, OrgSwitcherProp, OrgSwitcherProp as OrgSwitcherProps, } from "../../props/components/layout.prop.js";
4
+ /**
5
+ * Organization switcher with one stable shell contract: canonical trigger geometry, an optional
6
+ * status badge, a searchable desktop popover, a focus-trapped bottom Sheet at/below the shared
7
+ * `--sheet-responsive-breakpoint-width`, and explicit loading/empty/error states.
8
+ */
9
+ export declare function OrgSwitcher({ organizations, value, onValueChange, collapsed, disabled, loading, error, onRetry, labels, responsive, open, onOpenChange, className, }: OrgSwitcherProp): React.JSX.Element;