@setsquare/ui 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/README.md +42 -4
  3. package/dist/index.d.ts +15 -9
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +2 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/lib/cn.d.ts.map +1 -1
  8. package/dist/lib/cn.js +1 -0
  9. package/dist/lib/cn.js.map +1 -1
  10. package/dist/lib/english.d.ts +11 -0
  11. package/dist/lib/english.d.ts.map +1 -0
  12. package/dist/lib/english.js +67 -0
  13. package/dist/lib/english.js.map +1 -0
  14. package/dist/lib/labels.d.ts +38 -8
  15. package/dist/lib/labels.d.ts.map +1 -1
  16. package/dist/lib/labels.js +97 -9
  17. package/dist/lib/labels.js.map +1 -1
  18. package/dist/lib/locale.d.ts +108 -0
  19. package/dist/lib/locale.d.ts.map +1 -0
  20. package/dist/lib/locale.js +42 -0
  21. package/dist/lib/locale.js.map +1 -0
  22. package/dist/shell/app-header.d.ts +4 -1
  23. package/dist/shell/app-header.d.ts.map +1 -1
  24. package/dist/shell/app-header.js +6 -3
  25. package/dist/shell/app-header.js.map +1 -1
  26. package/dist/shell/dashboard-shell.d.ts +16 -2
  27. package/dist/shell/dashboard-shell.d.ts.map +1 -1
  28. package/dist/shell/dashboard-shell.js +7 -3
  29. package/dist/shell/dashboard-shell.js.map +1 -1
  30. package/dist/shell/landmarks.d.ts +33 -0
  31. package/dist/shell/landmarks.d.ts.map +1 -0
  32. package/dist/shell/landmarks.js +20 -0
  33. package/dist/shell/landmarks.js.map +1 -0
  34. package/dist/shell/nav-status.d.ts +46 -0
  35. package/dist/shell/nav-status.d.ts.map +1 -0
  36. package/dist/shell/nav-status.js +26 -0
  37. package/dist/shell/nav-status.js.map +1 -0
  38. package/dist/shell/rail.d.ts +73 -0
  39. package/dist/shell/rail.d.ts.map +1 -0
  40. package/dist/shell/rail.js +87 -0
  41. package/dist/shell/rail.js.map +1 -0
  42. package/dist/shell/right-panel.d.ts +7 -2
  43. package/dist/shell/right-panel.d.ts.map +1 -1
  44. package/dist/shell/right-panel.js +13 -7
  45. package/dist/shell/right-panel.js.map +1 -1
  46. package/dist/shell/sidebar.d.ts +41 -8
  47. package/dist/shell/sidebar.d.ts.map +1 -1
  48. package/dist/shell/sidebar.js +73 -19
  49. package/dist/shell/sidebar.js.map +1 -1
  50. package/dist/shell/site-menu.d.ts +11 -3
  51. package/dist/shell/site-menu.d.ts.map +1 -1
  52. package/dist/shell/site-menu.js +7 -1
  53. package/dist/shell/site-menu.js.map +1 -1
  54. package/dist/shell/site-nav.d.ts +4 -1
  55. package/dist/shell/site-nav.d.ts.map +1 -1
  56. package/dist/shell/site-nav.js +3 -3
  57. package/dist/shell/site-nav.js.map +1 -1
  58. package/dist/shell/site-shell.d.ts +4 -2
  59. package/dist/shell/site-shell.d.ts.map +1 -1
  60. package/dist/shell/site-shell.js +3 -2
  61. package/dist/shell/site-shell.js.map +1 -1
  62. package/dist/styles/theme.css +78 -0
  63. package/dist/styles/tokens.css +4 -4
  64. package/dist/tokens.json +4 -4
  65. package/dist/ui/alert.d.ts +2 -0
  66. package/dist/ui/alert.d.ts.map +1 -1
  67. package/dist/ui/alert.js +11 -9
  68. package/dist/ui/alert.js.map +1 -1
  69. package/dist/ui/breadcrumbs.d.ts +9 -1
  70. package/dist/ui/breadcrumbs.d.ts.map +1 -1
  71. package/dist/ui/breadcrumbs.js +5 -2
  72. package/dist/ui/breadcrumbs.js.map +1 -1
  73. package/dist/ui/calendar.d.ts +1 -0
  74. package/dist/ui/calendar.d.ts.map +1 -1
  75. package/dist/ui/calendar.js +7 -1
  76. package/dist/ui/calendar.js.map +1 -1
  77. package/dist/ui/checkbox.d.ts.map +1 -1
  78. package/dist/ui/checkbox.js +7 -7
  79. package/dist/ui/checkbox.js.map +1 -1
  80. package/dist/ui/code-block-parts.d.ts +30 -0
  81. package/dist/ui/code-block-parts.d.ts.map +1 -0
  82. package/dist/ui/code-block-parts.js +27 -0
  83. package/dist/ui/code-block-parts.js.map +1 -0
  84. package/dist/ui/code-block.d.ts +4 -10
  85. package/dist/ui/code-block.d.ts.map +1 -1
  86. package/dist/ui/code-block.js +4 -9
  87. package/dist/ui/code-block.js.map +1 -1
  88. package/dist/ui/combo-box.d.ts +7 -1
  89. package/dist/ui/combo-box.d.ts.map +1 -1
  90. package/dist/ui/combo-box.js +9 -2
  91. package/dist/ui/combo-box.js.map +1 -1
  92. package/dist/ui/copy-button.d.ts +7 -1
  93. package/dist/ui/copy-button.d.ts.map +1 -1
  94. package/dist/ui/copy-button.js +11 -10
  95. package/dist/ui/copy-button.js.map +1 -1
  96. package/dist/ui/date-field.d.ts +1 -0
  97. package/dist/ui/date-field.d.ts.map +1 -1
  98. package/dist/ui/date-field.js +20 -16
  99. package/dist/ui/date-field.js.map +1 -1
  100. package/dist/ui/dialog.d.ts +6 -1
  101. package/dist/ui/dialog.d.ts.map +1 -1
  102. package/dist/ui/dialog.js +11 -8
  103. package/dist/ui/dialog.js.map +1 -1
  104. package/dist/ui/error-state.d.ts +17 -2
  105. package/dist/ui/error-state.d.ts.map +1 -1
  106. package/dist/ui/error-state.js +32 -34
  107. package/dist/ui/error-state.js.map +1 -1
  108. package/dist/ui/feature-card.d.ts +18 -3
  109. package/dist/ui/feature-card.d.ts.map +1 -1
  110. package/dist/ui/feature-card.js +18 -3
  111. package/dist/ui/feature-card.js.map +1 -1
  112. package/dist/ui/field.d.ts +28 -9
  113. package/dist/ui/field.d.ts.map +1 -1
  114. package/dist/ui/field.js +22 -10
  115. package/dist/ui/field.js.map +1 -1
  116. package/dist/ui/file-upload.d.ts +23 -5
  117. package/dist/ui/file-upload.d.ts.map +1 -1
  118. package/dist/ui/file-upload.js +54 -36
  119. package/dist/ui/file-upload.js.map +1 -1
  120. package/dist/ui/form.d.ts +11 -1
  121. package/dist/ui/form.d.ts.map +1 -1
  122. package/dist/ui/form.js +7 -4
  123. package/dist/ui/form.js.map +1 -1
  124. package/dist/ui/number-field.d.ts.map +1 -1
  125. package/dist/ui/number-field.js +5 -3
  126. package/dist/ui/number-field.js.map +1 -1
  127. package/dist/ui/pagination.d.ts +11 -3
  128. package/dist/ui/pagination.d.ts.map +1 -1
  129. package/dist/ui/pagination.js +30 -23
  130. package/dist/ui/pagination.js.map +1 -1
  131. package/dist/ui/radio-group.d.ts.map +1 -1
  132. package/dist/ui/radio-group.js +4 -4
  133. package/dist/ui/radio-group.js.map +1 -1
  134. package/dist/ui/search.d.ts +3 -0
  135. package/dist/ui/search.d.ts.map +1 -1
  136. package/dist/ui/search.js +11 -10
  137. package/dist/ui/search.js.map +1 -1
  138. package/dist/ui/select.d.ts +7 -1
  139. package/dist/ui/select.d.ts.map +1 -1
  140. package/dist/ui/select.js +7 -3
  141. package/dist/ui/select.js.map +1 -1
  142. package/dist/ui/selection-bar.d.ts +17 -4
  143. package/dist/ui/selection-bar.d.ts.map +1 -1
  144. package/dist/ui/selection-bar.js +15 -11
  145. package/dist/ui/selection-bar.js.map +1 -1
  146. package/dist/ui/sheet.d.ts +1 -0
  147. package/dist/ui/sheet.d.ts.map +1 -1
  148. package/dist/ui/sheet.js +4 -1
  149. package/dist/ui/sheet.js.map +1 -1
  150. package/dist/ui/slider.d.ts +9 -2
  151. package/dist/ui/slider.d.ts.map +1 -1
  152. package/dist/ui/slider.js +13 -5
  153. package/dist/ui/slider.js.map +1 -1
  154. package/dist/ui/tabs.d.ts +1 -0
  155. package/dist/ui/tabs.d.ts.map +1 -1
  156. package/dist/ui/tabs.js +13 -12
  157. package/dist/ui/tabs.js.map +1 -1
  158. package/dist/ui/text-field.d.ts.map +1 -1
  159. package/dist/ui/text-field.js +7 -3
  160. package/dist/ui/text-field.js.map +1 -1
  161. package/dist/ui/theme-switcher.d.ts +1 -0
  162. package/dist/ui/theme-switcher.d.ts.map +1 -1
  163. package/dist/ui/theme-switcher.js +7 -7
  164. package/dist/ui/theme-switcher.js.map +1 -1
  165. package/dist/ui/toast.d.ts +1 -0
  166. package/dist/ui/toast.d.ts.map +1 -1
  167. package/dist/ui/toast.js +4 -1
  168. package/dist/ui/toast.js.map +1 -1
  169. package/dist/ui/tree.d.ts.map +1 -1
  170. package/dist/ui/tree.js +5 -5
  171. package/dist/ui/tree.js.map +1 -1
  172. package/package.json +2 -1
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { TriangleAlert } from 'lucide-react';
3
+ import { cn } from '../lib/cn.js';
4
+ import { renderIcon } from '../ui/icon.js';
5
+ // Developer-facing: said once per page.
6
+ let warned = false;
7
+ /** A status with no words to say it would be colour and shape alone: say so in the console. */
8
+ export function warnUnlabelledStatus(component, status, label) {
9
+ if (!status || label || warned)
10
+ return;
11
+ warned = true;
12
+ console.error(`<${component} status="${status}"> has no statusLabel: the badge is then shown but never said. Give it statusLabel.`);
13
+ }
14
+ /**
15
+ * The item's icon in a box that holds its status badge. The icon is rendered by `renderIcon`, so a component, an
16
+ * element or a custom drawing (a progress ring) all work; the box carries the colour, so a drawing that uses
17
+ * `currentColor` turns brand on the current page even if it ignores `className`.
18
+ */
19
+ export function NavIcon({ icon, status, className, }) {
20
+ return (_jsxs("span", { className: cn('nav-icon size-5', className), children: [renderIcon(icon, 'size-5 shrink-0'), status && (_jsx("span", { "aria-hidden": "true", "data-status": status, className: "nav-status" }, status))] }));
21
+ }
22
+ /** The detail as shown: muted and tabular, or the warning colour after a warning icon. Hidden from assistive tech. */
23
+ export function NavDetail({ detail, tone, className, }) {
24
+ return (_jsxs("span", { "aria-hidden": "true", "data-tone": tone ?? 'neutral', className: cn('inline-flex items-center gap-1 text-[13px] leading-4 font-medium whitespace-nowrap text-muted-foreground tabular-nums', tone === 'warning' && 'font-semibold text-warning', className), children: [tone === 'warning' && _jsx(TriangleAlert, { className: "size-3.5 shrink-0" }), detail] }));
25
+ }
26
+ //# sourceMappingURL=nav-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav-status.js","sourceRoot":"","sources":["../../src/shell/nav-status.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,UAAU,EAAiB,MAAM,eAAe,CAAC;AA6B1D,wCAAwC;AACxC,IAAI,MAAM,GAAG,KAAK,CAAC;AACnB,+FAA+F;AAC/F,MAAM,UAAU,oBAAoB,CAAC,SAAiB,EAAE,MAAkB,EAAE,KAAc;IACxF,IAAI,CAAC,MAAM,IAAI,KAAK,IAAI,MAAM;QAAE,OAAO;IACvC,MAAM,GAAG,IAAI,CAAC;IACd,OAAO,CAAC,KAAK,CACX,IAAI,SAAS,YAAY,MAAM,qFAAqF,CACrH,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,EACtB,IAAI,EACJ,MAAM,EACN,SAAS,GAKV;IACC,OAAO,CACL,gBAAM,SAAS,EAAE,EAAE,CAAC,iBAAiB,EAAE,SAAS,CAAC,aAC9C,UAAU,CAAC,IAAI,EAAE,iBAAiB,CAAC,EACnC,MAAM,IAAI,CAET,8BAA+B,MAAM,iBAAc,MAAM,EAAE,SAAS,EAAC,YAAY,IAAtE,MAAM,CAAmE,CACrF,IACI,CACR,CAAC;AACJ,CAAC;AAED,sHAAsH;AACtH,MAAM,UAAU,SAAS,CAAC,EACxB,MAAM,EACN,IAAI,EACJ,SAAS,GAKV;IACC,OAAO,CACL,+BACc,MAAM,eACP,IAAI,IAAI,SAAS,EAC5B,SAAS,EAAE,EAAE,CACX,uHAAuH,EACvH,IAAI,KAAK,SAAS,IAAI,4BAA4B,EAClD,SAAS,CACV,aAEA,IAAI,KAAK,SAAS,IAAI,KAAC,aAAa,IAAC,SAAS,EAAC,mBAAmB,GAAG,EACrE,MAAM,IACF,CACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,73 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { IconProp } from '../ui/icon.js';
3
+ import { type NavItemLiveProps } from './nav-status.js';
4
+ /** The rail's words, for the `labels` prop. App-wide, `SetsquareLabels.rail` over `SetsquareLabels.sidebar`. */
5
+ export interface RailLabels {
6
+ /** Names the Sheet it becomes on phones; `{product}` is the shell's `productName`. */
7
+ navigation: string;
8
+ /** Names the navigation landmark. */
9
+ primary: string;
10
+ /** Added to an item's name for its `detail`: "Spend, $1.20". */
11
+ detail: string;
12
+ /** The same for a `detailTone="warning"` detail, with words for the warning: "Spend, $1.20, warning". */
13
+ detailWarning: string;
14
+ /** Added to an item's name for its `statusLabel`: "Scenes, Claude is working". */
15
+ status: string;
16
+ }
17
+ export declare const railLabels: RailLabels;
18
+ export interface RailProps {
19
+ children: ReactNode;
20
+ labels?: Partial<RailLabels>;
21
+ className?: string;
22
+ }
23
+ /**
24
+ * Layout option C: a labelled navigation rail, `--rail-width` wide, used in a DashboardShell instead of Sidebar. Every
25
+ * item shows its icon over its name, so every destination is visible without the sidebar's width. It never
26
+ * collapses or expands. On phones it is the same Sheet the Sidebar becomes, with the items as rows. Give the shell
27
+ * `navigation="rail"`, so AppHeader shows its trigger only where it opens that Sheet.
28
+ */
29
+ export declare function Rail({ children, labels: given, className }: RailProps): import("react").JSX.Element;
30
+ /** The product's mark, at the top. In the phone's Sheet it sits at the start of the row, as the items do. */
31
+ export declare function RailHeader({ children, className }: {
32
+ children: ReactNode;
33
+ className?: string;
34
+ }): import("react").JSX.Element;
35
+ /**
36
+ * The items. Scrolls when they are taller than the shell, and is positioned, so the visually hidden group names
37
+ * inside it scroll with it rather than being laid out against the shell.
38
+ */
39
+ export declare function RailContent({ children, className }: {
40
+ children: ReactNode;
41
+ className?: string;
42
+ }): import("react").JSX.Element;
43
+ /** Pinned to the bottom: help, settings, the account. Put items in a `RailList`, as in a SidebarFooter. */
44
+ export declare function RailFooter({ children, className }: {
45
+ children: ReactNode;
46
+ className?: string;
47
+ }): import("react").JSX.Element;
48
+ /**
49
+ * A labelled group of items. Its name is visually hidden and names the group for assistive technology; a rule above
50
+ * it separates it from what comes before. Groups are `role="group"`, not landmarks, so the landmark list stays short.
51
+ */
52
+ export declare function RailGroup({ label, children, className, }: {
53
+ label: string;
54
+ children: ReactNode;
55
+ className?: string;
56
+ }): import("react").JSX.Element;
57
+ /** Items outside a group: a list, as a group's are. */
58
+ export declare function RailList({ children, className }: {
59
+ children: ReactNode;
60
+ className?: string;
61
+ }): import("react").JSX.Element;
62
+ export type RailItemProps = {
63
+ href?: string;
64
+ onPress?: () => void;
65
+ /** A component, an element, or a drawing of the product's own (a progress ring) that takes `className`. */
66
+ icon: IconProp;
67
+ /** The name, always shown under the icon: two lines at most, hyphenated rather than cut off. */
68
+ children: string;
69
+ /** Marks the current page: brand-subtle fill, a marker edge, the icon and name in brand at weight 700, and aria-current. */
70
+ isCurrent?: boolean;
71
+ } & NavItemLiveProps;
72
+ export declare function RailItem({ href, onPress, icon, children, isCurrent, detail, detailTone, status, statusLabel, }: RailItemProps): import("react").JSX.Element;
73
+ //# sourceMappingURL=rail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rail.d.ts","sourceRoot":"","sources":["../../src/shell/rail.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG9C,OAAO,EAA4C,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAElG,gHAAgH;AAChH,MAAM,WAAW,UAAU;IACzB,sFAAsF;IACtF,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,yGAAyG;IACzG,aAAa,EAAE,MAAM,CAAC;IACtB,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,UAAU,EAAE,UAMxB,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,SAAS,+BA4CrE;AAED,6GAA6G;AAC7G,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAY9F;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAY/F;AAED,2GAA2G;AAC3G,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAY9F;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,QAAQ,EACR,SAAS,GACV,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAcA;AAED,uDAAuD;AACvD,wBAAgB,QAAQ,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAE5F;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,2GAA2G;IAC3G,IAAI,EAAE,QAAQ,CAAC;IACf,gGAAgG;IAChG,QAAQ,EAAE,MAAM,CAAC;IACjB,4HAA4H;IAC5H,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,gBAAgB,CAAC;AAErB,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,MAAM,EACN,UAAU,EACV,MAAM,EACN,WAAW,GACZ,EAAE,aAAa,+BAkEf"}
@@ -0,0 +1,87 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId } from 'react';
4
+ import { Link } from 'react-aria-components';
5
+ import { cn } from '../lib/cn.js';
6
+ import { useLabels } from '../lib/locale.js';
7
+ import { Sheet } from '../ui/sheet.js';
8
+ import { useShell } from './dashboard-shell.js';
9
+ import { NavDetail, NavIcon, warnUnlabelledStatus } from './nav-status.js';
10
+ export const railLabels = {
11
+ navigation: '{product} navigation',
12
+ primary: 'Primary',
13
+ detail: ', {detail}',
14
+ detailWarning: ', {detail}, warning',
15
+ status: ', {status}',
16
+ };
17
+ // Developer-facing: said once per page, not once per render.
18
+ let warned = false;
19
+ /**
20
+ * Layout option C: a labelled navigation rail, `--rail-width` wide, used in a DashboardShell instead of Sidebar. Every
21
+ * item shows its icon over its name, so every destination is visible without the sidebar's width. It never
22
+ * collapses or expands. On phones it is the same Sheet the Sidebar becomes, with the items as rows. Give the shell
23
+ * `navigation="rail"`, so AppHeader shows its trigger only where it opens that Sheet.
24
+ */
25
+ export function Rail({ children, labels: given, className }) {
26
+ const { size, mobileNavOpen, setMobileNavOpen, productName, isSizeForced, navigation } = useShell();
27
+ // The sidebar's app-wide words, then the rail's: a product that switches keeps its translations.
28
+ const t = useLabels(['sidebar', 'rail'], railLabels, given);
29
+ useEffect(() => {
30
+ if (navigation === 'rail' || warned)
31
+ return;
32
+ warned = true;
33
+ console.error('<Rail> is inside a <DashboardShell> without navigation="rail": AppHeader will show a sidebar toggle that does nothing. Pass navigation="rail" to the shell.');
34
+ }, [navigation]);
35
+ if (size === 'mobile') {
36
+ return (_jsx(Sheet, { side: "left", isOpen: mobileNavOpen, onOpenChange: setMobileNavOpen, "aria-label": t('navigation', { product: productName }), children: _jsx("nav", { "aria-label": t('primary'), "data-layout": "rows", className: "group/rail flex h-full min-h-0 flex-col text-sidebar-foreground", children: children }) }));
37
+ }
38
+ return (_jsx("nav", { "aria-label": t('primary'), "data-layout": "tiles", style: { viewTransitionName: 'shell-sidebar' }, className: cn('group/rail flex h-full min-h-0 w-rail shrink-0 flex-col text-sidebar-foreground', !isSizeForced && '@max-[48rem]/shell:hidden', className), children: children }));
39
+ }
40
+ /** The product's mark, at the top. In the phone's Sheet it sits at the start of the row, as the items do. */
41
+ export function RailHeader({ children, className }) {
42
+ return (_jsx("div", { className: cn('flex min-h-[calc(var(--nav-item-height)+12px)] shrink-0 items-center justify-center px-1 pt-3', 'group-data-[layout=rows]/rail:justify-start group-data-[layout=rows]/rail:px-[22px]', className), children: children }));
43
+ }
44
+ /**
45
+ * The items. Scrolls when they are taller than the shell, and is positioned, so the visually hidden group names
46
+ * inside it scroll with it rather than being laid out against the shell.
47
+ */
48
+ export function RailContent({ children, className }) {
49
+ return (_jsx("div", { className: cn('relative flex min-h-0 flex-1 flex-col gap-(--nav-item-gap) overflow-y-auto px-1 py-3', 'group-data-[layout=rows]/rail:px-3', className), children: children }));
50
+ }
51
+ /** Pinned to the bottom: help, settings, the account. Put items in a `RailList`, as in a SidebarFooter. */
52
+ export function RailFooter({ children, className }) {
53
+ return (_jsx("div", { className: cn('flex shrink-0 flex-col gap-(--nav-item-gap) border-t border-sidebar-border px-1 py-3', 'group-data-[layout=rows]/rail:px-3', className), children: children }));
54
+ }
55
+ /**
56
+ * A labelled group of items. Its name is visually hidden and names the group for assistive technology; a rule above
57
+ * it separates it from what comes before. Groups are `role="group"`, not landmarks, so the landmark list stays short.
58
+ */
59
+ export function RailGroup({ label, children, className, }) {
60
+ const id = useId();
61
+ return (_jsxs("div", { role: "group", "aria-labelledby": id, className: cn('not-first:border-t not-first:border-sidebar-border not-first:pt-3', className), children: [_jsx("h2", { id: id, className: "sr-only", children: label }), _jsx(RailList, { children: children })] }));
62
+ }
63
+ /** Items outside a group: a list, as a group's are. */
64
+ export function RailList({ children, className }) {
65
+ return _jsx("ul", { className: cn('flex flex-col gap-(--nav-item-gap)', className), children: children });
66
+ }
67
+ export function RailItem({ href, onPress, icon, children, isCurrent, detail, detailTone, status, statusLabel, }) {
68
+ const t = useLabels(['sidebar', 'rail'], railLabels, undefined);
69
+ warnUnlabelledStatus('RailItem', status, statusLabel);
70
+ return (_jsx("li", { children: _jsx(Link, { href: href, onPress: onPress, "aria-current": isCurrent ? 'page' : undefined, className: cn(
71
+ // Layer 1: the row is the target, the rail's full width and at least --target-min tall.
72
+ 'group/item relative flex min-h-target w-full cursor-pointer items-center text-sidebar-foreground no-underline outline-none'), children: _jsxs("span", { className: cn('relative flex w-full min-w-0 flex-col items-center gap-1 rounded-md px-0.5 py-2', 'group-data-hovered/item:bg-sidebar-accent group-data-hovered/item:text-sidebar-accent-foreground group-data-focus-visible/item:focus-ring', 'group-aria-[current=page]/item:bg-brand-subtle group-aria-[current=page]/item:text-foreground',
73
+ // The marker: a 3px edge along the tile's start, an inset shadow so it follows the rounded corners.
74
+ 'group-aria-[current=page]/item:shadow-[inset_3px_0_0_var(--sidebar-primary)]',
75
+ // Hovered, the current page keeps its fill: the hover ground under brand text would be a pair the audit doesn't
76
+ // promise. Stacked variants, so this is more specific than the hover rule whatever order they're written in.
77
+ 'group-data-hovered/item:group-aria-[current=page]/item:bg-brand-subtle group-data-hovered/item:group-aria-[current=page]/item:text-foreground',
78
+ // Forced colours drop shadows and fills: there the marker is a bar in Highlight.
79
+ 'forced-colors:group-aria-[current=page]/item:before:absolute forced-colors:group-aria-[current=page]/item:before:inset-y-0 forced-colors:group-aria-[current=page]/item:before:start-0 forced-colors:group-aria-[current=page]/item:before:w-[3px] forced-colors:group-aria-[current=page]/item:before:rounded-s-md forced-colors:group-aria-[current=page]/item:before:bg-[Highlight] forced-colors:group-aria-[current=page]/item:before:forced-color-adjust-none',
80
+ // In the phone's Sheet, a row: the icon beside the name, as the Sidebar's are.
81
+ 'group-data-[layout=rows]/rail:min-h-nav-item group-data-[layout=rows]/rail:flex-row group-data-[layout=rows]/rail:gap-3 group-data-[layout=rows]/rail:px-3 group-data-[layout=rows]/rail:py-0',
82
+ // The status badge's cut-out follows the ground: the rail, the hovered tile, the current one.
83
+ '[--nav-cut:var(--sidebar)] group-data-hovered/item:[--nav-cut:var(--sidebar-accent)] group-aria-[current=page]/item:[--nav-cut:var(--brand-subtle)] group-data-hovered/item:group-aria-[current=page]/item:[--nav-cut:var(--brand-subtle)]'), children: [_jsx(NavIcon, { icon: icon, status: status, className: "group-aria-[current=page]/item:text-brand" }), _jsxs("span", { "data-name": "", className: cn('w-full min-w-0 text-center text-[13px] leading-4 font-medium [overflow-wrap:anywhere] hyphens-auto', 'group-aria-[current=page]/item:font-bold group-aria-[current=page]/item:text-brand', 'group-data-[layout=rows]/rail:flex-1 group-data-[layout=rows]/rail:text-start group-data-[layout=rows]/rail:text-sm group-data-[layout=rows]/rail:leading-5'), children: [children, detail && (_jsx("span", { className: "sr-only", children: t(detailTone === 'warning' ? 'detailWarning' : 'detail', { detail }) })), statusLabel && _jsx("span", { className: "sr-only", children: t('status', { status: statusLabel }) })] }), detail && (_jsx(NavDetail, { detail: detail, tone: detailTone,
84
+ // Under the name in a tile, wrapping rather than running out of it; after the name in a row.
85
+ className: "max-w-full flex-wrap justify-center text-center whitespace-normal [overflow-wrap:anywhere] group-data-[layout=rows]/rail:whitespace-nowrap" }))] }) }) }));
86
+ }
87
+ //# sourceMappingURL=rail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rail.js","sourceRoot":"","sources":["../../src/shell/rail.tsx"],"names":[],"mappings":"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"}
@@ -1,9 +1,12 @@
1
1
  import { type ReactNode } from 'react';
