@godxjp/ui 18.3.0 → 18.5.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 (131) hide show
  1. package/README.md +18 -3
  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 +29 -0
  9. package/dist/components/charts/compact-bar-trend.js +74 -0
  10. package/dist/components/charts/index.d.ts +3 -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 +16 -1
  15. package/dist/components/data-display/data-table.js +56 -0
  16. package/dist/components/data-display/index.d.ts +3 -1
  17. package/dist/components/data-display/index.js +10 -1
  18. package/dist/components/data-display/list-row.d.ts +22 -4
  19. package/dist/components/data-display/list-row.js +21 -3
  20. package/dist/components/data-display/service-launcher-card.d.ts +40 -0
  21. package/dist/components/data-display/service-launcher-card.js +97 -0
  22. package/dist/components/data-entry/command-palette.d.ts +37 -0
  23. package/dist/components/data-entry/command-palette.js +125 -0
  24. package/dist/components/data-entry/index.d.ts +2 -0
  25. package/dist/components/data-entry/index.js +2 -0
  26. package/dist/components/feedback/dialog.d.ts +2 -0
  27. package/dist/components/feedback/dialog.js +42 -33
  28. package/dist/components/feedback/index.d.ts +4 -1
  29. package/dist/components/feedback/index.js +6 -2
  30. package/dist/components/feedback/sheet.d.ts +26 -4
  31. package/dist/components/feedback/sheet.js +54 -7
  32. package/dist/components/feedback/two-factor-setup.d.ts +31 -0
  33. package/dist/components/feedback/two-factor-setup.js +97 -0
  34. package/dist/components/general/index.d.ts +1 -1
  35. package/dist/components/general/logo.d.ts +42 -8
  36. package/dist/components/general/logo.js +67 -14
  37. package/dist/components/layout/app-shell.js +22 -6
  38. package/dist/components/layout/auth-divider.d.ts +7 -0
  39. package/dist/components/layout/auth-divider.js +21 -0
  40. package/dist/components/layout/auth-footer.d.ts +5 -0
  41. package/dist/components/layout/auth-footer.js +24 -0
  42. package/dist/components/layout/auth-identity.d.ts +5 -0
  43. package/dist/components/layout/auth-identity.js +17 -0
  44. package/dist/components/layout/auth-shell.d.ts +8 -1
  45. package/dist/components/layout/auth-shell.js +25 -6
  46. package/dist/components/layout/auth-stack.d.ts +4 -0
  47. package/dist/components/layout/auth-stack.js +8 -0
  48. package/dist/components/layout/centered-shell.d.ts +5 -2
  49. package/dist/components/layout/centered-shell.js +2 -1
  50. package/dist/components/layout/index.d.ts +18 -3
  51. package/dist/components/layout/index.js +17 -2
  52. package/dist/components/layout/legal-document-shell.d.ts +34 -0
  53. package/dist/components/layout/legal-document-shell.js +186 -0
  54. package/dist/components/layout/master-detail.d.ts +24 -0
  55. package/dist/components/layout/master-detail.js +48 -0
  56. package/dist/components/layout/org-switcher.d.ts +9 -0
  57. package/dist/components/layout/org-switcher.js +218 -0
  58. package/dist/components/layout/page-container.d.ts +1 -1
  59. package/dist/components/layout/page-container.js +3 -2
  60. package/dist/components/layout/sidebar-link.d.ts +43 -0
  61. package/dist/components/layout/sidebar-link.js +13 -0
  62. package/dist/components/layout/sidebar.d.ts +42 -5
  63. package/dist/components/layout/sidebar.js +170 -85
  64. package/dist/components/navigation/app-setting-picker.js +17 -6
  65. package/dist/components/navigation/filter-bar.d.ts +12 -3
  66. package/dist/components/navigation/filter-bar.js +8 -2
  67. package/dist/components/navigation/index.d.ts +2 -2
  68. package/dist/components/navigation/index.js +3 -1
  69. package/dist/components/navigation/tabs-scroll.d.ts +70 -0
  70. package/dist/components/navigation/tabs-scroll.js +76 -0
  71. package/dist/components/navigation/tabs.js +35 -20
  72. package/dist/email/brand-mark.d.ts +92 -0
  73. package/dist/email/brand-mark.js +83 -0
  74. package/dist/email/color.d.ts +52 -0
  75. package/dist/email/color.js +37 -0
  76. package/dist/email/geometry.d.ts +112 -0
  77. package/dist/email/geometry.js +89 -0
  78. package/dist/email/index.d.ts +78 -0
  79. package/dist/email/index.js +76 -0
  80. package/dist/email/inline-style.d.ts +25 -0
  81. package/dist/email/inline-style.js +21 -0
  82. package/dist/email/tokens.generated.d.ts +147 -0
  83. package/dist/email/tokens.generated.js +142 -0
  84. package/dist/i18n/messages/en.json +21 -2
  85. package/dist/i18n/messages/ja.json +21 -2
  86. package/dist/i18n/messages/vi.json +21 -2
  87. package/dist/inertia/index.d.ts +50 -0
  88. package/dist/inertia/index.js +5 -0
  89. package/dist/props/components/app.prop.d.ts +11 -1
  90. package/dist/props/components/charts.prop.d.ts +40 -1
  91. package/dist/props/components/data-display.prop.d.ts +13 -1
  92. package/dist/props/components/feedback.prop.d.ts +10 -0
  93. package/dist/props/components/index.d.ts +2 -2
  94. package/dist/props/components/layout.prop.d.ts +374 -11
  95. package/dist/props/components/layout.prop.js +1 -0
  96. package/dist/props/components/navigation.prop.d.ts +19 -1
  97. package/dist/props/registry.d.ts +291 -7
  98. package/dist/props/registry.js +341 -7
  99. package/dist/props/vocabulary/index.d.ts +1 -1
  100. package/dist/props/vocabulary/interaction.prop.d.ts +1 -1
  101. package/dist/props/vocabulary/layout.prop.d.ts +18 -0
  102. package/dist/styles/base.css +9 -3
  103. package/dist/styles/card-layout.css +162 -8
  104. package/dist/styles/chart-layout.css +97 -0
  105. package/dist/styles/control.css +48 -2
  106. package/dist/styles/data-display-layout.css +52 -6
  107. package/dist/styles/dialog-layout.css +105 -4
  108. package/dist/styles/fonts.css +29 -20
  109. package/dist/styles/index.css +11 -3
  110. package/dist/styles/layout.css +368 -2
  111. package/dist/styles/logo-layout.css +62 -0
  112. package/dist/styles/navigation-layout.css +43 -0
  113. package/dist/styles/shell-layout.css +505 -62
  114. package/dist/theme/dxs.canonical.css +76 -0
  115. package/dist/tokens/axes.css +39 -0
  116. package/dist/tokens/base.css +5 -0
  117. package/dist/tokens/components/card.css +52 -5
  118. package/dist/tokens/components/chart.css +47 -0
  119. package/dist/tokens/components/control.css +16 -0
  120. package/dist/tokens/components/email.css +66 -0
  121. package/dist/tokens/components/feedback.css +17 -0
  122. package/dist/tokens/components/legal-document.css +67 -0
  123. package/dist/tokens/components/list-row.css +23 -2
  124. package/dist/tokens/components/logo.css +32 -0
  125. package/dist/tokens/components/navigation.css +13 -0
  126. package/dist/tokens/components/sheet.css +19 -0
  127. package/dist/tokens/components/shell.css +149 -1
  128. package/dist/tokens/components/sidebar.css +33 -0
  129. package/dist/tokens/foundation.css +5 -2
  130. package/dist/tokens/semantic/layout.css +44 -2
  131. package/package.json +17 -6
