@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,218 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Check, ChevronsUpDown, Loader2, RotateCcw } from "lucide-react";
5
+ import { cn } from "../../lib/utils.js";
6
+ import { Popover, PopoverContent, PopoverTrigger } from "../data-display/popover.js";
7
+ import {
8
+ Command,
9
+ CommandEmpty,
10
+ CommandGroup,
11
+ CommandInput,
12
+ CommandItem,
13
+ CommandList
14
+ } from "../data-entry/command.js";
15
+ import {
16
+ Sheet,
17
+ SheetBody,
18
+ SheetContent,
19
+ SheetHeader,
20
+ SheetTrigger,
21
+ useSheetResponsiveMode
22
+ } from "../feedback/sheet.js";
23
+ import { Button } from "../general/button.js";
24
+ function OrganizationMark({ organization }) {
25
+ return /* @__PURE__ */ jsx("span", { className: "ui-org-switcher-avatar", "aria-hidden": "true", children: organization?.avatar ?? organization?.name.trim().charAt(0).toUpperCase() ?? "?" });
26
+ }
27
+ function OrganizationBadge({ organization }) {
28
+ if (organization.badge == null) return null;
29
+ return /* @__PURE__ */ jsx(
30
+ "span",
31
+ {
32
+ "data-slot": "org-switcher-badge",
33
+ className: "shrink-0",
34
+ "aria-hidden": organization.badgeLabel != null ? "true" : void 0,
35
+ children: organization.badge
36
+ }
37
+ );
38
+ }
39
+ const OrgSwitcherTrigger = React.forwardRef(
40
+ ({ organization, collapsed, disabled, label, ...props }, ref) => {
41
+ const reactId = React.useId();
42
+ const badgeDescriptionId = !collapsed && organization?.badge != null && organization.badgeLabel != null ? `${reactId}-badge` : void 0;
43
+ return /* @__PURE__ */ jsxs(
44
+ Button,
45
+ {
46
+ ref,
47
+ type: "button",
48
+ variant: "ghost",
49
+ className: "ui-org-switcher-trigger",
50
+ "data-collapsed": collapsed ? "true" : void 0,
51
+ disabled,
52
+ "aria-label": label,
53
+ "aria-describedby": badgeDescriptionId,
54
+ ...props,
55
+ children: [
56
+ /* @__PURE__ */ jsx(OrganizationMark, { organization }),
57
+ !collapsed ? /* @__PURE__ */ jsxs(Fragment, { children: [
58
+ /* @__PURE__ */ jsxs("span", { className: "ui-org-switcher-current", children: [
59
+ /* @__PURE__ */ jsx("span", { className: "ui-org-switcher-name", children: organization?.name ?? label }),
60
+ organization?.meta != null ? /* @__PURE__ */ jsx("span", { className: "ui-org-switcher-meta", children: organization.meta }) : null
61
+ ] }),
62
+ organization != null ? /* @__PURE__ */ jsx(OrganizationBadge, { organization }) : null,
63
+ badgeDescriptionId != null ? /* @__PURE__ */ jsx("span", { id: badgeDescriptionId, className: "sr-only", children: organization?.badgeLabel }) : null,
64
+ /* @__PURE__ */ jsx(ChevronsUpDown, { className: "ui-org-switcher-chevron", "aria-hidden": "true" })
65
+ ] }) : null
66
+ ]
67
+ }
68
+ );
69
+ }
70
+ );
71
+ OrgSwitcherTrigger.displayName = "OrgSwitcherTrigger";
72
+ function OrgSwitcherPanel({
73
+ organizations,
74
+ value,
75
+ onValueChange,
76
+ loading,
77
+ error,
78
+ onRetry,
79
+ labels,
80
+ close
81
+ }) {
82
+ if (loading) {
83
+ return /* @__PURE__ */ jsxs("div", { className: "ui-org-switcher-state", role: "status", children: [
84
+ /* @__PURE__ */ jsx(Loader2, { className: "ui-org-switcher-spinner", "aria-hidden": "true" }),
85
+ /* @__PURE__ */ jsx("span", { children: labels.loading })
86
+ ] });
87
+ }
88
+ if (error != null) {
89
+ return /* @__PURE__ */ jsxs("div", { className: "ui-org-switcher-state", role: "alert", children: [
90
+ /* @__PURE__ */ jsx("span", { children: error }),
91
+ onRetry != null ? /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: onRetry, children: [
92
+ /* @__PURE__ */ jsx(RotateCcw, { "aria-hidden": "true" }),
93
+ labels.retry
94
+ ] }) : null
95
+ ] });
96
+ }
97
+ return /* @__PURE__ */ jsxs(Command, { label: labels.title, className: "ui-org-switcher-command", children: [
98
+ /* @__PURE__ */ jsx(CommandInput, { placeholder: labels.search, "aria-label": labels.search }),
99
+ /* @__PURE__ */ jsxs(CommandList, { children: [
100
+ /* @__PURE__ */ jsx(CommandEmpty, { children: labels.empty }),
101
+ /* @__PURE__ */ jsx(CommandGroup, { children: organizations.map((organization) => {
102
+ const selected = organization.id === value;
103
+ return /* @__PURE__ */ jsxs(
104
+ CommandItem,
105
+ {
106
+ value: `${organization.name} ${String(organization.meta ?? "")}`,
107
+ keywords: organization.badgeLabel != null ? [organization.id, organization.badgeLabel] : [organization.id],
108
+ disabled: organization.disabled,
109
+ onSelect: () => {
110
+ onValueChange?.(organization.id);
111
+ close();
112
+ },
113
+ children: [
114
+ /* @__PURE__ */ jsx(OrganizationMark, { organization }),
115
+ /* @__PURE__ */ jsxs("span", { className: "ui-org-switcher-option", children: [
116
+ /* @__PURE__ */ jsx("span", { className: "ui-org-switcher-name", children: organization.name }),
117
+ organization.meta != null ? /* @__PURE__ */ jsx("span", { className: "ui-org-switcher-meta", children: organization.meta }) : null
118
+ ] }),
119
+ /* @__PURE__ */ jsx(OrganizationBadge, { organization }),
120
+ organization.badge != null && organization.badgeLabel != null ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: organization.badgeLabel }) : null,
121
+ selected ? /* @__PURE__ */ jsxs(Fragment, { children: [
122
+ /* @__PURE__ */ jsx(Check, { className: "ui-org-switcher-check", "aria-hidden": "true" }),
123
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
124
+ "(",
125
+ labels.title,
126
+ ")"
127
+ ] })
128
+ ] }) : null
129
+ ]
130
+ },
131
+ organization.id
132
+ );
133
+ }) })
134
+ ] })
135
+ ] });
136
+ }
137
+ function OrgSwitcher({
138
+ organizations,
139
+ value,
140
+ onValueChange,
141
+ collapsed = false,
142
+ disabled = false,
143
+ loading = false,
144
+ error,
145
+ onRetry,
146
+ labels,
147
+ responsive = "auto",
148
+ open,
149
+ onOpenChange,
150
+ className
151
+ }) {
152
+ const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);
153
+ const controlled = open !== void 0;
154
+ const resolvedOpen = controlled ? open : uncontrolledOpen;
155
+ const setOpen = React.useCallback(
156
+ (nextOpen) => {
157
+ if (!controlled) {
158
+ setUncontrolledOpen(nextOpen);
159
+ }
160
+ onOpenChange?.(nextOpen);
161
+ },
162
+ [controlled, onOpenChange]
163
+ );
164
+ const compactViewport = useSheetResponsiveMode("auto") === "bottom";
165
+ const sheet = responsive === "sheet" || responsive === "auto" && compactViewport;
166
+ const current = organizations.find((organization) => organization.id === value);
167
+ const fallbackName = current?.name ?? labels.title;
168
+ const triggerLabel = labels.trigger(fallbackName);
169
+ const triggerDisabled = disabled;
170
+ const trigger = /* @__PURE__ */ jsx(
171
+ OrgSwitcherTrigger,
172
+ {
173
+ organization: current,
174
+ collapsed,
175
+ disabled: triggerDisabled,
176
+ label: triggerLabel
177
+ }
178
+ );
179
+ const panel = /* @__PURE__ */ jsx(
180
+ OrgSwitcherPanel,
181
+ {
182
+ organizations,
183
+ value,
184
+ onValueChange,
185
+ loading,
186
+ error,
187
+ onRetry,
188
+ labels,
189
+ close: () => setOpen(false)
190
+ }
191
+ );
192
+ if (sheet) {
193
+ return /* @__PURE__ */ jsx("div", { className: cn("ui-org-switcher", className), "data-collapsed": collapsed || void 0, children: /* @__PURE__ */ jsxs(Sheet, { open: resolvedOpen, onOpenChange: setOpen, children: [
194
+ /* @__PURE__ */ jsx(SheetTrigger, { asChild: true, children: trigger }),
195
+ /* @__PURE__ */ jsxs(
196
+ SheetContent,
197
+ {
198
+ responsive: "bottom",
199
+ className: "ui-org-switcher-sheet",
200
+ style: {
201
+ "--sheet-bottom-max-height": "var(--org-switcher-sheet-max-height)"
202
+ },
203
+ children: [
204
+ /* @__PURE__ */ jsx(SheetHeader, { title: labels.title }),
205
+ /* @__PURE__ */ jsx(SheetBody, { children: panel })
206
+ ]
207
+ }
208
+ )
209
+ ] }) });
210
+ }
211
+ return /* @__PURE__ */ jsx("div", { className: cn("ui-org-switcher", className), "data-collapsed": collapsed || void 0, children: /* @__PURE__ */ jsxs(Popover, { open: resolvedOpen, onOpenChange: setOpen, children: [
212
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: trigger }),
213
+ /* @__PURE__ */ jsx(PopoverContent, { align: "start", className: "ui-org-switcher-popover", "aria-label": labels.title, children: panel })
214
+ ] }) });
215
+ }
216
+ export {
217
+ OrgSwitcher
218
+ };
@@ -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, stickyFooter, footerReveal, fill, children, className, }: PageContainerProp): import("react").JSX.Element;
5
+ declare function PageContainerRoot({ title, subtitle, extra, footer, breadcrumb, breadcrumbLabel, breadcrumbAriaLabel, linkComponent: LinkComponent, density, variant, preset, headerLayout, measure, stickyFooter, footerReveal, fill, children, className, }: PageContainerProp): import("react").JSX.Element;
6
6
  export declare const PageContainer: typeof PageContainerRoot & {
7
7
  Inset: typeof PageContainerInset;
8
8
  };