2
2
  /** The panel's words, for the `labels` prop. */
3
3
  export interface RightPanelLabels {
4
- /** Its close button; `{title}` is the panel's title in lower case. */
4
+ /** Its close button; `{title}` is the panel's title, as written. */
5
5
  close: string;
6
+ /** PanelToggle's name. */
7
+ toggle: string;
6
8
  }
9
+ export declare const rightPanelLabels: RightPanelLabels;
7
10
  export interface RightPanelProps {
8
11
  /** The panel's heading; also names the landmark or dialog. */
9
12
  title: string;
@@ -19,8 +22,10 @@ export interface RightPanelProps {
19
22
  */
20
23
  export declare function RightPanel({ title, children, footer, labels: given, className }: RightPanelProps): import("react").JSX.Element | null;
21
24
  /** Shows and hides the right panel. Put it in the page actions. */
22
- export declare function PanelToggle({ label }: {
25
+ export declare function PanelToggle({ label, labels, }: {
26
+ /** @deprecated Use `labels.toggle`. Kept for one release. */
23
27
  label?: string;
28
+ labels?: Partial<RightPanelLabels>;
24
29
  }): import("react").JSX.Element;
25
30
  /** A titled group inside the panel. */
26
31
  export declare function PanelSection({ title, children, className, }: {
@@ -1 +1 @@
1
- {"version":3,"file":"right-panel.d.ts","sourceRoot":"","sources":["../../src/shell/right-panel.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAS,KAAK,SAAS,EAAkB,MAAM,OAAO,CAAC;AAQ9D,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;CACf;AAID,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,SAAS,CAAC;IACpB,oCAAoC;IACpC,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,eAAe,sCA4DhG;AAED,mEAAmE;AACnE,wBAAgB,WAAW,CAAC,EAAE,KAAuB,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,+BAa1E;AAED,uCAAuC;AACvC,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAOA"}
1
+ {"version":3,"file":"right-panel.d.ts","sourceRoot":"","sources":["../../src/shell/right-panel.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAS,KAAK,SAAS,EAAkB,MAAM,OAAO,CAAC;AAS9D,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,gBAAgB,EAAE,gBAG9B,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,SAAS,CAAC;IACpB,oCAAoC;IACpC,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,eAAe,sCA6DhG;AAED,mEAAmE;AACnE,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,MAAM,GACP,EAAE;IACD,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACpC,+BAcA;AAED,uCAAuC;AACvC,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAOA"}
@@ -3,35 +3,41 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useId } from 'react';
4
4
  import { PanelRight, X } from 'lucide-react';
5
5
  import { cn } from '../lib/cn.js';
6
- import { fill, withLabels } from '../lib/labels.js';
6
+ import { literal } from '../lib/labels.js';
7
+ import { useLabels } from '../lib/locale.js';
7
8
  import { IconButton } from '../ui/icon-button.js';
8
9
  import { Sheet } from '../ui/sheet.js';
9
10
  import { useShell } from './dashboard-shell.js';
10
- const rightPanelLabels = { close: 'Close {title}' };
11
+ export const rightPanelLabels = {
12
+ close: 'Close {title}',
13
+ toggle: 'Details panel',
14
+ };
11
15
  /**
12
16
  * Extra information about the current selection. An <aside> beside main on wide shells,
13
17
  * a Sheet (modal dialog) on narrow ones. Closing returns focus to the PanelToggle.
14
18
  */
15
19
  export function RightPanel({ title, children, footer, labels: given, className }) {
16
- const labels = withLabels(rightPanelLabels, given);
20
+ const t = useLabels('rightPanel', rightPanelLabels, given);
21
+ const close = t('close', { title });
17
22
  const { panelOpen, setPanelOpen, panelMode, panelToggleRef, isSizeForced } = useShell();
18
23
  const headingId = useId();
19
24
  const foot = footer ? (_jsx("div", { className: "flex gap-(--control-gap) border-t border-border px-4 py-3 *:flex-1", children: footer })) : null;
20
25
  const body = (_jsx("div", { className: "relative flex min-h-0 flex-1 flex-col gap-5 overflow-y-auto p-4", children: children }));
21
26
  if (panelMode === 'sheet') {
22
- return (_jsx(Sheet, { side: "right", title: title, labels: { close: fill(labels.close, { title: title.toLowerCase() }) }, isOpen: panelOpen, onOpenChange: setPanelOpen, footer: foot, children: body }));
27
+ return (_jsx(Sheet, { side: "right", title: title, labels: { close: literal(close) }, isOpen: panelOpen, onOpenChange: setPanelOpen, footer: foot, children: body }));
23
28
  }
24
29
  if (!panelOpen)
25
30
  return null;
26
- return (_jsxs("aside", { "aria-labelledby": headingId, style: { viewTransitionName: 'shell-panel' }, className: cn('flex w-panel shrink-0 flex-col border-l border-border bg-card text-card-foreground', !isSizeForced && '@max-[64rem]/shell:hidden', className), children: [_jsxs("div", { className: "flex min-h-[calc(var(--control-height)+1rem)] items-center justify-between gap-2 border-b border-border py-2 pr-2 pl-4", children: [_jsx("h2", { id: headingId, className: "text-subheading", children: title }), _jsx(IconButton, { label: fill(labels.close, { title: title.toLowerCase() }), icon: X, visualSize: "icon-sm", tooltip: false, className: "justify-end", onPress: () => {
31
+ return (_jsxs("aside", { "aria-labelledby": headingId, style: { viewTransitionName: 'shell-panel' }, className: cn('flex w-panel shrink-0 flex-col border-l border-border bg-card text-card-foreground', !isSizeForced && '@max-[64rem]/shell:hidden', className), children: [_jsxs("div", { className: "flex min-h-[calc(var(--control-height)+1rem)] items-center justify-between gap-2 border-b border-border py-2 pr-2 pl-4", children: [_jsx("h2", { id: headingId, className: "text-subheading", children: title }), _jsx(IconButton, { label: close, icon: X, visualSize: "icon-sm", tooltip: false, className: "justify-end", onPress: () => {
27
32
  setPanelOpen(false);
28
33
  requestAnimationFrame(() => panelToggleRef.current?.focus());
29
34
  } })] }), body, foot] }));
30
35
  }
31
36
  /** Shows and hides the right panel. Put it in the page actions. */
32
- export function PanelToggle({ label = 'Details panel' }) {
37
+ export function PanelToggle({ label, labels, }) {
38
+ const t = useLabels('rightPanel', rightPanelLabels, labels);
33
39
  const { panelOpen, setPanelOpen, panelToggleRef, panelMode } = useShell();
34
- return (_jsx(IconButton, { ref: panelToggleRef, label: label, icon: PanelRight, variant: "outline", "aria-expanded": panelMode === 'inline' ? panelOpen : undefined, "aria-haspopup": panelMode === 'sheet' ? 'dialog' : undefined, onPress: () => setPanelOpen(!panelOpen) }));
40
+ return (_jsx(IconButton, { ref: panelToggleRef, label: label ?? t('toggle'), icon: PanelRight, variant: "outline", "aria-expanded": panelMode === 'inline' ? panelOpen : undefined, "aria-haspopup": panelMode === 'sheet' ? 'dialog' : undefined, onPress: () => setPanelOpen(!panelOpen) }));
35
41
  }
36
42
  /** A titled group inside the panel. */
37
43
  export function PanelSection({ title, children, className, }) {
@@ -1 +1 @@
1
- {"version":3,"file":"right-panel.js","sourceRoot":"","sources":["../../src/shell/right-panel.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"right-panel.js","sourceRoot":"","sources":["../../src/shell/right-panel.tsx"],"names":[],"mappings":"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"}
@@ -1,16 +1,36 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import { type Key } from 'react-aria-components';
3
3
  import { type IconProp } from '../ui/icon.js';
4
+ import { type NavItemLiveProps } from './nav-status.js';
4
5
  /** True when the sidebar shows icons only (never inside the mobile Sheet). */
5
6
  export declare const useSidebarCollapsed: () => boolean;
6
7
  /** The sidebar's words, for the `labels` prop. */
7
8
  export interface SidebarLabels {
8
9
  /** Names the Sheet it becomes on phones; `{product}` is the shell's `productName`. */
9
10
  navigation: string;
11
+ /** Names the navigation landmark. */
12
+ primary: string;
13
+ /** Added to an item's name for its count, which shows as a number only: "Projects, 12". */
14
+ count: string;
15
+ /** A collapsed item's tooltip, with its count: "Projects (12)". */
16
+ tipCount: string;
17
+ /** Added to an item's name for its `detail`: "Spend, $1.20". */
18
+ detail: string;
19
+ /** The same for a `detailTone="warning"` detail, with words for the warning: "Spend, $1.20, warning". */
20
+ detailWarning: string;
21
+ /** Added to an item's name for its `statusLabel`: "Drafts, A draft is waiting for you". */
22
+ status: string;
23
+ /** A collapsed item's tooltip with its detail: `{name}` is the tooltip so far. "Spend ($1.20)". */
24
+ tipDetail: string;
25
+ /** The same for a warning detail: "Spend ($1.20, warning)". */
26
+ tipDetailWarning: string;
27
+ /** A collapsed item's tooltip with its status: `{tip}` is the tooltip so far. "Scenes: Claude is working". */
28
+ tipStatus: string;
10
29
  }
30
+ export declare const sidebarLabels: SidebarLabels;
11
31
  export interface SidebarProps {
12
32
  children: ReactNode;
13
- /** Names the navigation landmark. */
33
+ /** @deprecated Use `labels.primary`. Kept for one release. */
14
34
  label?: string;
15
35
  labels?: Partial<SidebarLabels>;
16
36
  className?: string;
@@ -19,7 +39,7 @@ export interface SidebarProps {
19
39
  * The full-height sidebar. Expanded, collapsed to icons (labels stay in the accessibility tree
20
40
  * and show as tooltips), or — on phones — a Sheet opened from the header.
21
41
  */
22
- export declare function Sidebar({ children, label, labels: given, className }: SidebarProps): import("react").JSX.Element;
42
+ export declare function Sidebar({ children, label: givenLabel, labels: given, className }: SidebarProps): import("react").JSX.Element;
23
43
  export declare function SidebarHeader({ children, className, }: {
24
44
  children: ReactNode;
25
45
  className?: string;
@@ -42,17 +62,25 @@ export declare function SidebarList({ children, className }: {
42
62
  children: ReactNode;
43
63
  className?: string;
44
64
  }): import("react").JSX.Element;
45
- export interface SidebarItemProps {
65
+ export type SidebarItemProps = {
46
66
  href?: string;
67
+ /** A component, an element, or a drawing of the product's own (a progress ring) that takes `className`. */
47
68
  icon: IconProp;
48
69
  children: string;
49
- /** Marks the current page: brand-subtle fill, marker bar and weight 700, plus aria-current. */
70
+ /** Marks the current page: brand-subtle fill, a marker edge, the icon and name in brand at weight 700, and aria-current. */
50
71
  isCurrent?: boolean;
51
72
  /** Shown beside the label and read as part of it: "Projects, 12". */
52
73
  count?: number;
53
74
  onPress?: () => void;
54
- }
55
- export declare function SidebarItem({ href, icon, children, isCurrent, count, onPress }: SidebarItemProps): import("react").JSX.Element;
75
+ } & NavItemLiveProps;
76
+ /** A collapsed item's tooltip: its name, then its count, detail and status, each through its own template. */
77
+ export declare function navTooltip(t: (key: 'tipCount' | 'tipDetail' | 'tipDetailWarning' | 'tipStatus', values: Record<string, string | number>) => string, name: string, { count, detail, detailTone, statusLabel, }: {
78
+ count?: number;
79
+ detail?: string;
80
+ detailTone?: string;
81
+ statusLabel?: string;
82
+ }): string;
83
+ export declare function SidebarItem({ href, icon, children, isCurrent, count, onPress, detail, detailTone, status, statusLabel, }: SidebarItemProps): import("react").JSX.Element;
56
84
  export interface SidebarSubItem {
57
85
  href: string;
58
86
  label: string;
@@ -97,9 +125,14 @@ export interface SidebarTriggerLabels {
97
125
  /** Wider, where it collapses the sidebar to icons and back. */
98
126
  toggleSidebar: string;
99
127
  }
100
- /** Collapses the sidebar to icons, or opens it as a Sheet on phones. */
128
+ export declare const sidebarTriggerLabels: SidebarTriggerLabels;
129
+ /**
130
+ * Collapses the sidebar to icons, or opens it as a Sheet on phones. In a shell with `navigation="rail"` there is
131
+ * nothing to collapse, so it is only there on phones, where it opens the rail's Sheet: before the shell has measured
132
+ * itself a container query hides it from bp-md up, and once it has (or its size is forced) it isn't rendered there.
133
+ */
101
134
  export declare function SidebarTrigger({ labels: given, className, }: {
102
135
  labels?: Partial<SidebarTriggerLabels>;
103
136
  className?: string;
104
- }): import("react").JSX.Element;
137
+ }): import("react").JSX.Element | null;
105
138
  //# sourceMappingURL=sidebar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sidebar.d.ts","sourceRoot":"","sources":["../../src/shell/sidebar.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sidebar.d.ts","sourceRoot":"","sources":["../../src/shell/sidebar.tsx"],"names":[],"mappings":"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"}
@@ -1,29 +1,42 @@
1
1
  'use client';
2
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { createContext, useContext, useId } from 'react';
4
- import { Button as RACButton, Disclosure as RACDisclosure, DisclosurePanel, Link, } from 'react-aria-components';
4
+ import { Button as RACButton, Disclosure as RACDisclosure, DisclosurePanel, Link, useLocale, } from 'react-aria-components';
5
5
  import { ChevronDown, ChevronsUpDown, Menu as MenuIcon, PanelLeft } from 'lucide-react';
6
6
  import { cn } from '../lib/cn.js';
7
- import { fill, withLabels } from '../lib/labels.js';
7
+ import { useLabels } from '../lib/locale.js';
8
8
  import { IconButton } from '../ui/icon-button.js';
9
9
  import { renderIcon } from '../ui/icon.js';
10
10
  import { Menu, MenuItem, MenuTrigger, Popover } from '../ui/menu.js';
11
11
  import { Sheet } from '../ui/sheet.js';
12
12
  import { Tooltip, TooltipTrigger } from '../ui/tooltip.js';
13
13
  import { useShell } from './dashboard-shell.js';
14
+ import { NavDetail, NavIcon, warnUnlabelledStatus } from './nav-status.js';
14
15
  const SidebarCollapsedContext = createContext(false);
15
16
  /** True when the sidebar shows icons only (never inside the mobile Sheet). */
16
17
  export const useSidebarCollapsed = () => useContext(SidebarCollapsedContext);
17
- const sidebarLabels = { navigation: '{product} navigation' };
18
+ export const sidebarLabels = {
19
+ navigation: '{product} navigation',
20
+ primary: 'Primary',
21
+ count: ', {count, number}',
22
+ tipCount: '{name} ({count, number})',
23
+ detail: ', {detail}',
24
+ detailWarning: ', {detail}, warning',
25
+ status: ', {status}',
26
+ tipDetail: '{name} ({detail})',
27
+ tipDetailWarning: '{name} ({detail}, warning)',
28
+ tipStatus: '{tip}: {status}',
29
+ };
18
30
  /**
19
31
  * The full-height sidebar. Expanded, collapsed to icons (labels stay in the accessibility tree
20
32
  * and show as tooltips), or — on phones — a Sheet opened from the header.
21
33
  */
22
- export function Sidebar({ children, label = 'Primary', labels: given, className }) {
34
+ export function Sidebar({ children, label: givenLabel, labels: given, className }) {
23
35
  const { size, sidebarCollapsed, mobileNavOpen, setMobileNavOpen, productName, isSizeForced } = useShell();
24
- const labels = withLabels(sidebarLabels, given);
36
+ const t = useLabels('sidebar', sidebarLabels, given);
37
+ const label = givenLabel ?? t('primary');
25
38
  if (size === 'mobile') {
26
- return (_jsx(Sheet, { side: "left", isOpen: mobileNavOpen, onOpenChange: setMobileNavOpen, "aria-label": fill(labels.navigation, { product: productName }), children: _jsx(SidebarCollapsedContext.Provider, { value: false, children: _jsx("nav", { "aria-label": label, className: "flex h-full min-h-0 flex-col text-sidebar-foreground", children: children }) }) }));
39
+ return (_jsx(Sheet, { side: "left", isOpen: mobileNavOpen, onOpenChange: setMobileNavOpen, "aria-label": t('navigation', { product: productName }), children: _jsx(SidebarCollapsedContext.Provider, { value: false, children: _jsx("nav", { "aria-label": label, className: "flex h-full min-h-0 flex-col text-sidebar-foreground", children: children }) }) }));
27
40
  }
28
41
  return (_jsx(SidebarCollapsedContext.Provider, { value: sidebarCollapsed, children: _jsx("nav", { "aria-label": label, "data-collapsed": sidebarCollapsed || undefined, style: { viewTransitionName: 'shell-sidebar' }, className: cn('group/sidebar flex h-full min-h-0 w-sidebar shrink-0 flex-col text-sidebar-foreground', 'transition-[width] duration-(--duration-panel) ease-(--ease-standard) motion-reduce:transition-none aaa:transition-none', 'data-collapsed:w-sidebar-collapsed', !isSizeForced && '@max-[48rem]/shell:hidden', className), children: children }) }));
29
42
  }
@@ -46,12 +59,33 @@ export function SidebarGroup({ label, children, className, }) {
46
59
  export function SidebarList({ children, className }) {
47
60
  return _jsx("ul", { className: cn('flex flex-col gap-(--nav-item-gap)', className), children: children });
48
61
  }
49
- export function SidebarItem({ href, icon, children, isCurrent, count, onPress }) {
62
+ /** A collapsed item's tooltip: its name, then its count, detail and status, each through its own template. */
63
+ export function navTooltip(t, name, { count, detail, detailTone, statusLabel, }) {
64
+ let tip = count != null ? t('tipCount', { name, count }) : name;
65
+ if (detail)
66
+ tip = t(detailTone === 'warning' ? 'tipDetailWarning' : 'tipDetail', { name: tip, detail });
67
+ if (statusLabel)
68
+ tip = t('tipStatus', { tip, status: statusLabel });
69
+ return tip;
70
+ }
71
+ export function SidebarItem({ href, icon, children, isCurrent, count, onPress, detail, detailTone, status, statusLabel, }) {
50
72
  const collapsed = useSidebarCollapsed();
73
+ const t = useLabels('sidebar', sidebarLabels, undefined);
74
+ const { locale } = useLocale();
75
+ warnUnlabelledStatus('SidebarItem', status, statusLabel);
51
76
  const link = (_jsx(Link, { href: href, onPress: onPress, "aria-current": isCurrent ? 'page' : undefined, className: cn(
52
77
  // Layer 1: the row is the target, full width and at least --target-min tall.
53
- 'group/item relative flex min-h-target w-full cursor-pointer items-center text-sm font-medium text-sidebar-foreground no-underline outline-none', 'group-data-collapsed/sidebar:justify-center'), children: _jsxs("span", { className: cn('relative flex min-h-nav-item w-full min-w-0 items-center gap-3 rounded-md px-3', 'group-data-hovered/item:bg-sidebar-accent group-data-hovered/item:text-sidebar-accent-foreground group-data-focus-visible/item:focus-ring', 'group-aria-[current=page]/item:bg-brand-subtle group-aria-[current=page]/item:font-bold group-aria-[current=page]/item:text-foreground', 'group-aria-[current=page]/item:before:absolute group-aria-[current=page]/item:before:inset-y-2 group-aria-[current=page]/item:before:left-0 group-aria-[current=page]/item:before:w-[3px] group-aria-[current=page]/item:before:rounded-full group-aria-[current=page]/item:before:bg-sidebar-primary', 'group-data-collapsed/sidebar:w-nav-item group-data-collapsed/sidebar:justify-center group-data-collapsed/sidebar:px-0'), children: [renderIcon(icon, 'size-5 shrink-0 group-aria-[current=page]/item:text-brand'), _jsxs("span", { className: "min-w-0 flex-1 truncate group-data-collapsed/sidebar:sr-only", children: [children, count != null && _jsxs("span", { className: "sr-only", children: [", ", count] })] }), count != null && (_jsx("span", { "aria-hidden": "true", className: "text-[13px] font-medium text-muted-foreground tabular-nums group-data-collapsed/sidebar:hidden", children: count }))] }) }));
54
- return (_jsx("li", { children: collapsed ? (_jsxs(TooltipTrigger, { delay: 300, children: [link, _jsx(Tooltip, { placement: "right", children: count != null ? `${children} (${count})` : children })] })) : (link) }));
78
+ 'group/item relative flex min-h-target w-full cursor-pointer items-center text-sm font-medium text-sidebar-foreground no-underline outline-none', 'group-data-collapsed/sidebar:justify-center'), children: _jsxs("span", { className: cn('relative flex min-h-nav-item w-full min-w-0 items-center gap-3 rounded-md px-3', 'group-data-hovered/item:bg-sidebar-accent group-data-hovered/item:text-sidebar-accent-foreground group-data-focus-visible/item:focus-ring',
79
+ // The current page, as RailItem marks it: the fill, the name in brand at weight 700, and a 3px edge along
80
+ // the start, an inset shadow so it follows the rounded corners. Forced colours drop shadows and fills, so
81
+ // there the edge is a bar in Highlight.
82
+ 'group-aria-[current=page]/item:bg-brand-subtle group-aria-[current=page]/item:font-bold group-aria-[current=page]/item:text-brand', 'group-aria-[current=page]/item:shadow-[inset_3px_0_0_var(--sidebar-primary)]',
83
+ // Hovered, the current page keeps its fill: the hover ground under brand text would be a pair the audit doesn't
84
+ // promise. Stacked variants, so this is more specific than the hover rule whatever order they're written in.
85
+ 'group-data-hovered/item:group-aria-[current=page]/item:bg-brand-subtle group-data-hovered/item:group-aria-[current=page]/item:text-brand', 'forced-colors:group-aria-[current=page]/item:before:absolute forced-colors:group-aria-[current=page]/item:before:inset-y-0 forced-colors:group-aria-[current=page]/item:before:start-0 forced-colors:group-aria-[current=page]/item:before:w-[3px] forced-colors:group-aria-[current=page]/item:before:rounded-s-md forced-colors:group-aria-[current=page]/item:before:bg-[Highlight] forced-colors:group-aria-[current=page]/item:before:forced-color-adjust-none', 'group-data-collapsed/sidebar:w-nav-item group-data-collapsed/sidebar:justify-center group-data-collapsed/sidebar:px-0',
86
+ // The status badge's cut-out follows the ground: the sidebar, the hovered row, the current one.
87
+ '[--nav-cut:var(--sidebar)] group-data-hovered/item:[--nav-cut:var(--sidebar-accent)] group-aria-[current=page]/item:[--nav-cut:var(--brand-subtle)] group-data-hovered/item:group-aria-[current=page]/item:[--nav-cut:var(--brand-subtle)]'), children: [_jsx(NavIcon, { icon: icon, status: status, className: "group-aria-[current=page]/item:text-brand" }), _jsxs("span", { className: "min-w-0 flex-1 truncate group-data-collapsed/sidebar:sr-only", children: [children, count != null && _jsx("span", { className: "sr-only", children: t('count', { count }) }), detail && (_jsx("span", { className: "sr-only", children: t(detailTone === 'warning' ? 'detailWarning' : 'detail', { detail }) })), statusLabel && _jsx("span", { className: "sr-only", children: t('status', { status: statusLabel }) })] }), count != null && (_jsx("span", { "aria-hidden": "true", className: "text-[13px] font-medium text-muted-foreground tabular-nums group-data-collapsed/sidebar:hidden", children: new Intl.NumberFormat(locale).format(count) })), detail && (_jsx(NavDetail, { detail: detail, tone: detailTone, className: "shrink-0 group-data-collapsed/sidebar:hidden" }))] }) }));
88
+ return (_jsx("li", { children: collapsed ? (_jsxs(TooltipTrigger, { delay: 300, children: [link, _jsx(Tooltip, { placement: "right", children: navTooltip(t, children, { count, detail, detailTone, statusLabel }) })] })) : (link) }));
55
89
  }
56
90
  /**
57
91
  * A sidebar row with rows under it. Expanded (and in the phone's Sheet), the row is a button that opens and closes
@@ -70,27 +104,47 @@ export function SidebarDisclosure({ icon, children, items, defaultExpanded, }) {
70
104
  if (collapsed) {
71
105
  return (_jsx("li", { children: _jsxs(MenuTrigger, { children: [_jsxs(TooltipTrigger, { delay: 300, children: [_jsx(RACButton, { className: target, children: _jsxs("span", { className: paint, children: [renderIcon(icon, iconClass), _jsx("span", { className: "sr-only", children: children })] }) }), _jsx(Tooltip, { placement: "right", children: children })] }), _jsx(Popover, { placement: "right top", className: "min-w-56", children: _jsx(Menu, { "aria-label": children, selectionMode: "single", selectedKeys: current ? [current.href] : [], children: items.map((item) => (_jsx(MenuItem, { id: item.href, href: item.href, children: item.label }, item.href))) }) })] }) }));
72
106
  }
73
- return (_jsx("li", { children: _jsxs(RACDisclosure, { defaultExpanded: defaultExpanded ?? !!current, className: "group/disclosure", children: [_jsx(RACButton, { slot: "trigger", className: target, children: _jsxs("span", { className: paint, children: [renderIcon(icon, iconClass), _jsx("span", { className: "min-w-0 flex-1 truncate", children: children }), _jsx(ChevronDown, { "aria-hidden": "true", className: "size-4 shrink-0 text-muted-foreground group-data-expanded/disclosure:rotate-180" })] }) }), _jsx(DisclosurePanel, { children: _jsx("ul", { className: "flex flex-col gap-(--nav-item-gap) pt-(--nav-item-gap)", children: items.map((item) => (_jsx("li", { children: _jsx(Link, { href: item.href, "aria-current": item.isCurrent ? 'page' : undefined, className: "group/sub relative flex min-h-target w-full cursor-pointer items-center text-sm font-medium text-sidebar-foreground no-underline outline-none", children: _jsx("span", { className: cn('relative flex min-h-nav-item w-full min-w-0 items-center rounded-md ps-11 pe-3', 'group-data-hovered/sub:bg-sidebar-accent group-data-hovered/sub:text-sidebar-accent-foreground group-data-focus-visible/sub:focus-ring', 'group-aria-[current=page]/sub:bg-brand-subtle group-aria-[current=page]/sub:font-bold group-aria-[current=page]/sub:text-foreground', 'group-aria-[current=page]/sub:before:absolute group-aria-[current=page]/sub:before:inset-y-2 group-aria-[current=page]/sub:before:left-0 group-aria-[current=page]/sub:before:w-[3px] group-aria-[current=page]/sub:before:rounded-full group-aria-[current=page]/sub:before:bg-sidebar-primary'), children: _jsx("span", { className: "min-w-0 flex-1 truncate", children: item.label }) }) }) }, item.href))) }) })] }) }));
107
+ return (_jsx("li", { children: _jsxs(RACDisclosure, { defaultExpanded: defaultExpanded ?? !!current, className: "group/disclosure", children: [_jsx(RACButton, { slot: "trigger", className: target, children: _jsxs("span", { className: paint, children: [renderIcon(icon, iconClass), _jsx("span", { className: "min-w-0 flex-1 truncate", children: children }), _jsx(ChevronDown, { "aria-hidden": "true", className: "size-4 shrink-0 text-muted-foreground group-data-expanded/disclosure:rotate-180" })] }) }), _jsx(DisclosurePanel, { children: _jsx("ul", { className: "flex flex-col gap-(--nav-item-gap) pt-(--nav-item-gap)", children: items.map((item) => (_jsx("li", { children: _jsx(Link, { href: item.href, "aria-current": item.isCurrent ? 'page' : undefined, className: "group/sub relative flex min-h-target w-full cursor-pointer items-center text-sm font-medium text-sidebar-foreground no-underline outline-none", children: _jsx("span", { className: cn('relative flex min-h-nav-item w-full min-w-0 items-center rounded-md ps-11 pe-3', 'group-data-hovered/sub:bg-sidebar-accent group-data-hovered/sub:text-sidebar-accent-foreground group-data-focus-visible/sub:focus-ring',
108
+ // The current page, as RailItem marks it: the fill, the name in brand at weight 700, and a 3px edge along
109
+ // the start, an inset shadow so it follows the rounded corners. Forced colours drop shadows and fills, so
110
+ // there the edge is a bar in Highlight.
111
+ 'group-aria-[current=page]/sub:bg-brand-subtle group-aria-[current=page]/sub:font-bold group-aria-[current=page]/sub:text-brand', 'group-aria-[current=page]/sub:shadow-[inset_3px_0_0_var(--sidebar-primary)]', 'group-data-hovered/sub:group-aria-[current=page]/sub:bg-brand-subtle group-data-hovered/sub:group-aria-[current=page]/sub:text-brand', 'forced-colors:group-aria-[current=page]/sub:before:absolute forced-colors:group-aria-[current=page]/sub:before:inset-y-0 forced-colors:group-aria-[current=page]/sub:before:start-0 forced-colors:group-aria-[current=page]/sub:before:w-[3px] forced-colors:group-aria-[current=page]/sub:before:rounded-s-md forced-colors:group-aria-[current=page]/sub:before:bg-[Highlight] forced-colors:group-aria-[current=page]/sub:before:forced-color-adjust-none'), children: _jsx("span", { className: "min-w-0 flex-1 truncate", children: item.label }) }) }) }, item.href))) }) })] }) }));
74
112
  }
75
113
  /** A sidebar row that opens a menu: the workspace switcher at the top, the account menu at the foot. */
76
114
  export function SidebarSwitcher({ leading, title, subtitle, menuLabel, children, onAction, selectedKey, placement = 'bottom start', }) {
77
115
  const collapsed = useSidebarCollapsed();
78
116
  // Named by its content, not an aria-label: the title and subtitle as shown, then the menu's purpose, so the
79
- // name always contains the visible text (2.5.3). The commas are for the name; the layout already separates them.
117
+ // name always contains the visible text (2.5.3). They are a list in the locale's punctuation, which is for the
118
+ // name only; the layout already separates them.
119
+ const { locale } = useLocale();
120
+ const parts = new Intl.ListFormat(locale, { type: 'unit', style: 'short' }).formatToParts(subtitle ? [title, subtitle, menuLabel] : [title, menuLabel]);
121
+ let element = -1;
80
122
  const button = (_jsx(RACButton, { className: cn(
81
123
  // Layer 1: the target. Collapsed, it keeps at least --target-min across the rail.
82
- 'group/switcher flex min-h-target w-full min-w-target cursor-pointer items-center text-left outline-none', 'group-data-collapsed/sidebar:justify-center'), children: _jsxs("span", { className: cn('flex min-h-[calc(var(--nav-item-height)+12px)] w-full min-w-0 items-center gap-3 rounded-md px-2', 'group-data-hovered/switcher:bg-sidebar-accent group-data-pressed/switcher:bg-sidebar-accent group-data-focus-visible/switcher:focus-ring', 'group-data-collapsed/sidebar:w-nav-item group-data-collapsed/sidebar:justify-center group-data-collapsed/sidebar:px-0'), children: [leading, _jsxs("span", { className: "flex min-w-0 flex-1 flex-col group-data-collapsed/sidebar:sr-only", children: [_jsx("span", { className: "truncate text-sm leading-5 font-bold", children: title }), subtitle && (_jsxs(_Fragment, { children: [_jsx("span", { className: "sr-only", children: ", " }), _jsx("span", { className: "truncate text-[13px] leading-5 text-muted-foreground", children: subtitle })] })), _jsxs("span", { className: "sr-only", children: [", ", menuLabel] })] }), _jsx(ChevronsUpDown, { "aria-hidden": "true", className: "size-4 shrink-0 text-muted-foreground group-data-collapsed/sidebar:hidden" })] }) }));
124
+ 'group/switcher flex min-h-target w-full min-w-target cursor-pointer items-center text-left outline-none', 'group-data-collapsed/sidebar:justify-center'), children: _jsxs("span", { className: cn('flex min-h-[calc(var(--nav-item-height)+12px)] w-full min-w-0 items-center gap-3 rounded-md px-2', 'group-data-hovered/switcher:bg-sidebar-accent group-data-pressed/switcher:bg-sidebar-accent group-data-focus-visible/switcher:focus-ring', 'group-data-collapsed/sidebar:w-nav-item group-data-collapsed/sidebar:justify-center group-data-collapsed/sidebar:px-0'), children: [leading, _jsx("span", { className: "flex min-w-0 flex-1 flex-col group-data-collapsed/sidebar:sr-only", children: parts.map((part, i) => {
125
+ if (part.type === 'literal')
126
+ return (_jsx("span", { className: "sr-only", children: part.value }, i));
127
+ element++;
128
+ return element === 0 ? (_jsx("span", { className: "truncate text-sm leading-5 font-bold", children: part.value }, i)) : subtitle && element === 1 ? (_jsx("span", { className: "truncate text-[13px] leading-5 text-muted-foreground", children: part.value }, i)) : (_jsx("span", { className: "sr-only", children: part.value }, i));
129
+ }) }), _jsx(ChevronsUpDown, { "aria-hidden": "true", className: "size-4 shrink-0 text-muted-foreground group-data-collapsed/sidebar:hidden" })] }) }));
83
130
  return (_jsxs(MenuTrigger, { children: [collapsed ? (_jsxs(TooltipTrigger, { delay: 300, children: [button, _jsx(Tooltip, { placement: "right", children: title })] })) : (button), _jsx(Popover, { placement: collapsed ? 'right top' : placement, className: "min-w-64", children: _jsx(Menu, { onAction: onAction, selectionMode: selectedKey != null ? 'single' : undefined, selectedKeys: selectedKey != null ? [selectedKey] : undefined, children: children }) })] }));
84
131
  }
85
- const sidebarTriggerLabels = {
132
+ export const sidebarTriggerLabels = {
86
133
  openNavigation: 'Open navigation',
87
134
  toggleSidebar: 'Toggle sidebar',
88
135
  };
89
- /** Collapses the sidebar to icons, or opens it as a Sheet on phones. */
136
+ /**
137
+ * Collapses the sidebar to icons, or opens it as a Sheet on phones. In a shell with `navigation="rail"` there is
138
+ * nothing to collapse, so it is only there on phones, where it opens the rail's Sheet: before the shell has measured
139
+ * itself a container query hides it from bp-md up, and once it has (or its size is forced) it isn't rendered there.
140
+ */
90
141
  export function SidebarTrigger({ labels: given, className, }) {
91
- const { size, sidebarCollapsed, toggleSidebar, mobileNavOpen, setMobileNavOpen } = useShell();
92
- const mobile = size === 'mobile';
93
- const labels = withLabels(sidebarTriggerLabels, given);
94
- return (_jsx(IconButton, { className: className, label: mobile ? labels.openNavigation : labels.toggleSidebar, icon: mobile ? MenuIcon : PanelLeft, "aria-expanded": mobile ? mobileNavOpen : !sidebarCollapsed, "aria-haspopup": mobile ? 'dialog' : undefined, onPress: () => (mobile ? setMobileNavOpen(true) : toggleSidebar()) }));
142
+ const { size, sidebarCollapsed, toggleSidebar, mobileNavOpen, setMobileNavOpen, navigation, isSizeForced, } = useShell();
143
+ const rail = navigation === 'rail';
144
+ const mobile = size === 'mobile' || (rail && !isSizeForced);
145
+ const t = useLabels('sidebarTrigger', sidebarTriggerLabels, given);
146
+ if (rail && isSizeForced && size !== 'mobile')
147
+ return null;
148
+ return (_jsx(IconButton, { className: cn(rail && !isSizeForced && '@min-[48rem]/shell:hidden', className), label: mobile ? t('openNavigation') : t('toggleSidebar'), icon: mobile ? MenuIcon : PanelLeft, "aria-expanded": mobile ? mobileNavOpen : !sidebarCollapsed, "aria-haspopup": mobile ? 'dialog' : undefined, onPress: () => (mobile ? setMobileNavOpen(true) : toggleSidebar()) }));
95
149
  }
96
150
  //# sourceMappingURL=sidebar.js.map