@@ -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;
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { Slot } from "@radix-ui/react-slot";
5
5
  import { ChevronDown } from "lucide-react";
@@ -23,72 +23,91 @@ function SidebarSection({
23
23
  /* @__PURE__ */ jsx("div", { className: "sb-nav", children })
24
24
  ] });
25
25
  }
26
+ function SidebarIcon({ icon: Icon }) {
27
+ return /* @__PURE__ */ jsx("span", { className: "sb-icon", children: Icon ? /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }) : null });
28
+ }
29
+ function SidebarRowContent({
30
+ item,
31
+ sub = false,
32
+ iconOnly = false
33
+ }) {
34
+ if (iconOnly) return /* @__PURE__ */ jsx(SidebarIcon, { icon: item.icon });
35
+ const showBadge = item.badge !== void 0 && item.badge !== "";
36
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
37
+ !sub ? /* @__PURE__ */ jsx(SidebarIcon, { icon: item.icon }) : null,
38
+ /* @__PURE__ */ jsx("span", { className: "sb-label", children: item.label }),
39
+ showBadge ? /* @__PURE__ */ jsx("span", { className: "sb-badge", children: item.badge }) : null
40
+ ] });
41
+ }
26
42
  function SidebarItem({
27
43
  item,
28
44
  active = false,
29
45
  sub = false,
30
46
  onActivate,
47
+ linkComponent: LinkComponent,
48
+ asChild = false,
31
49
  renderItem,
32
50
  children,
33
51
  ...props
34
52
  }) {
35
- const Icon = item.icon;
36
- const showBadge = item.badge !== void 0 && item.badge !== "";
37
53
  const disabled = item.disabled || props.disabled;
38
- const custom = children ?? (renderItem ? renderItem(item) : void 0);
39
54
  const rowClass = cn("sb-nav-item", sub && "sb-nav-item--sub");
40
55
  const stateProps = {
41
56
  className: rowClass,
42
57
  "data-active": active ? "true" : void 0,
43
- "aria-current": active ? "page" : void 0
58
+ "aria-current": active ? "page" : void 0,
59
+ "aria-disabled": disabled ? true : void 0
60
+ };
61
+ const content = /* @__PURE__ */ jsx(SidebarRowContent, { item, sub });
62
+ const activate = (event) => {
63
+ if (disabled) {
64
+ event.preventDefault();
65
+ return;
66
+ }
67
+ onActivate?.(item.id);
44
68
  };
69
+ if (asChild && React.isValidElement(children)) {
70
+ return /* @__PURE__ */ jsx(Slot, { ...stateProps, onClick: activate, children: React.cloneElement(children, void 0, content) });
71
+ }
72
+ const custom = children ?? (renderItem ? renderItem(item, { ...stateProps, children: content }) : void 0);
45
73
  if (custom !== void 0) {
46
- return /* @__PURE__ */ jsx(Slot, { ...stateProps, "aria-disabled": disabled || void 0, children: custom });
74
+ return /* @__PURE__ */ jsx(Slot, { ...stateProps, children: custom });
75
+ }
76
+ if (LinkComponent && item.href) {
77
+ return /* @__PURE__ */ jsx(
78
+ LinkComponent,
79
+ {
80
+ ...stateProps,
81
+ href: disabled ? void 0 : item.href,
82
+ "aria-disabled": disabled || void 0,
83
+ onClick: activate,
84
+ children: content
85
+ }
86
+ );
47
87
  }
48
88
  if (item.href) {
49
- return /* @__PURE__ */ jsxs(
89
+ return /* @__PURE__ */ jsx(
50
90
  "a",
51
91
  {
52
92
  ...props,
53
93
  href: disabled ? void 0 : item.href,
54
94
  ...stateProps,
55
95
  "aria-disabled": disabled || void 0,
56
- onClick: (event) => {
57
- if (disabled) event.preventDefault();
58
- else onActivate?.(item.id);
59
- },
60
- children: [
61
- !sub ? /* @__PURE__ */ jsx("span", { className: "sb-icon", children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }) }) : null,
62
- /* @__PURE__ */ jsx("span", { className: "sb-label", children: item.label }),
63
- showBadge ? /* @__PURE__ */ jsx("span", { className: "sb-badge", children: item.badge }) : null
64
- ]
96
+ onClick: activate,
97
+ children: content
65
98
  }
66
99
  );
67
100
  }
