@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.
- package/README.md +16 -1
- package/dist/app/theme-axes.d.ts +11 -3
- package/dist/app/theme-axes.js +2 -1
- package/dist/components/charts/chart-frame.d.ts +17 -3
- package/dist/components/charts/chart-frame.js +7 -1
- package/dist/components/charts/chart-summary.d.ts +21 -0
- package/dist/components/charts/chart-summary.js +32 -5
- package/dist/components/charts/compact-bar-trend.d.ts +30 -0
- package/dist/components/charts/compact-bar-trend.js +74 -0
- package/dist/components/charts/index.d.ts +6 -0
- package/dist/components/charts/index.js +2 -0
- package/dist/components/data-display/badge.d.ts +8 -0
- package/dist/components/data-display/badge.js +7 -1
- package/dist/components/data-display/data-table.d.ts +40 -4
- package/dist/components/data-display/data-table.js +69 -2
- package/dist/components/data-display/empty-state.js +1 -1
- package/dist/components/data-display/index.d.ts +4 -2
- package/dist/components/data-display/index.js +10 -1
- package/dist/components/data-display/list-row.d.ts +41 -4
- package/dist/components/data-display/list-row.js +23 -3
- package/dist/components/data-display/qr-code.d.ts +1 -1
- package/dist/components/data-display/service-launcher-card.d.ts +40 -0
- package/dist/components/data-display/service-launcher-card.js +97 -0
- package/dist/components/data-display/table.d.ts +47 -2
- package/dist/components/data-display/table.js +29 -4
- package/dist/components/data-entry/command-palette.d.ts +37 -0
- package/dist/components/data-entry/command-palette.js +125 -0
- package/dist/components/data-entry/command.d.ts +10 -10
- package/dist/components/data-entry/index.d.ts +3 -0
- package/dist/components/data-entry/index.js +2 -0
- package/dist/components/data-entry/input-otp.d.ts +1 -0
- package/dist/components/feedback/dialog.d.ts +16 -1
- package/dist/components/feedback/dialog.js +46 -33
- package/dist/components/feedback/index.d.ts +5 -2
- package/dist/components/feedback/index.js +8 -2
- package/dist/components/feedback/sheet.d.ts +26 -4
- package/dist/components/feedback/sheet.js +54 -7
- package/dist/components/feedback/two-factor-setup.d.ts +31 -0
- package/dist/components/feedback/two-factor-setup.js +97 -0
- package/dist/components/general/index.d.ts +1 -1
- package/dist/components/general/logo.d.ts +49 -9
- package/dist/components/general/logo.js +67 -14
- package/dist/components/layout/app-shell.d.ts +1 -1
- package/dist/components/layout/app-shell.js +55 -30
- package/dist/components/layout/auth-account-summary.d.ts +5 -0
- package/dist/components/layout/auth-account-summary.js +31 -0
- package/dist/components/layout/auth-divider.d.ts +7 -0
- package/dist/components/layout/auth-divider.js +21 -0
- package/dist/components/layout/auth-footer.d.ts +5 -0
- package/dist/components/layout/auth-footer.js +24 -0
- package/dist/components/layout/auth-identity.d.ts +5 -0
- package/dist/components/layout/auth-identity.js +17 -0
- package/dist/components/layout/auth-shell.d.ts +9 -1
- package/dist/components/layout/auth-shell.js +25 -6
- package/dist/components/layout/auth-stack.d.ts +4 -0
- package/dist/components/layout/auth-stack.js +8 -0
- package/dist/components/layout/centered-shell.d.ts +5 -2
- package/dist/components/layout/centered-shell.js +29 -19
- package/dist/components/layout/error-surface.d.ts +35 -0
- package/dist/components/layout/error-surface.js +155 -0
- package/dist/components/layout/flex.d.ts +1 -1
- package/dist/components/layout/flex.js +4 -0
- package/dist/components/layout/index.d.ts +23 -3
- package/dist/components/layout/index.js +21 -2
- package/dist/components/layout/legal-document-shell.d.ts +34 -0
- package/dist/components/layout/legal-document-shell.js +186 -0
- package/dist/components/layout/master-detail.d.ts +24 -0
- package/dist/components/layout/master-detail.js +48 -0
- package/dist/components/layout/org-switcher.d.ts +9 -0
- package/dist/components/layout/org-switcher.js +218 -0
- package/dist/components/layout/page-container.d.ts +1 -1
- package/dist/components/layout/page-container.js +7 -2
- package/dist/components/layout/sidebar-link.d.ts +43 -0
- package/dist/components/layout/sidebar-link.js +13 -0
- package/dist/components/layout/sidebar.d.ts +42 -5
- package/dist/components/layout/sidebar.js +170 -85
- package/dist/components/navigation/app-setting-picker.js +17 -6
- package/dist/components/navigation/filter-bar.d.ts +12 -3
- package/dist/components/navigation/filter-bar.js +8 -2
- package/dist/components/navigation/index.d.ts +2 -2
- package/dist/components/navigation/index.js +3 -1
- package/dist/components/navigation/steps.js +38 -19
- package/dist/components/navigation/tabs-scroll.d.ts +70 -0
- package/dist/components/navigation/tabs-scroll.js +76 -0
- package/dist/components/navigation/tabs.js +47 -23
- package/dist/components/ui/avatar.d.ts +2 -1
- package/dist/components/ui/avatar.js +2 -1
- package/dist/components/ui/input-otp.d.ts +4 -1
- package/dist/components/ui/input-otp.js +2 -1
- package/dist/components/ui/toggle-group.d.ts +7 -4
- package/dist/components/ui/toggle-group.js +32 -21
- package/dist/email/brand-mark.d.ts +108 -0
- package/dist/email/brand-mark.js +88 -0
- package/dist/email/color.d.ts +53 -0
- package/dist/email/color.js +37 -0
- package/dist/email/geometry.d.ts +120 -0
- package/dist/email/geometry.js +90 -0
- package/dist/email/index.d.ts +83 -0
- package/dist/email/index.js +82 -0
- package/dist/email/inline-style.d.ts +27 -0
- package/dist/email/inline-style.js +26 -0
- package/dist/email/tokens.generated.d.ts +167 -0
- package/dist/email/tokens.generated.js +162 -0
- package/dist/email/urgency.d.ts +22 -0
- package/dist/email/urgency.js +14 -0
- package/dist/i18n/messages/en.json +36 -3
- package/dist/i18n/messages/ja.json +36 -3
- package/dist/i18n/messages/vi.json +36 -3
- package/dist/inertia/index.d.ts +50 -0
- package/dist/inertia/index.js +5 -0
- package/dist/props/components/app.prop.d.ts +11 -1
- package/dist/props/components/charts.prop.d.ts +40 -1
- package/dist/props/components/data-display.prop.d.ts +75 -1
- package/dist/props/components/data-entry.prop.d.ts +6 -0
- package/dist/props/components/feedback.prop.d.ts +10 -0
- package/dist/props/components/index.d.ts +3 -3
- package/dist/props/components/layout.prop.d.ts +560 -10
- package/dist/props/components/layout.prop.js +1 -0
- package/dist/props/components/navigation.prop.d.ts +24 -2
- package/dist/props/registry.d.ts +433 -8
- package/dist/props/registry.js +527 -9
- package/dist/props/vocabulary/data.prop.d.ts +27 -0
- package/dist/props/vocabulary/index.d.ts +3 -3
- package/dist/props/vocabulary/interaction.prop.d.ts +10 -1
- package/dist/props/vocabulary/layout.prop.d.ts +49 -0
- package/dist/styles/base.css +8 -2
- package/dist/styles/card-layout.css +161 -7
- package/dist/styles/chart-layout.css +97 -0
- package/dist/styles/control.css +76 -2
- package/dist/styles/data-display-layout.css +78 -6
- package/dist/styles/dialog-layout.css +105 -4
- package/dist/styles/fonts.css +15 -4
- package/dist/styles/index.css +11 -3
- package/dist/styles/layout.css +466 -1
- package/dist/styles/logo-layout.css +95 -0
- package/dist/styles/navigation-layout.css +152 -0
- package/dist/styles/shell-layout.css +790 -40
- package/dist/styles/table-layout.css +164 -1
- package/dist/theme/dxs.canonical.css +77 -0
- package/dist/tokens/axes.css +40 -0
- package/dist/tokens/base.css +6 -0
- package/dist/tokens/components/card.css +48 -0
- package/dist/tokens/components/chart.css +47 -0
- package/dist/tokens/components/control.css +16 -0
- package/dist/tokens/components/data-display.css +13 -0
- package/dist/tokens/components/email.css +93 -0
- package/dist/tokens/components/error-surface.css +36 -0
- package/dist/tokens/components/feedback.css +17 -0
- package/dist/tokens/components/legal-document.css +67 -0
- package/dist/tokens/components/list-row.css +43 -2
- package/dist/tokens/components/logo.css +74 -0
- package/dist/tokens/components/navigation.css +28 -0
- package/dist/tokens/components/sheet.css +19 -0
- package/dist/tokens/components/shell.css +210 -0
- package/dist/tokens/components/sidebar.css +33 -0
- package/dist/tokens/components/table.css +45 -0
- package/dist/tokens/foundation.css +31 -2
- package/dist/tokens/semantic/layout.css +69 -2
- package/package.json +22 -5
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
/** Layout component prop types — @see docs/COMPONENTS.md#layout */
|
|
2
2
|
import type * as React from "react";
|
|
3
3
|
import type { ComponentType, ReactNode, SVGProps } from "react";
|
|
4
|
-
import type { BreadcrumbProp, TitleProp, SubtitleProp, ExtraProp, FooterProp, PageDensityProp, PageContainerVariantProp, CenteredShellWidthProp, GapProp, ClassNameProp, ChildrenProp } from "../vocabulary/index.js";
|
|
4
|
+
import type { BreadcrumbProp, TitleProp, SubtitleProp, ExtraProp, FooterProp, PageDensityProp, PageContainerVariantProp, CenteredShellWidthProp, CenteredShellAlignProp, CenteredShellPresetProp, ErrorSurfaceModeProp, ErrorSurfaceStatusProp, AuthShellPresetProp, BreakpointProp, GapProp, ClassNameProp, ChildrenProp, IdProp, DisabledProp, DescriptionProp, ActionProp, IconProp, HeadingLevelProp } from "../vocabulary/index.js";
|
|
5
|
+
import type { EmptyStateToneProp } from "./data-display.prop.js";
|
|
6
|
+
/**
|
|
7
|
+
* Arrangement of the page header's title band and its `extra` slot below the 640px step.
|
|
8
|
+
* `stack` (default) is the historical arrangement — `extra` drops onto its own full-width line
|
|
9
|
+
* under the subtitle. `responsive-inline` keeps `extra` beside the title band at the
|
|
10
|
+
* `--page-header-extra-measure` measure, letting the title/subtitle wrap into what is left.
|
|
11
|
+
*/
|
|
12
|
+
export type PageContainerHeaderLayoutProp = "stack" | "responsive-inline";
|
|
13
|
+
/** Whole-page semantic composition owned by PageContainer. */
|
|
14
|
+
export type PageContainerPresetProp = "default" | "admin-collection";
|
|
15
|
+
/**
|
|
16
|
+
* Bounded page MEASURE — the shared inline cap applied to the page header AND body together, so
|
|
17
|
+
* the header `extra` action ends on the same edge as the body surface. Orthogonal to
|
|
18
|
+
* `PageContainerVariantProp` (chrome) and to `PageContainerHeaderLayoutProp`, so a quiet
|
|
19
|
+
* `variant="ghost"` feed can finally have a bounded measure too (gh#245 / gh#247).
|
|
20
|
+
*
|
|
21
|
+
* `default` applies NO cap — the page is fluid exactly as before. `narrow` / `medium` read the
|
|
22
|
+
* `--page-measure-{narrow,medium}` tokens (42rem / 48rem OUTER, i.e. 624px / 720px VISIBLE surface
|
|
23
|
+
* once the package-owned page gutters are subtracted). Both are maxes, so a compact viewport stays
|
|
24
|
+
* fluid at the compact gutter.
|
|
25
|
+
*/
|
|
26
|
+
export type PageContainerMeasureProp = "default" | "narrow" | "medium";
|
|
5
27
|
/** @see PageContainer */
|
|
6
28
|
export type PageContainerProp = {
|
|
7
29
|
title: TitleProp;
|
|
@@ -21,6 +43,29 @@ export type PageContainerProp = {
|
|
|
21
43
|
linkComponent?: React.ElementType;
|
|
22
44
|
density?: PageDensityProp;
|
|
23
45
|
variant?: PageContainerVariantProp;
|
|
46
|
+
/**
|
|
47
|
+
* Whole-page composition contract. `admin-collection` sets the header-to-toolbar rhythm,
|
|
48
|
+
* collection search measure, control height and table density once for the entire subtree.
|
|
49
|
+
* Geometry remains token-owned and service-themeable; no child needs a sizing override.
|
|
50
|
+
*/
|
|
51
|
+
preset?: PageContainerPresetProp;
|
|
52
|
+
/**
|
|
53
|
+
* How the title band and `extra` share the header row below the 640px step. Defaults to
|
|
54
|
+
* `stack` — the historical arrangement, where `extra` wraps onto its own full-width line under
|
|
55
|
+
* the subtitle. Use `responsive-inline` to keep ONE compact control (a search field, a single
|
|
56
|
+
* primary action) beside the title at 390px, at the token-owned
|
|
57
|
+
* `--page-header-extra-measure`. At >=640px both arrangements are identical.
|
|
58
|
+
*/
|
|
59
|
+
headerLayout?: PageContainerHeaderLayoutProp;
|
|
60
|
+
/**
|
|
61
|
+
* Bounded page measure shared by the header and the body. Defaults to `default` — no cap, the
|
|
62
|
+
* historical fluid page. `narrow` (624px surface) / `medium` (720px surface) cap BOTH bands to
|
|
63
|
+
* one token-owned measure (`--page-measure-{narrow,medium}`), so a header action ends flush with
|
|
64
|
+
* the body surface instead of at the page edge. Orthogonal to `variant`, so `variant="ghost"`
|
|
65
|
+
* quiet chrome composes with a bounded measure (gh#245 / gh#247). Unlike `variant="narrow"`,
|
|
66
|
+
* which caps only the body.
|
|
67
|
+
*/
|
|
68
|
+
measure?: PageContainerMeasureProp;
|
|
24
69
|
/** Pin footer to viewport bottom on scroll — pairs well with `variant="narrow"`. */
|
|
25
70
|
stickyFooter?: boolean;
|
|
26
71
|
/**
|
|
@@ -53,12 +98,72 @@ export type FlexProp = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
53
98
|
align?: FlexAlignProp;
|
|
54
99
|
justify?: FlexJustifyProp;
|
|
55
100
|
wrap?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Drop this region below a breakpoint step (`sm` 40rem · `md` 48rem · `lg` 64rem · `xl` 80rem).
|
|
103
|
+
* The ONE public way to make a layout region responsive without a page-local media query — a
|
|
104
|
+
* public header hides its anchor navigation below the tablet step with `hideBelow="md"` instead
|
|
105
|
+
* of a consumer `@media` rule (gh#252). Omit (the default) and no attribute is emitted, so no
|
|
106
|
+
* rule can match and the Flex is unchanged. The region is removed from the accessibility tree
|
|
107
|
+
* too, so keep its destinations reachable elsewhere at that width (a footer nav).
|
|
108
|
+
*/
|
|
109
|
+
hideBelow?: BreakpointProp;
|
|
110
|
+
/**
|
|
111
|
+
* The inverse of `hideBelow` — drop this region FROM a breakpoint step upwards, i.e. keep it
|
|
112
|
+
* only on the narrow side (a compact-only affordance). Omit for no attribute and no rule.
|
|
113
|
+
*/
|
|
114
|
+
hideFrom?: BreakpointProp;
|
|
56
115
|
};
|
|
57
116
|
export type ResponsiveGridColumnsProp = number | {
|
|
58
117
|
sm?: number;
|
|
59
118
|
md?: number;
|
|
60
119
|
lg?: number;
|
|
61
120
|
};
|
|
121
|
+
export type MasterDetailRailWidthProp = "compact" | "standard";
|
|
122
|
+
export type MasterDetailRailProp = "master" | "detail";
|
|
123
|
+
/**
|
|
124
|
+
* Bounded viewport preset for the master collection. `auto` (default) never bounds it — the
|
|
125
|
+
* region grows with its content, exactly as before. `compact` / `standard` cap its block size
|
|
126
|
+
* with the `--master-detail-master-viewport-*` tokens and scroll the collection inside the
|
|
127
|
+
* region, so a long list cannot push the detail below the fold once the layout stacks.
|
|
128
|
+
*/
|
|
129
|
+
export type MasterDetailMasterViewportProp = "auto" | "compact" | "standard";
|
|
130
|
+
/** @see MasterDetail */
|
|
131
|
+
export type MasterDetailProp = {
|
|
132
|
+
/** Selectable collection; always first in DOM order, so the stacked order stays list-then-detail. */
|
|
133
|
+
master: ReactNode;
|
|
134
|
+
/** Detail surface for the current selection. */
|
|
135
|
+
children: ChildrenProp;
|
|
136
|
+
/**
|
|
137
|
+
* Which region is the fixed-width rail; the other one is fluid. Defaults to `detail` — the
|
|
138
|
+
* canonical fluid-list + fixed-detail-rail composition. Use `master` for a leading
|
|
139
|
+
* category/navigator rail beside a fluid detail surface.
|
|
140
|
+
*/
|
|
141
|
+
rail?: MasterDetailRailProp;
|
|
142
|
+
/** Rail track width: `compact` = 300px; `standard` = 320px. */
|
|
143
|
+
railWidth?: MasterDetailRailWidthProp;
|
|
144
|
+
/**
|
|
145
|
+
* Bound the master collection to a scrollable viewport instead of letting it grow with its
|
|
146
|
+
* content. `auto` (default) keeps the unbounded behaviour. `compact` (20rem) / `standard`
|
|
147
|
+
* (28rem) read the `--master-detail-master-viewport-*` tokens, scroll the collection INSIDE the
|
|
148
|
+
* region, and make it a keyboard-reachable scroll container. Pair with `masterLabel` so the
|
|
149
|
+
* scroll region is announced.
|
|
150
|
+
*/
|
|
151
|
+
masterViewport?: MasterDetailMasterViewportProp;
|
|
152
|
+
/**
|
|
153
|
+
* Stack the two regions below this breakpoint (`false` never stacks). Omit to inherit the
|
|
154
|
+
* themeable `--master-detail-collapse-below` token (default 40rem / the `sm` step).
|
|
155
|
+
*/
|
|
156
|
+
collapseBelow?: BreakpointProp | false;
|
|
157
|
+
/** Accessible name for the master region. */
|
|
158
|
+
masterLabel?: string;
|
|
159
|
+
/** Accessible name for the detail region. */
|
|
160
|
+
detailLabel?: string;
|
|
161
|
+
/**
|
|
162
|
+
* Id of the detail region, so the selection controls inside `master` can point at it with
|
|
163
|
+
* `aria-controls` and the app can move focus to it after a selection.
|
|
164
|
+
*/
|
|
165
|
+
detailId?: IdProp;
|
|
166
|
+
};
|
|
62
167
|
/** @see PageContainer.Inset — full-bleed inset region inside the page padding. */
|
|
63
168
|
export type PageInsetProp = React.HTMLAttributes<HTMLDivElement> & {
|
|
64
169
|
children?: ChildrenProp;
|
|
@@ -75,6 +180,17 @@ export type AppShellProp = {
|
|
|
75
180
|
breadcrumb?: ReactNode;
|
|
76
181
|
footer?: ReactNode;
|
|
77
182
|
sidebarCollapsed?: boolean;
|
|
183
|
+
/**
|
|
184
|
+
* Responsive navigation strategy below the canonical 900px shell breakpoint.
|
|
185
|
+
*
|
|
186
|
+
* - `"drawer"` (default) hides the docked sidebar and exposes the accessible mobile Sheet.
|
|
187
|
+
* - `"docked"` keeps the sidebar grid track, footer/account region and active navigation in the
|
|
188
|
+
* shell at narrow widths. The sidebar width remains owned by `--app-shell-sidebar-width`.
|
|
189
|
+
*
|
|
190
|
+
* Use `"docked"` only when the product's approved responsive contract explicitly retains the
|
|
191
|
+
* rail; it intentionally suppresses the redundant mobile drawer trigger.
|
|
192
|
+
*/
|
|
193
|
+
responsiveNavigation?: "drawer" | "docked";
|
|
78
194
|
/**
|
|
79
195
|
* Navigation shown in the mobile drawer at the DXS 900px breakpoint, where the docked sidebar is
|
|
80
196
|
* hidden. AppShell OWNS the drawer: it renders a hamburger trigger in the topbar and a focus-
|
|
@@ -106,6 +222,97 @@ export type AuthShellProp = {
|
|
|
106
222
|
brand?: ReactNode;
|
|
107
223
|
/** Footer slot pinned to the bottom (legal links, locale switch, support). */
|
|
108
224
|
footer?: ReactNode;
|
|
225
|
+
/**
|
|
226
|
+
* Visual contract for the auth surface. `"canonical"` applies the shared DXS compact geometry
|
|
227
|
+
* (36px controls, 22.5rem card measure, and responsive page insets) through component tokens.
|
|
228
|
+
* Default `"default"` preserves the existing comfortable shell.
|
|
229
|
+
*/
|
|
230
|
+
variant?: "default" | "canonical";
|
|
231
|
+
/**
|
|
232
|
+
* Named flow MEASURE — the page geometry contract for one canonical hosted-identity flow: the
|
|
233
|
+
* auth card's max-width plus the desktop and mobile page gutters, all owned by component tokens
|
|
234
|
+
* (`--auth-shell-{login,device,context,recovery}-*`). Selecting a preset replaces every
|
|
235
|
+
* consumer-side geometry override.
|
|
236
|
+
*
|
|
237
|
+
* - `"default"` (default) — the shell's own measure; nothing changes.
|
|
238
|
+
* - `"login"` — SCR-001's 360px card at x=540/332/15 and y=363/363/353 for the canonical
|
|
239
|
+
* 1440x900, 1024x900 and 390x844 viewports. The identity occupies a package-owned anchor slot,
|
|
240
|
+
* so standalone, one-line requester and wrapped two-line requester states keep the same card
|
|
241
|
+
* position without truncating or inventing requester data. Pass AuthIdentity, Card and
|
|
242
|
+
* AuthFooter as direct children (an anchor may wrap AuthIdentity).
|
|
243
|
+
* - `"device-authorization"` — 380px card measure with a 5px inline page gutter at a 390px
|
|
244
|
+
* viewport (canonical device-grant artboard).
|
|
245
|
+
* - `"context-selection"` — 25rem card measure on desktop/tablet, edge-to-edge on mobile, and a
|
|
246
|
+
* tokenized rhythm between the intro, the card and the trailing "remember" row.
|
|
247
|
+
* - `"account-recovery"` — 27rem/432px panel measure with a 15px inline page gutter at 390px
|
|
248
|
+
* (panel x=15, width=360). One measure for BOTH canonical SCR-008 panels: password recovery
|
|
249
|
+
* (request · sent · new-password · expired) and the sign-in MFA challenge (OTP · recovery-code
|
|
250
|
+
* · passkey-failure), whose title and description sit INSIDE the bordered surface.
|
|
251
|
+
*
|
|
252
|
+
* Orthogonal to `variant`: presets are applied AFTER it, so `variant="canonical"` keeps owning
|
|
253
|
+
* control density and heading size while the preset re-measures/anchors the page.
|
|
254
|
+
*/
|
|
255
|
+
preset?: AuthShellPresetProp;
|
|
256
|
+
/**
|
|
257
|
+
* Vertical density scoped to auth-card descendants. The canonical variant defaults to
|
|
258
|
+
* `"compact"`; the default variant defaults to `"comfortable"`.
|
|
259
|
+
*/
|
|
260
|
+
density?: "comfortable" | "compact";
|
|
261
|
+
className?: ClassNameProp;
|
|
262
|
+
};
|
|
263
|
+
/** @see AuthDivider */
|
|
264
|
+
export type AuthDividerProp = {
|
|
265
|
+
/** Short localized conjunction rendered between the two separator rules (for example, "or"). */
|
|
266
|
+
label: string;
|
|
267
|
+
className?: ClassNameProp;
|
|
268
|
+
};
|
|
269
|
+
/**
|
|
270
|
+
* @see AuthFooter — the canonical hosted-identity legal line (host · Terms · Privacy · locale).
|
|
271
|
+
* AuthFooter owns ONLY the geometry: the mono type ramp, the wrap behaviour and the `·`
|
|
272
|
+
* separators between the slots that are actually present (tokens `--auth-footer-*`). Every slot
|
|
273
|
+
* is consumer-owned content — real localized links and a real locale control — so the library
|
|
274
|
+
* never invents navigation. Drop it into `AuthShell`'s `footer` slot (which supplies the
|
|
275
|
+
* `contentinfo` landmark); it renders a plain `div`, so it can also sit inside an existing footer.
|
|
276
|
+
*/
|
|
277
|
+
export type AuthFooterProp = {
|
|
278
|
+
/** Product / host identity — the operator of the auth surface (e.g. "GoDX ID"). */
|
|
279
|
+
product: ReactNode;
|
|
280
|
+
/** Terms-of-service link or localized text. */
|
|
281
|
+
terms: ReactNode;
|
|
282
|
+
/** Privacy-policy link or localized text. */
|
|
283
|
+
privacy: ReactNode;
|
|
284
|
+
/** Optional consumer-owned locale control (e.g. `<AppSettingPicker kind="locale" compact />`). */
|
|
285
|
+
locale?: ReactNode;
|
|
286
|
+
className?: ClassNameProp;
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* @see AuthIdentity — the canonical hosted-identity heading block: the brand-green GoDX mark
|
|
290
|
+
* (`Logo mark="godx"`, independent of `--primary`), the `h1` auth heading, and an optional
|
|
291
|
+
* requesting-client line for delegated flows (device grant, OAuth consent). Centred, token-spaced
|
|
292
|
+
* (`--auth-identity-gap` / `--auth-requester-*`) — a consumer never re-centres or re-spaces it.
|
|
293
|
+
*/
|
|
294
|
+
export type AuthIdentityProp = {
|
|
295
|
+
/** Primary auth heading, rendered as the page `h1`. */
|
|
296
|
+
title: ReactNode;
|
|
297
|
+
/**
|
|
298
|
+
* Optional real requesting-client context ("Attendance is requesting sign in"). Pass it ONLY
|
|
299
|
+
* when the client identity is authoritative — never a placeholder.
|
|
300
|
+
*/
|
|
301
|
+
requester?: ReactNode;
|
|
302
|
+
className?: ClassNameProp;
|
|
303
|
+
};
|
|
304
|
+
/**
|
|
305
|
+
* @see AuthAccountSummary — compact signed-in identity row for hosted authentication surfaces.
|
|
306
|
+
* It owns avatar fallback, bidi-safe email truncation and the keyboard action geometry; the
|
|
307
|
+
* consumer owns the authoritative email, localized action label and navigation handler.
|
|
308
|
+
*/
|
|
309
|
+
export type AuthAccountSummaryProp = {
|
|
310
|
+
email: string;
|
|
311
|
+
avatarSrc?: string;
|
|
312
|
+
avatarFallback?: ReactNode;
|
|
313
|
+
actionLabel: ReactNode;
|
|
314
|
+
onAction: () => void;
|
|
315
|
+
disabled?: DisabledProp;
|
|
109
316
|
className?: ClassNameProp;
|
|
110
317
|
};
|
|
111
318
|
/**
|
|
@@ -134,6 +341,128 @@ export type CenteredShellProp = {
|
|
|
134
341
|
* wider than AuthShell's 24rem auth card. A service retunes each tier via `--centered-shell-width-*`.
|
|
135
342
|
*/
|
|
136
343
|
width?: CenteredShellWidthProp;
|
|
344
|
+
/**
|
|
345
|
+
* Block alignment of the centred column inside the `100dvh` shell. `"start"` (default) keeps the
|
|
346
|
+
* top-aligned flowing/scrolling page shape. `"center"` centres the column in the viewport — the
|
|
347
|
+
* SYSTEM-level standalone surface (a 500/503 error page, a maintenance notice) whose full-page
|
|
348
|
+
* geometry must stay package-owned instead of a consumer re-implementing `min-h-dvh` + flex
|
|
349
|
+
* centring. Overflowing content still scrolls from the top (auto block offsets collapse to 0), so
|
|
350
|
+
* a long localized message is never clipped.
|
|
351
|
+
*/
|
|
352
|
+
align?: CenteredShellAlignProp;
|
|
353
|
+
/**
|
|
354
|
+
* Whole-page shell contract. `"default"` (the default) emits no attribute and keeps the shell's
|
|
355
|
+
* exact box. `"public-landing"` owns the PUBLIC landing geometry — one content measure shared by
|
|
356
|
+
* the header bar, the centred column and the footer, the section rhythm, the flat public-surface
|
|
357
|
+
* card chrome and the hero `h1` tier — from `--centered-shell-landing-*` tokens, so a landing
|
|
358
|
+
* composition (header · hero · sections · legal footer) needs no page-local CSS and no descendant
|
|
359
|
+
* selector against shell internals (gh#252).
|
|
360
|
+
*/
|
|
361
|
+
preset?: CenteredShellPresetProp;
|
|
362
|
+
className?: ClassNameProp;
|
|
363
|
+
};
|
|
364
|
+
/**
|
|
365
|
+
* @see ErrorSurface — the optional maintenance / planned-outage timing slot (503, occasionally a
|
|
366
|
+
* planned 500).
|
|
367
|
+
*
|
|
368
|
+
* `start` / `end` are **ISO-8601 instants** and `timeZone` an **IANA** zone id: the surface formats
|
|
369
|
+
* them with `Intl.DateTimeFormat(locale, …).formatRange()` (CLDR), so ja / en / vi each read
|
|
370
|
+
* natively. NEVER pass a pre-formatted string like `"18:00 - 20:00 JST"` — it cannot localize, and
|
|
371
|
+
* the machine-readable value is what lands in `<time dateTime>`.
|
|
372
|
+
*
|
|
373
|
+
* `progress` is server-sent on purpose: deriving "how far through the window are we" from the
|
|
374
|
+
* client clock makes SSR and hydration disagree, and an exception page must be readable before
|
|
375
|
+
* hydration.
|
|
376
|
+
*/
|
|
377
|
+
export type ErrorSurfaceMaintenanceProp = {
|
|
378
|
+
/** Window start as an ISO-8601 instant (`2026-08-02T18:00:00Z`). Also the `<time dateTime>` value. */
|
|
379
|
+
start: string;
|
|
380
|
+
/** Window end as an ISO-8601 instant. Omit for an open-ended outage — a single instant is shown. */
|
|
381
|
+
end?: string;
|
|
382
|
+
/**
|
|
383
|
+
* IANA time zone id (`Asia/Tokyo`) the window is presented in. Omit to use the runtime zone —
|
|
384
|
+
* pass it explicitly whenever the page is server-rendered, or SSR and client output diverge.
|
|
385
|
+
*/
|
|
386
|
+
timeZone?: string;
|
|
387
|
+
/**
|
|
388
|
+
* Completion of the maintenance window as a **percentage 0–100**, rendered as a labelled
|
|
389
|
+
* `Progress` meter. Server-sent (see above); omit for an outage with no published progress.
|
|
390
|
+
*/
|
|
391
|
+
progress?: number;
|
|
392
|
+
};
|
|
393
|
+
/**
|
|
394
|
+
* @see ErrorSurface — the package-owned semantic exception surface for 403 / 404 / 500 / 503.
|
|
395
|
+
*
|
|
396
|
+
* The `mode` is the SHELL CONTRACT, not a skin:
|
|
397
|
+
* - `mode="application"` (403/404) renders the surface as the **body** you put inside the
|
|
398
|
+
* `AppShell` the route already provides (normally within a `PageContainer`). It deliberately does
|
|
399
|
+
* NOT reconstruct navigation chrome: the sidebar, topbar and user menu are consumer-owned data,
|
|
400
|
+
* so the surface preserves the shell it is placed in instead of manufacturing a fake one.
|
|
401
|
+
* - `mode="system"` (500/503) owns the whole page: it renders `CenteredShell align="center"`, so
|
|
402
|
+
* the viewport-centred geometry at 1440 / 1024 / 390 stays package-owned and a consumer never
|
|
403
|
+
* writes `min-h-dvh`, a flex-centring class or a media query.
|
|
404
|
+
*
|
|
405
|
+
* `action` is **exactly one** recovery action, enforced structurally by a single slot (a second
|
|
406
|
+
* element is dropped with a development error). Support contact belongs in `description`, not in a
|
|
407
|
+
* second CTA.
|
|
408
|
+
*
|
|
409
|
+
* All product COPY stays consumer-owned (`title` / `description` / `action` come from the app's own
|
|
410
|
+
* `t()`); the surface owns only its own metadata labels, which it localizes itself.
|
|
411
|
+
*/
|
|
412
|
+
export type ErrorSurfaceProp = {
|
|
413
|
+
/** Where the surface lives — `application` = AppShell body (403/404), `system` = own page (500/503). */
|
|
414
|
+
mode: ErrorSurfaceModeProp;
|
|
415
|
+
/** HTTP status presented. Drives the default `icon`, `tone` and the rendered status code. */
|
|
416
|
+
status: ErrorSurfaceStatusProp;
|
|
417
|
+
/** Headline. Consumer-owned copy from the app's `t()` — the library ships no product text. */
|
|
418
|
+
title: TitleProp;
|
|
419
|
+
/** Supporting sentence under the title. Put support-contact guidance here, never in a 2nd CTA. */
|
|
420
|
+
description?: DescriptionProp;
|
|
421
|
+
/**
|
|
422
|
+
* The ONE recovery action (a `Button`, or a `Button asChild` wrapping a router `Link`). A single
|
|
423
|
+
* slot IS the enforcement: pass more than one element and only the first renders, with a
|
|
424
|
+
* development-time error.
|
|
425
|
+
*/
|
|
426
|
+
action: ActionProp;
|
|
427
|
+
/** Override the status-derived icon (403 ShieldAlert · 404 SearchX · 500 ServerCrash · 503 Wrench). */
|
|
428
|
+
icon?: IconProp;
|
|
429
|
+
/** Override the status-derived tone (403/503 `warning` · 404 `muted` · 500 `destructive`). */
|
|
430
|
+
tone?: EmptyStateToneProp;
|
|
431
|
+
/**
|
|
432
|
+
* Semantic heading level of `title`. Defaults to `2` in `application` mode (a `PageContainer`
|
|
433
|
+
* `h1` sits above it) and `1` in `system` mode (the surface IS the page). Choose it to keep the
|
|
434
|
+
* outline valid, never for size.
|
|
435
|
+
*/
|
|
436
|
+
titleLevel?: HeadingLevelProp;
|
|
437
|
+
/**
|
|
438
|
+
* Support correlation id for the failure, rendered as a monospace/tabular metadata row so it can
|
|
439
|
+
* be read out or copied accurately. Pass the bare id — the localized label is the surface's.
|
|
440
|
+
*/
|
|
441
|
+
requestId?: string;
|
|
442
|
+
/**
|
|
443
|
+
* The permission / role the viewer is missing (403). Pass the bare permission name
|
|
444
|
+
* (`reports.view`) — the surface renders the localized "Required permission" label around it.
|
|
445
|
+
*/
|
|
446
|
+
permission?: ReactNode;
|
|
447
|
+
/**
|
|
448
|
+
* The organization / tenant the failed request was scoped to. Disambiguates a 403 caused by
|
|
449
|
+
* being in the wrong workspace from one caused by a missing role.
|
|
450
|
+
*/
|
|
451
|
+
organization?: ReactNode;
|
|
452
|
+
/** Optional planned-outage timing + progress (503). ISO-8601 + IANA, formatted with `Intl`. */
|
|
453
|
+
maintenance?: ErrorSurfaceMaintenanceProp;
|
|
454
|
+
/**
|
|
455
|
+
* `system` mode only — brand slot above the status code (a `Logo`). Ignored in `application`
|
|
456
|
+
* mode, where the shell already shows the product brand.
|
|
457
|
+
*/
|
|
458
|
+
brand?: ReactNode;
|
|
459
|
+
/** `system` mode only — the page footer (contentinfo): copyright, status page, locale switch. */
|
|
460
|
+
footer?: FooterProp;
|
|
461
|
+
/**
|
|
462
|
+
* `system` mode only — measure of the centred column (`CenteredShell` width tier). Default `sm`.
|
|
463
|
+
*/
|
|
464
|
+
width?: CenteredShellWidthProp;
|
|
465
|
+
id?: IdProp;
|
|
137
466
|
className?: ClassNameProp;
|
|
138
467
|
};
|
|
139
468
|
/** @see Sidebar */
|
|
@@ -146,14 +475,24 @@ export type SidebarProductProp = {
|
|
|
146
475
|
export type SidebarItemProp = {
|
|
147
476
|
id: string;
|
|
148
477
|
label: string;
|
|
478
|
+
/**
|
|
479
|
+
* Leading 16px glyph — REQUIRED: the collapsed rail is icon-only and the expanded rail aligns
|
|
480
|
+
* every label to the icon column. Untyped/API-driven data that omits it no longer crashes the
|
|
481
|
+
* shell (the row renders an empty `.sb-icon` slot, keeping the 32px row / 10px gap), but the rail
|
|
482
|
+
* reads as a hole. Its colour is themeable separately from the label via
|
|
483
|
+
* `--sidebar-nav-icon-foreground` (see {@link SidebarProp}).
|
|
484
|
+
*/
|
|
149
485
|
icon: ComponentType<SVGProps<SVGSVGElement>>;
|
|
150
486
|
badge?: ReactNode;
|
|
151
487
|
disabled?: boolean;
|
|
152
488
|
/**
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
489
|
+
* Destination of the row. It is the SOLE interactive element (no nested `<button>`), so
|
|
490
|
+
* right-click / open-in-new-tab / middle-click all work. Omit for SPA rows that only report
|
|
491
|
+
* selection via `onSelect(id)`.
|
|
492
|
+
*
|
|
493
|
+
* With `Sidebar.linkComponent` this same `href` is what the framework router `<Link>` receives —
|
|
494
|
+
* the LIBRARY still composes the row (icon · label · badge · active · collapsed), so a router
|
|
495
|
+
* link never has to reconstruct row markup (gh#213).
|
|
157
496
|
*/
|
|
158
497
|
href?: string;
|
|
159
498
|
/** Nested rows — renders a collapsible submenu group (the parent reads active when any child is). */
|
|
@@ -161,11 +500,121 @@ export type SidebarItemProp = {
|
|
|
161
500
|
};
|
|
162
501
|
/** @see Sidebar */
|
|
163
502
|
export type SidebarItemData = SidebarItemProp;
|
|
503
|
+
/**
|
|
504
|
+
* Props the Sidebar hands to `Sidebar.linkComponent` for one nav row (gh#213).
|
|
505
|
+
*
|
|
506
|
+
* Every field is ANCHOR-SAFE — a router `<Link>` may spread the whole object onto its `<a>` without
|
|
507
|
+
* emitting an unknown-DOM-attribute warning. `children` is the LIBRARY-COMPOSED row content (the
|
|
508
|
+
* `.sb-icon` slot, the `.sb-label`, the `.sb-badge`); render it as-is and never rebuild it, which is
|
|
509
|
+
* what makes icons/badges survive a consumer link (the reported production regression).
|
|
510
|
+
*/
|
|
511
|
+
export type SidebarLinkProp = {
|
|
512
|
+
/** `SidebarItemProp.href`. Absent for a disabled row — render an inert `<a>` with no navigation. */
|
|
513
|
+
href?: string;
|
|
514
|
+
/** Library-composed row content: icon slot + label + badge (icon only on the collapsed rail). */
|
|
515
|
+
children: ReactNode;
|
|
516
|
+
/** Canonical row class (`sb-nav-item`, plus `sb-nav-item--sub` for a submenu child). */
|
|
517
|
+
className: string;
|
|
518
|
+
/** Present only on the active row — drives `--sidebar-item-active-*`. */
|
|
519
|
+
"data-active"?: "true";
|
|
520
|
+
/** WAI-ARIA current-page semantics for the active row. */
|
|
521
|
+
"aria-current"?: "page";
|
|
522
|
+
/** Set when the item (or its row) is disabled; the row must not navigate. */
|
|
523
|
+
"aria-disabled"?: true;
|
|
524
|
+
/** Accessible name for the icon-only collapsed rail, where the visible label is hidden. */
|
|
525
|
+
"aria-label"?: string;
|
|
526
|
+
/** `"menuitem"` inside the collapsed rail's portaled flyout menu; absent for ordinary rows. */
|
|
527
|
+
role?: "menuitem";
|
|
528
|
+
/** Reports selection to `Sidebar.onSelect` after the router link runs its own handler. */
|
|
529
|
+
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
|
530
|
+
};
|
|
531
|
+
/**
|
|
532
|
+
* A framework router link component driven by {@link SidebarLinkProp} — Inertia's `<Link href>`,
|
|
533
|
+
* a React Router / TanStack link wrapped by `createSidebarLink(Link, "to")`, or any component that
|
|
534
|
+
* renders a single `<a>`. It must forward its `ref` to that anchor so the collapsed rail's Tooltip
|
|
535
|
+
* can anchor to it.
|
|
536
|
+
*/
|
|
537
|
+
export type SidebarLinkComponentProp = ComponentType<SidebarLinkProp>;
|
|
538
|
+
/**
|
|
539
|
+
* Row state supplied to the DEPRECATED `Sidebar.renderItem`.
|
|
540
|
+
*
|
|
541
|
+
* @deprecated Prefer `Sidebar.linkComponent` (or `SidebarItem asChild`), where the library composes
|
|
542
|
+
* the row and the consumer supplies only the element. `renderItem` leaves row CONTENT to the
|
|
543
|
+
* consumer, which is how a `<Link>{item.label}</Link>` silently dropped every icon and badge
|
|
544
|
+
* (gh#213). Spreading `rowProps` — including its `children` — now yields the canonical row.
|
|
545
|
+
*/
|
|
546
|
+
export type SidebarRenderItemProp = {
|
|
547
|
+
className: string;
|
|
548
|
+
"data-active"?: "true";
|
|
549
|
+
"aria-current"?: "page";
|
|
550
|
+
"aria-disabled"?: true;
|
|
551
|
+
/**
|
|
552
|
+
* Library-composed row content (icon slot · label · badge). Spread `rowProps` onto your element,
|
|
553
|
+
* or render `rowProps.children` explicitly, to keep the canonical row while adding an affix.
|
|
554
|
+
*/
|
|
555
|
+
children?: ReactNode;
|
|
556
|
+
};
|
|
164
557
|
/** @see Sidebar */
|
|
165
558
|
export type SidebarSectionProp = {
|
|
166
559
|
label?: string;
|
|
167
560
|
items: SidebarItemProp[];
|
|
168
561
|
};
|
|
562
|
+
/** One selectable organization in the public {@link OrgSwitcher} contract. */
|
|
563
|
+
export type OrgSwitcherOrganization = {
|
|
564
|
+
id: string;
|
|
565
|
+
name: string;
|
|
566
|
+
/** Secondary organization context, for example the member's role or tenant identifier. */
|
|
567
|
+
meta?: ReactNode;
|
|
568
|
+
/** Optional owned mark/avatar. When omitted, OrgSwitcher renders the first name character. */
|
|
569
|
+
avatar?: ReactNode;
|
|
570
|
+
/**
|
|
571
|
+
* Status/plan affordance rendered end-aligned in the expanded trigger and in the menu row
|
|
572
|
+
* (e.g. `<Badge tone="warning">Trial</Badge>`). Hidden in the collapsed rail, which only has room
|
|
573
|
+
* for the mark. Pair a non-textual badge with {@link OrgSwitcherOrganization.badgeLabel}.
|
|
574
|
+
*/
|
|
575
|
+
badge?: ReactNode;
|
|
576
|
+
/**
|
|
577
|
+
* Localized screen-reader text for `badge`. Required whenever the badge carries meaning the
|
|
578
|
+
* accessible name would otherwise lose (WCAG 1.1.1 / 1.4.1): the trigger's `aria-label` owns its
|
|
579
|
+
* accessible name, so the badge is announced through `aria-describedby` instead. When omitted, a
|
|
580
|
+
* textual badge is still announced inside the menu row but NOT on the trigger.
|
|
581
|
+
*/
|
|
582
|
+
badgeLabel?: string;
|
|
583
|
+
disabled?: boolean;
|
|
584
|
+
};
|
|
585
|
+
/** Localized copy owned by the consuming product, never hard-coded by the component. */
|
|
586
|
+
export type OrgSwitcherLabels = {
|
|
587
|
+
trigger: (organizationName: string) => string;
|
|
588
|
+
title: string;
|
|
589
|
+
search: string;
|
|
590
|
+
empty: string;
|
|
591
|
+
loading: string;
|
|
592
|
+
retry?: string;
|
|
593
|
+
};
|
|
594
|
+
/** @see OrgSwitcher */
|
|
595
|
+
export type OrgSwitcherProp = {
|
|
596
|
+
organizations: readonly OrgSwitcherOrganization[];
|
|
597
|
+
value?: string;
|
|
598
|
+
onValueChange?: (value: string) => void;
|
|
599
|
+
collapsed?: boolean;
|
|
600
|
+
disabled?: boolean;
|
|
601
|
+
loading?: boolean;
|
|
602
|
+
/** Error content replaces the list while preserving the trigger and retry affordance. */
|
|
603
|
+
error?: ReactNode;
|
|
604
|
+
onRetry?: () => void;
|
|
605
|
+
labels: OrgSwitcherLabels;
|
|
606
|
+
/**
|
|
607
|
+
* `"auto"` (default) uses the desktop popover above `--sheet-responsive-breakpoint-width` and a
|
|
608
|
+
* focus-trapped bottom Sheet at/below it — the SAME token that drives `SheetContent
|
|
609
|
+
* responsive="auto"`, resolved through the shared `useSheetResponsiveMode()` hook, so a service
|
|
610
|
+
* moves the drawer line once for every overlay instead of per component.
|
|
611
|
+
* Explicit modes are useful for deterministic embedded surfaces and component tests.
|
|
612
|
+
*/
|
|
613
|
+
responsive?: "auto" | "popover" | "sheet";
|
|
614
|
+
open?: boolean;
|
|
615
|
+
onOpenChange?: (open: boolean) => void;
|
|
616
|
+
className?: ClassNameProp;
|
|
617
|
+
};
|
|
169
618
|
/** @see Sidebar */
|
|
170
619
|
export type SidebarProp = {
|
|
171
620
|
/** Accessible navigation landmark name; make it unique when multiple sidebars share a document. */
|
|
@@ -179,12 +628,39 @@ export type SidebarProp = {
|
|
|
179
628
|
collapsed?: boolean;
|
|
180
629
|
children?: ChildrenProp;
|
|
181
630
|
/**
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
631
|
+
* THE framework-router contract (gh#213). Supply only the LINK ELEMENT TYPE; the Sidebar still
|
|
632
|
+
* composes the row — icon slot, label, badge, `data-active`/`aria-current`, the icon-only
|
|
633
|
+
* collapsed rail and its tooltip name — and passes it as {@link SidebarLinkProp} `children`.
|
|
634
|
+
* Used for every row that carries an `href`: top-level leaves, submenu children, collapsed-rail
|
|
635
|
+
* leaves and collapsed flyout entries. A group TRIGGER stays a `<button>` (it owns
|
|
636
|
+
* `aria-expanded` disclosure semantics per WAI-ARIA APG); its children take the link.
|
|
637
|
+
*
|
|
638
|
+
* Rows without an `href` keep the `<button>` + `onSelect(id)` shape — a router link with no
|
|
639
|
+
* destination is not a link.
|
|
640
|
+
*
|
|
641
|
+
* @example
|
|
642
|
+
* ```tsx
|
|
643
|
+
* // Inertia — its <Link href> already matches SidebarLinkProp.
|
|
644
|
+
* import { Link } from "@inertiajs/react";
|
|
645
|
+
* <Sidebar linkComponent={inertiaSidebarLink(Link)} sections={sections} activeId={activeId} />
|
|
646
|
+
*
|
|
647
|
+
* // React Router / TanStack — remap `href` to `to`.
|
|
648
|
+
* import { Link } from "react-router-dom";
|
|
649
|
+
* <Sidebar linkComponent={createSidebarLink(Link, "to")} sections={sections} activeId={activeId} />
|
|
650
|
+
* ```
|
|
651
|
+
*/
|
|
652
|
+
linkComponent?: SidebarLinkComponentProp;
|
|
653
|
+
/**
|
|
654
|
+
* @deprecated Use {@link SidebarProp.linkComponent} (or `SidebarItem asChild`) instead — there the
|
|
655
|
+
* LIBRARY composes the row and you supply only the element, so icons/labels/badges cannot be lost.
|
|
656
|
+
*
|
|
657
|
+
* Legacy escape hatch: return a SINGLE interactive element and the Sidebar merges the row styling
|
|
658
|
+
* + active state onto it via Slot. Because row CONTENT stayed consumer-authored, a
|
|
659
|
+
* `<Link>{item.label}</Link>` silently dropped every icon and badge (the gh#213 production
|
|
660
|
+
* regression). `rowProps` now also carries the composed `children`, so spreading it restores the
|
|
661
|
+
* canonical row.
|
|
186
662
|
*/
|
|
187
|
-
renderItem?: (item: SidebarItemData) => ReactNode;
|
|
663
|
+
renderItem?: (item: SidebarItemData, rowProps: SidebarRenderItemProp) => ReactNode;
|
|
188
664
|
footer?: ReactNode;
|
|
189
665
|
/**
|
|
190
666
|
* Override the nav landmark's accessible name. Defaults to a localized "Main navigation".
|
|
@@ -211,3 +687,77 @@ export type TopbarProp = Omit<React.HTMLAttributes<HTMLDivElement>, "children">
|
|
|
211
687
|
/** Escape hatch — render fully custom bar content instead of the three slots. */
|
|
212
688
|
children?: ReactNode;
|
|
213
689
|
};
|
|
690
|
+
/**
|
|
691
|
+
* @see LegalDocumentShell — one entry of the table of contents + the matching document section.
|
|
692
|
+
* `id` is the REAL anchor target (`href="#{id}"`, `<section id>`), so it must be unique on the page
|
|
693
|
+
* and URL-safe: it is what a deep link, a hash jump and `aria-current` all key off.
|
|
694
|
+
*/
|
|
695
|
+
export type LegalDocumentSectionProp = {
|
|
696
|
+
/** URL-safe anchor id — the `<section id>` AND the contents `href="#…"` target. */
|
|
697
|
+
id: string;
|
|
698
|
+
/** Section heading text — rendered as an `<h2>` AND reused as the contents-list label. */
|
|
699
|
+
title: string;
|
|
700
|
+
/** Section body. Consumer-owned legal copy: paragraphs, lists, tables, nested `<h3>`s. */
|
|
701
|
+
content: ReactNode;
|
|
702
|
+
};
|
|
703
|
+
/**
|
|
704
|
+
* @see LegalDocumentShell — the long-form legal/policy document surface (terms of service, privacy
|
|
705
|
+
* policy, DPA, cookie policy, SLA, EULA) with a table of contents.
|
|
706
|
+
*
|
|
707
|
+
* It owns the parts an app must NOT re-implement: the readable measure + top-aligned document
|
|
708
|
+
* geometry, the sticky contents rail (single-column compact block below 56rem), scroll-spy
|
|
709
|
+
* active-section tracking, hash deep-linking with a token-driven scroll offset, focus handoff to
|
|
710
|
+
* the target `<section>`, and `prefers-reduced-motion`-aware smooth scrolling. All legal TEXT stays
|
|
711
|
+
* owned by the consumer — the shell only receives it through `sections` and the slots.
|
|
712
|
+
*
|
|
713
|
+
* Semantics: `<article>` labelled by the document title · a NAMED `<nav>` for the contents · REAL
|
|
714
|
+
* `<a href="#…">` anchors carrying `aria-current="location"` · one `<section>` per entry, labelled
|
|
715
|
+
* by its `<h2>`.
|
|
716
|
+
*/
|
|
717
|
+
export type LegalDocumentShellProp = {
|
|
718
|
+
/** Document title — the `<h1>` that names the `<article>` (e.g. "Terms of Service"). */
|
|
719
|
+
title: TitleProp;
|
|
720
|
+
/**
|
|
721
|
+
* Document version identifier (e.g. `"2.4"`). Rendered as a localized "Version {version}" line —
|
|
722
|
+
* pass the bare identifier, never a pre-localized sentence.
|
|
723
|
+
*/
|
|
724
|
+
version?: string;
|
|
725
|
+
/**
|
|
726
|
+
* Effective date as an **ISO 8601** calendar date (`yyyy-MM-dd`) or a full ISO instant. Formatted
|
|
727
|
+
* for display with `Intl.DateTimeFormat` in the active locale and emitted inside a
|
|
728
|
+
* `<time dateTime={effectiveDate}>`, so the machine-readable value is always the ISO input.
|
|
729
|
+
* NEVER pass a pre-formatted string.
|
|
730
|
+
*/
|
|
731
|
+
effectiveDate?: string;
|
|
732
|
+
/** Short plain-language summary rendered under the metadata, above the contents. */
|
|
733
|
+
summary?: ReactNode;
|
|
734
|
+
/**
|
|
735
|
+
* Accessible name + visible caption of the contents `<nav>` (e.g. "Contents"). Defaults to a
|
|
736
|
+
* localized "Contents"; override it when two documents render in the same view, so the two `nav`
|
|
737
|
+
* landmarks stay distinguishable (axe `landmark-unique`, WCAG 2.4.1).
|
|
738
|
+
*/
|
|
739
|
+
contentsLabel?: string;
|
|
740
|
+
/** The document's sections, in reading order. Drives BOTH the contents list and the body. */
|
|
741
|
+
sections: LegalDocumentSectionProp[];
|
|
742
|
+
/**
|
|
743
|
+
* Controlled active section id (the entry marked `aria-current="location"`). Pair it with
|
|
744
|
+
* `onActiveSectionChange`; omit both for the uncontrolled form.
|
|
745
|
+
*/
|
|
746
|
+
activeSection?: string;
|
|
747
|
+
/** Uncontrolled initial active section id. Defaults to the first section. */
|
|
748
|
+
defaultActiveSection?: string;
|
|
749
|
+
/**
|
|
750
|
+
* Fires whenever the active section changes — on a contents-anchor activation, on an initial
|
|
751
|
+
* hash deep link, and continuously from the scroll spy as the reader moves through the document.
|
|
752
|
+
*/
|
|
753
|
+
onActiveSectionChange?: (sectionId: string) => void;
|
|
754
|
+
/**
|
|
755
|
+
* Slot above the contents list in the rail — a document switcher across the legal set
|
|
756
|
+
* (Terms · Privacy · Cookies). Rendered as a plain wrapper, so the consumer owns its semantics.
|
|
757
|
+
*/
|
|
758
|
+
documentNavigation?: ReactNode;
|
|
759
|
+
/** Slot below the last section — the accept/download/print/contact actions. */
|
|
760
|
+
footerAction?: ReactNode;
|
|
761
|
+
id?: IdProp;
|
|
762
|
+
className?: ClassNameProp;
|
|
763
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";
|