@@ -44,6 +44,9 @@ function PageContainerRoot({
44
44
  linkComponent: LinkComponent = "a",
45
45
  density,
46
46
  variant = "default",
47
+ preset = "default",
48
+ headerLayout = "stack",
49
+ measure = "default",
47
50
  stickyFooter = false,
48
51
  footerReveal = "always",
49
52
  fill = false,
@@ -56,6 +59,8 @@ function PageContainerRoot({
56
59
  return /* @__PURE__ */ jsxs(
57
60
  "div",
58
61
  {
62
+ "data-preset": preset,
63
+ "data-measure": measure,
59
64
  "data-revealed": revealed ? "true" : void 0,
60
65
  className: cn(
61
66
  "ui-page-container",
@@ -69,7 +74,7 @@ function PageContainerRoot({
69
74
  className
70
75
  ),
71
76
  children: [
72
- /* @__PURE__ */ jsxs("header", { ref: headerRef, className: "ui-page-header", children: [
77
+ /* @__PURE__ */ jsxs("header", { ref: headerRef, className: "ui-page-header", "data-layout": headerLayout, children: [
73
78
  breadcrumb && breadcrumb.length > 0 && /* @__PURE__ */ jsx(
74
79
  "nav",
75
80
  {
@@ -100,7 +105,7 @@ function PageContainerRoot({
100
105
  }
101
106
  ),
102
107
  /* @__PURE__ */ jsxs("div", { className: "ui-page-header-row", children: [
103
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
108
+ /* @__PURE__ */ jsxs("div", { className: "ui-page-header-heading min-w-0", children: [
104
109
  /* @__PURE__ */ jsx("h1", { className: "ui-page-title", children: title }),
105
110
  subtitle && /* @__PURE__ */ jsx("p", { className: "ui-page-subtitle", children: subtitle })
106
111
  ] }),
@@ -0,0 +1,43 @@
1
+ import * as React from "react";
2
+ import type { SidebarLinkComponentProp } from "../../props/components/layout.prop.js";
3
+ /**
4
+ * Adapt a framework router `Link` to {@link SidebarLinkComponentProp} (gh#213) — a dependency-free
5
+ * factory, so `@godxjp/ui` still ships ZERO router/framework dependencies.
6
+ *
7
+ * It lives in its own module (imports only React + the prop types) so `@godxjp/ui/inertia` can build
8
+ * on it without pulling the whole Sidebar — and its Collapsible/Popover/Tooltip tree — into a
9
+ * form-only consumer's bundle.
10
+ *
11
+ * It normalizes only the two things that differ between routers:
12
+ * 1. **Which prop carries the destination** — `"href"` (Inertia, Next.js) or `"to"` (React Router,
13
+ * TanStack Router).
14
+ * 2. **A row with no destination** — a disabled row must never mount a router link (Inertia and
15
+ * React Router both require a string destination and would navigate to `""`); it falls back to an
16
+ * inert `<a>` that keeps the canonical row class and `aria-disabled`. That fallback carries an
17
+ * explicit `role="link"`: an `<a>` without `href` has NO implicit role, so a screen reader would
18
+ * announce it as plain text and `aria-label` would be a prohibited attribute — which is exactly
19
+ * how the icon-only collapsed rail (where the label lives on `aria-label`) would have lost the
20
+ * name of every disabled row. Callers that supply their own role — the collapsed flyout's
21
+ * `menuitem` — still win, because `rest` is spread after it.
22
+ *
23
+ * Everything else — the icon, the label, the badge, `data-active`/`aria-current`, the collapsed
24
+ * rail's `aria-label` — is composed by the LIBRARY and arrives as `children`. The consumer never
25
+ * reconstructs row markup, which is what makes the reported "every sidebar icon disappeared"
26
+ * regression impossible.
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * // React Router / TanStack Router — destination prop is `to`.
31
+ * import { Link } from "react-router-dom";
32
+ * const RouterLink = createSidebarLink(Link, "to");
33
+ * <Sidebar linkComponent={RouterLink} sections={sections} activeId={activeId} />
34
+ *
35
+ * // Inertia / Next.js — destination prop is `href` (the default).
36
+ * import { Link } from "@inertiajs/react";
37
+ * const InertiaLink = createSidebarLink(Link);
38
+ * ```
39
+ */
40
+ export declare function createSidebarLink<P extends {
41
+ className?: string;
42
+ children?: React.ReactNode;
43
+ }>(Link: React.ComponentType<P>, hrefProp?: "href" | "to"): SidebarLinkComponentProp;
@@ -0,0 +1,13 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ function createSidebarLink(Link, hrefProp = "href") {
4
+ const Adapter = React.forwardRef(function SidebarLinkAdapter({ href, ...rest }, ref) {
5
+ if (!href) return /* @__PURE__ */ jsx("a", { ref, role: "link", ...rest });
6
+ const routed = { [hrefProp]: href, ...rest, ref };
7
+ return /* @__PURE__ */ jsx(Link, { ...routed });
8
+ });
9
+ return Adapter;
10
+ }
11
+ export {
12
+ createSidebarLink
13
+ };
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
- import type { SidebarItemData, SidebarProp } from "../../props/components/layout.prop.js";
3
- export type { SidebarItemData, SidebarProductProp as SidebarProduct, SidebarProp, SidebarProp as SidebarProps, } from "../../props/components/layout.prop.js";
4
- type RenderItem = (item: SidebarItemData) => React.ReactNode;
2
+ import type { SidebarItemData, SidebarLinkComponentProp, SidebarProp, SidebarRenderItemProp } from "../../props/components/layout.prop.js";
3
+ export type { SidebarItemData, SidebarLinkComponentProp, SidebarLinkProp, SidebarProductProp as SidebarProduct, SidebarProp, SidebarProp as SidebarProps, SidebarRenderItemProp, } from "../../props/components/layout.prop.js";
4
+ type RenderItem = (item: SidebarItemData, rowProps: SidebarRenderItemProp) => React.ReactNode;
5
5
  type SidebarHeaderProps = React.HTMLAttributes<HTMLDivElement>;
6
6
  type SidebarSectionProps = {
7
7
  label?: string;
@@ -13,9 +13,46 @@ type SidebarItemProps = {
13
13
  active?: boolean;
14
14
  sub?: boolean;
15
15
  onActivate?: (id: string) => void;
16
+ /**
17
+ * Framework-router link element TYPE (gh#213). The row content stays library-composed — see
18
+ * {@link SidebarLinkProp}. Applied only when `item.href` is set.
19
+ */
20
+ linkComponent?: SidebarLinkComponentProp;
21
+ /**
22
+ * Radix-style element swap: the single child element (a router `<Link>`) BECOMES the row and the
23
+ * library injects its composed icon + label + badge as that element's children (gh#213). Use it
24
+ * when you compose `SidebarItem` by hand instead of passing `sections` + `linkComponent`.
25
+ */
26
+ asChild?: boolean;
27
+ /** @deprecated Use `linkComponent` / `asChild` — see {@link SidebarProp.renderItem}. */
16
28
  renderItem?: RenderItem;
17
29
  };
18
30
  export declare function SidebarHeader({ children, className, ...props }: SidebarHeaderProps): React.JSX.Element;
19
31
  export declare function SidebarSection({ label, collapsed, children, className, ...props }: SidebarSectionProps & React.HTMLAttributes<HTMLDivElement>): React.JSX.Element;
20
- export declare function SidebarItem({ item, active, sub, onActivate, renderItem, children, ...props }: SidebarItemProps & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onClick">): React.JSX.Element;
21
- export declare function Sidebar({ ariaLabel: ariaLabelCamel, activeId, onSelect, sections, product, onProductClick, brand, collapsed, children, renderItem, footer, "aria-label": ariaLabel, }: SidebarProp): React.JSX.Element;
32
+ export declare function SidebarItem({ item, active, sub, onActivate, linkComponent: LinkComponent, asChild, renderItem, children, ...props }: SidebarItemProps & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onClick">): React.JSX.Element;
33
+ export { createSidebarLink } from "./sidebar-link.js";
34
+ /**
35
+ * Sidebar — data-driven vertical nav rail.
36
+ *
37
+ * ROUTER LINKS (gh#213): pass `linkComponent` and the LIBRARY keeps composing every row (icon slot,
38
+ * label, badge, active state, the icon-only collapsed rail and its tooltip name) — the consumer
39
+ * supplies only the link ELEMENT. Use {@link createSidebarLink} to adapt a router `Link`, or
40
+ * `SidebarItem asChild` when composing rows by hand. `renderItem` is DEPRECATED: it left row content
41
+ * to the consumer, so a `<Link>{item.label}</Link>` dropped every icon and badge.
42
+ *
43
+ * THEMING (gh#228): the row/label and the nav ICON read SEPARATE component tokens, so a service can
44
+ * match a canonical shell's darker 16px icons without page-local CSS and without re-tinting every
45
+ * muted text globally:
46
+ *
47
+ * - `--sidebar-nav-item-foreground` (row + label, incl. sub rows) · default `hsl(var(--muted-foreground))`
48
+ * - `--sidebar-nav-item-hover-foreground` · default `hsl(var(--foreground))`
49
+ * - `--sidebar-nav-item-disabled-foreground` · default = the resting row colour
50
+ * - `--sidebar-nav-icon-foreground` (`.sb-icon`, incl. the collapsed rail + group triggers) ·
51
+ * default `currentColor` = the row colour
52
+ * - `--sidebar-nav-icon-{hover,active,disabled}-foreground` · each defaults to the base icon knob
53
+ *
54
+ * The active row keeps `--sidebar-item-active-background` / `--sidebar-item-active-foreground`.
55
+ * These are COLOUR knobs only — icon size stays `--sidebar-nav-icon-size` (16px) and row geometry
56
+ * stays `--sidebar-nav-item-height` / `-gap` / `-padding-x`.
57
+ */
58
+ export declare function Sidebar({ ariaLabel: ariaLabelCamel, activeId, onSelect, sections, product, onProductClick, brand, collapsed, children, linkComponent, renderItem, footer, "aria-label": ariaLabel, }: SidebarProp): React.JSX.Element;