68
- return /* @__PURE__ */ jsxs(
69
- "button",
70
- {
71
- type: "button",
72
- ...stateProps,
73
- "aria-disabled": disabled,
74
- ...props,
75
- onClick: () => {
76
- if (disabled) return;
77
- onActivate?.(item.id);
78
- },
79
- children: [
80
- !sub ? /* @__PURE__ */ jsx("span", { className: "sb-icon", children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }) }) : null,
81
- /* @__PURE__ */ jsx("span", { className: "sb-label", children: item.label }),
82
- showBadge ? /* @__PURE__ */ jsx("span", { className: "sb-badge", children: item.badge }) : null
83
- ]
84
- }
101
+ return (
102
+ // ui-audit-disable-next-line no-raw-button — raw <button> is the correct element here.
103
+ /* @__PURE__ */ jsx("button", { type: "button", ...stateProps, "aria-disabled": disabled, ...props, onClick: activate, children: content })
85
104
  );
86
105
  }
87
106
  function isItemActive(item, activeId) {
88
107
  if (item.id === activeId) return true;
89
108
  return (item.children ?? []).some((child) => isItemActive(child, activeId));
90
109
  }
91
- function NavLeaf({ item, activeId, onSelect, sub = false, renderItem }) {
110
+ function NavLeaf({ item, activeId, onSelect, sub = false, linkComponent, renderItem }) {
92
111
  const active = item.id === activeId;
93
112
  return /* @__PURE__ */ jsx(
94
113
  SidebarItem,
@@ -97,6 +116,7 @@ function NavLeaf({ item, activeId, onSelect, sub = false, renderItem }) {
97
116
  active,
98
117
  onActivate: onSelect,
99
118
  sub,
119
+ linkComponent,
100
120
  renderItem
101
121
  }
102
122
  );
@@ -105,6 +125,7 @@ function NavLeafsInGroup({
105
125
  children,
106
126
  activeId,
107
127
  onSelect,
128
+ linkComponent,
108
129
  renderItem
109
130
  }) {
110
131
  return children.map((child) => /* @__PURE__ */ jsx(
@@ -114,13 +135,13 @@ function NavLeafsInGroup({
114
135
  activeId,
115
136
  onSelect,
116
137
  sub: true,
138
+ linkComponent,
117
139
  renderItem
118
140
  },
119
141
  child.id
120
142
  ));
121
143
  }
122
- function NavGroup({ item, activeId, onSelect, renderItem }) {
123
- const Icon = item.icon;
144
+ function NavGroup({ item, activeId, onSelect, linkComponent, renderItem }) {
124
145
  const active = isItemActive(item, activeId);
125
146
  const children = item.children ?? [];
126
147
  const [open, setOpen] = React.useState(active);
@@ -134,18 +155,25 @@ function NavGroup({ item, activeId, onSelect, renderItem }) {
134
155
  className: "sb-nav-item sb-nav-group-trigger",
135
156
  "data-active": active ? "true" : void 0,
136
157
  children: [
137
- /* @__PURE__ */ jsx("span", { className: "sb-icon", children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }) }),
138
- /* @__PURE__ */ jsx("span", { className: "sb-label", children: item.label }),
158
+ /* @__PURE__ */ jsx(SidebarRowContent, { item }),
139
159
  /* @__PURE__ */ jsx(ChevronDown, { className: "sb-chevron", "aria-hidden": "true" })
140
160
  ]
141
161
  }
142
162
  ),
143
- /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "sb-nav-sub", children: children.length > 0 ? /* @__PURE__ */ jsx(NavLeafsInGroup, { activeId, onSelect, renderItem, children }) : null }) })
163
+ /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "sb-nav-sub", children: children.length > 0 ? /* @__PURE__ */ jsx(
164
+ NavLeafsInGroup,
165
+ {
166
+ activeId,
167
+ onSelect,
168
+ linkComponent,
169
+ renderItem,
170
+ children
171
+ }
172
+ ) : null }) })
144
173
  ] });
145
174
  }
146
- function CollapsedRow({ item, activeId, onSelect }) {
175
+ function CollapsedRow({ item, activeId, onSelect, linkComponent: LinkComponent }) {
147
176
  const [menuOpen, setMenuOpen] = React.useState(false);
148
- const Icon = item.icon;
149
177
  const active = isItemActive(item, activeId);
150
178
  const children = item.children ?? [];
151
179
  const hasChildren = children.length > 0;
@@ -163,12 +191,31 @@ function CollapsedRow({ item, activeId, onSelect }) {
163
191
  onClick: () => {
164
192
  if (!hasChildren && !item.disabled) onSelect?.(item.id);
165
193
  },
166
- children: /* @__PURE__ */ jsx("span", { className: "sb-icon", children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }) })
194
+ children: /* @__PURE__ */ jsx(SidebarIcon, { icon: item.icon })
167
195
  }
168
196
  );
197
+ const iconLink = LinkComponent && !hasChildren && item.href ? /* @__PURE__ */ jsx(
198
+ LinkComponent,
199
+ {
200
+ className: "sb-nav-item",
201
+ "data-active": active ? "true" : void 0,
202
+ "aria-current": active ? "page" : void 0,
203
+ "aria-disabled": item.disabled || void 0,
204
+ "aria-label": item.label,
205
+ href: item.disabled ? void 0 : item.href,
206
+ onClick: (event) => {
207
+ if (item.disabled) {
208
+ event.preventDefault();
209
+ return;
210
+ }
211
+ onSelect?.(item.id);
212
+ },
213
+ children: /* @__PURE__ */ jsx(SidebarRowContent, { item, iconOnly: true })
214
+ }
215
+ ) : null;
169
216
  if (!hasChildren) {
170
217
  return /* @__PURE__ */ jsxs(Tooltip, { children: [
171
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: iconButton }),
218
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: iconLink ?? iconButton }),
172
219
  /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: item.label })
173
220
  ] });
174
221
  }
@@ -187,27 +234,45 @@ function CollapsedRow({ item, activeId, onSelect }) {
187
234
  className: "sb-flyout-pop",
188
235
  children: [
189
236
  /* @__PURE__ */ jsx("div", { className: "sb-flyout-title", children: item.label }),
190
- children.map((child) => /* @__PURE__ */ jsx(
191
- "button",
192
- {
193
- type: "button",
237
+ children.map((child) => {
238
+ const childActive = child.id === activeId;
239
+ const select = (event) => {
240
+ setMenuOpen(false);
241
+ if (child.disabled) {
242
+ event.preventDefault();
243
+ return;
244
+ }
245
+ onSelect?.(child.id);
246
+ };
247
+ const flyoutState = {
194
248
  role: "menuitem",
195
249
  className: "sb-nav-item",
196
- "data-active": child.id === activeId ? "true" : void 0,
197
- "aria-current": child.id === activeId ? "page" : void 0,
198
- onClick: () => {
199
- setMenuOpen(false);
200
- if (!child.disabled) onSelect?.(child.id);
250
+ "data-active": childActive ? "true" : void 0,
251
+ "aria-current": childActive ? "page" : void 0,
252
+ "aria-disabled": child.disabled || void 0
253
+ };
254
+ return LinkComponent && child.href ? /* @__PURE__ */ jsx(
255
+ LinkComponent,
256
+ {
257
+ ...flyoutState,
258
+ href: child.disabled ? void 0 : child.href,
259
+ onClick: select,
260
+ children: /* @__PURE__ */ jsx(SidebarRowContent, { item: child, sub: true })
201
261
  },
202
- children: /* @__PURE__ */ jsx("span", { className: "sb-label", children: child.label })
203
- },
204
- child.id
205
- ))
262
+ child.id
263
+ ) : (
264
+ // A collapsed-rail flyout entry is a `menuitem`-styled nav row owning `.sb-nav-item`
265
+ // composition, not a <Button> — same contract as the leaf row above.
266
+ // ui-audit-disable-next-line no-raw-button
267
+ /* @__PURE__ */ jsx("button", { type: "button", ...flyoutState, onClick: select, children: /* @__PURE__ */ jsx(SidebarRowContent, { item: child, sub: true }) }, child.id)
268
+ );
269
+ })
206
270
  ]
207
271
  }
208
272
  )
209
273
  ] });
210
274
  }
275
+ import { createSidebarLink } from "./sidebar-link.js";
211
276
  function Sidebar({
212
277
  ariaLabel: ariaLabelCamel,
213
278
  activeId,
@@ -218,6 +283,7 @@ function Sidebar({
218
283
  brand,
219
284
  collapsed = false,
220
285
  children,
286
+ linkComponent,
221
287
  renderItem,
222
288
  footer,
223
289
  "aria-label": ariaLabel
@@ -257,35 +323,53 @@ function Sidebar({
257
323
  meta
258
324
  ] });
259
325
  })() : null,
260
- /* @__PURE__ */ jsx("nav", { className: "sb-nav-scroll", "aria-label": ariaLabel ?? ariaLabelCamel ?? t("layout.sidebar.ariaLabel"), children: children ?? resolvedSections.map((section, sectionIndex) => /* @__PURE__ */ jsx(
261
- SidebarSection,
326
+ /* @__PURE__ */ jsx(
327
+ "nav",
262
328
  {
263
- label: section.label,
264
- collapsed,
265
- children: section.items.map(
266
- (item) => collapsed ? /* @__PURE__ */ jsx(CollapsedRow, { item, activeId, onSelect }, item.id) : item.children && item.children.length > 0 ? /* @__PURE__ */ jsx(
267
- NavGroup,
268
- {
269
- item,
270
- activeId,
271
- onSelect,
272
- renderItem
273
- },
274
- item.id
275
- ) : /* @__PURE__ */ jsx(
276
- NavLeaf,
277
- {
278
- item,
279
- activeId,
280
- onSelect,
281
- renderItem
282
- },
283
- item.id
284
- )
285
- )
286
- },
287
- section.label ?? sectionIndex
288
- )) }),
329
+ className: "sb-nav-scroll",
330
+ "aria-label": ariaLabel ?? ariaLabelCamel ?? t("layout.sidebar.ariaLabel"),
331
+ children: children ?? resolvedSections.map((section, sectionIndex) => /* @__PURE__ */ jsx(
332
+ SidebarSection,
333
+ {
334
+ label: section.label,
335
+ collapsed,
336
+ children: section.items.map(
337
+ (item) => collapsed ? /* @__PURE__ */ jsx(
338
+ CollapsedRow,
339
+ {
340
+ item,
341
+ activeId,
342
+ onSelect,
343
+ linkComponent
344
+ },
345
+ item.id
346
+ ) : item.children && item.children.length > 0 ? /* @__PURE__ */ jsx(
347
+ NavGroup,
348
+ {
349
+ item,
350
+ activeId,
351
+ onSelect,
352
+ linkComponent,
353
+ renderItem
354
+ },
355
+ item.id
356
+ ) : /* @__PURE__ */ jsx(
357
+ NavLeaf,
358
+ {
359
+ item,
360
+ activeId,
361
+ onSelect,
362
+ linkComponent,
363
+ renderItem
364
+ },
365
+ item.id
366
+ )
367
+ )
368
+ },
369
+ section.label ?? sectionIndex
370
+ ))
371
+ }
372
+ ),
289
373
  footer ? /* @__PURE__ */ jsx("div", { className: "sb-footer", children: footer }) : null
290
374
  ] });
291
375
  }
@@ -293,5 +377,6 @@ export {
293
377
  Sidebar,
294
378
  SidebarHeader,
295
379
  SidebarItem,
296
- SidebarSection
380
+ SidebarSection,
381
+ createSidebarLink
297
382
  };
@@ -63,7 +63,7 @@ const ARIA_KEY = {
63
63
  };
64
64
  const BRAND_NONE = "__app__";
65
65
  const AppSettingPicker = React.forwardRef(
66
- function AppSettingPicker2({ kind, appearance, className, disabled, id, name, value, onValueChange }, ref) {
66
+ function AppSettingPicker2({ kind, appearance, compact = false, className, disabled, id, name, value, onValueChange }, ref) {
67
67
  const resolvedAppearance = appearance ?? (kind === "locale" ? "icon" : "labeled");
68
68
  const ctx = useOptionalAppContext();
69
69
  const { t, locale, fallbackLocale } = useTranslation();
@@ -121,6 +121,8 @@ const AppSettingPicker = React.forwardRef(
121
121
  const unbound = current === void 0 || !handleChange;
122
122
  const Icon = ICON[kind];
123
123
  const iconOnly = resolvedAppearance === "icon";
124
+ const inline = resolvedAppearance === "inline";
125
+ const isCompact = compact && !inline;
124
126
  return /* @__PURE__ */ jsxs(
125
127
  Select,
126
128
  {
@@ -135,9 +137,9 @@ const AppSettingPicker = React.forwardRef(
135
137
  {
136
138
  ref,
137
139
  id,
138
- showIndicator: !iconOnly,
140
+ showIndicator: !iconOnly && !inline,
139
141
  className: cn(
140
- iconOnly ? (
142
+ inline ? "ui-app-setting-picker-inline h-auto min-h-0 w-auto rounded-none border-0 bg-transparent p-0 shadow-none" : iconOnly ? (
141
143
  // Structurally icon-only: drop the owned width + value spacing and square the box to
142
144
  // the density-aware --control-height tap target, centring the icon.
143
145
  "w-[length:var(--control-height)] justify-center ps-0 pe-0"
@@ -147,16 +149,25 @@ const AppSettingPicker = React.forwardRef(
147
149
  // `w-full` — so a labeled picker dropped into a narrow topbar no longer stretches to
148
150
  // fill the bar (gh#165). A form field that wants a full-width control passes
149
151
  // `className="w-full"`, which wins over `w-auto`.
150
- cn("w-auto max-w-full", TRIGGER_WIDTH[kind])
152
+ // `compact` drops the per-kind width entirely so the trigger hugs its value.
153
+ cn("w-auto max-w-full", !isCompact && TRIGGER_WIDTH[kind])
151
154
  ),
155
+ // Compact re-tiers the box through tokens (--app-setting-picker-compact-*); the height
156
+ // still comes from the official --control-height-sm tier, never a literal. The gap is a
157
+ // utility (not the class rule) so it beats SelectTrigger's own `gap-2`.
158
+ isCompact && "ui-app-setting-picker-compact gap-[length:var(--app-setting-picker-compact-gap)]",
152
159
  className
153
160
  ),
154
161
  "aria-label": t(ARIA_KEY[kind]),
155
162
  children: [
156
- /* @__PURE__ */ jsx(
163
+ inline ? null : /* @__PURE__ */ jsx(
157
164
  Icon,
158
165
  {
159
- className: cn("size-4 shrink-0 opacity-70", !iconOnly && "me-2"),
166
+ className: cn(
167
+ "size-4 shrink-0 opacity-70",
168
+ // Compact relies on the trigger's tokenized flex gap instead of an icon margin.
169
+ !iconOnly && !isCompact && "me-2"
170
+ ),
160
171
  "aria-hidden": "true"
161
172
  }
162
173
  ),
@@ -1,4 +1,13 @@
1
1
  import type { ToolbarGroupProp, ToolbarProp } from "../../props/components/navigation.prop.js";
2
- export type { ToolbarGroupProp, ToolbarGroupProp as ToolbarGroupProps, ToolbarProp, ToolbarProp as ToolbarProps, } from "../../props/components/navigation.prop.js";
3
- export declare function Toolbar({ onClear, hasActiveFilters, sticky, className, children, }: ToolbarProp): import("react").JSX.Element;
4
- export declare function ToolbarGroup({ label, className, children }: ToolbarGroupProp): import("react").JSX.Element;
2
+ export type { FilterBarOverflowProp, ToolbarGroupProp, ToolbarGroupProp as ToolbarGroupProps, ToolbarGroupProp as FilterBarGroupProp, ToolbarGroupProp as FilterBarGroupProps, ToolbarProp, ToolbarProp as ToolbarProps, ToolbarProp as FilterBarProp, ToolbarProp as FilterBarProps, } from "../../props/components/navigation.prop.js";
3
+ export declare function Toolbar({ onClear, hasActiveFilters, sticky, overflow, className, children, }: ToolbarProp): import("react").JSX.Element;
4
+ export declare function ToolbarGroup({ label, controlId, className, children }: ToolbarGroupProp): import("react").JSX.Element;
5
+ /**
6
+ * Canonical list-page filter strip.
7
+ *
8
+ * `Toolbar` remains available for backwards compatibility. `FilterBar` is the
9
+ * public domain-neutral name used by application design specifications.
10
+ */
11
+ export declare const FilterBar: typeof Toolbar;
12
+ /** Labelled control group for {@link FilterBar}. */
13
+ export declare const FilterBarGroup: typeof ToolbarGroup;
@@ -9,6 +9,7 @@ function Toolbar({
9
9
  onClear,
10
10
  hasActiveFilters = true,
11
11
  sticky = false,
12
+ overflow = "wrap",
12
13
  className,
13
14
  children
14
15
  }) {
@@ -18,6 +19,7 @@ function Toolbar({
18
19
  {
19
20
  role: "toolbar",
20
21
  "aria-label": t("navigation.toolbar.ariaLabel"),
22
+ "data-overflow": overflow,
21
23
  className: cn("ui-toolbar", sticky && "ui-toolbar-sticky", className),
22
24
  children: [
23
25
  children,
@@ -29,7 +31,7 @@ function Toolbar({
29
31
  }
30
32
  );
31
33
  }
32
- function ToolbarGroup({ label, className, children }) {
34
+ function ToolbarGroup({ label, controlId, className, children }) {
33
35
  const labelId = useId();
34
36
  return /* @__PURE__ */ jsxs(
35
37
  "div",
@@ -38,13 +40,17 @@ function ToolbarGroup({ label, className, children }) {
38
40
  "aria-labelledby": label ? labelId : void 0,
39
41
  className: cn("ui-stack-xs ui-toolbar-group", className),
40
42
  children: [
41
- label ? /* @__PURE__ */ jsx("div", { id: labelId, className: "ui-toolbar-label", children: label }) : null,
43
+ label ? controlId ? /* @__PURE__ */ jsx("label", { id: labelId, htmlFor: controlId, className: "ui-toolbar-label", children: label }) : /* @__PURE__ */ jsx("div", { id: labelId, className: "ui-toolbar-label", children: label }) : null,
42
44
  children
43
45
  ]
44
46
  }
45
47
  );
46
48
  }
49
+ const FilterBar = Toolbar;
50
+ const FilterBarGroup = ToolbarGroup;
47
51
  export {
52
+ FilterBar,
53
+ FilterBarGroup,
48
54
  Toolbar,
49
55
  ToolbarGroup
50
56
  };
@@ -4,8 +4,8 @@ export { Pagination, PaginationContent, PaginationItem, PaginationLink, Paginati
4
4
  export { Menubar, MenubarCheckboxItem, MenubarContent, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, } from "./menubar.js";
5
5
  export { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, } from "./navigation-menu.js";
6
6
  export { Steps } from "./steps.js";
7
- export { Toolbar, ToolbarGroup } from "./filter-bar.js";
8
- export type { ToolbarGroupProps, ToolbarProps } from "./filter-bar.js";
7
+ export { FilterBar, FilterBarGroup, Toolbar, ToolbarGroup } from "./filter-bar.js";
8
+ export type { FilterBarGroupProp, FilterBarGroupProps, FilterBarProp, FilterBarProps, ToolbarGroupProps, ToolbarProps, } from "./filter-bar.js";
9
9
  export { AppSettingPicker } from "./app-setting-picker.js";
10
10
  export type { AppSettingPickerProp, AppSettingPickerProps, AppSettingKind, } from "./app-setting-picker.js";
11
11
  export type { BreadcrumbItemProp as BreadcrumbItem } from "../../props/vocabulary/navigation.prop.js";
@@ -51,7 +51,7 @@ import {
51
51
  NavigationMenuViewport
52
52
  } from "./navigation-menu.js";
53
53
  import { Steps } from "./steps.js";
54
- import { Toolbar, ToolbarGroup } from "./filter-bar.js";
54
+ import { FilterBar, FilterBarGroup, Toolbar, ToolbarGroup } from "./filter-bar.js";
55
55
  import { AppSettingPicker } from "./app-setting-picker.js";
56
56
  import {
57
57
  DropdownMenu,
@@ -101,6 +101,8 @@ export {
101
101
  DropdownMenuSubContent,
102
102
  DropdownMenuSubTrigger,
103
103
  DropdownMenuTrigger,
104
+ FilterBar,
105
+ FilterBarGroup,
104
106
  Menubar,
105
107
  MenubarCheckboxItem,
106
108
  MenubarContent,