@crewlethq/ui 0.12.0 → 0.13.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 (157) hide show
  1. package/README.md +66 -5
  2. package/dist/AppShell/index.js +2 -1
  3. package/dist/Avatar/index.js +1 -1
  4. package/dist/{Avatar-QHYOFID3.css → Avatar-BPJ36QZR.css} +1 -1
  5. package/dist/AvatarPicker/index.js +6 -6
  6. package/dist/Breadcrumb/Breadcrumb.d.ts +68 -0
  7. package/dist/Breadcrumb/index.d.ts +3 -0
  8. package/dist/Breadcrumb/index.js +3 -0
  9. package/dist/Breadcrumb-YMWVM3SV.css +193 -0
  10. package/dist/ChoiceButtons/ChoiceButtons.d.ts +43 -0
  11. package/dist/ChoiceButtons/index.d.ts +3 -0
  12. package/dist/ChoiceButtons/index.js +3 -0
  13. package/dist/ChoiceButtons-DJ5EOGUA.css +127 -0
  14. package/dist/Combobox/index.js +2 -2
  15. package/dist/CommandPalette/CommandPalette.d.ts +48 -1
  16. package/dist/CommandPalette/index.js +3 -3
  17. package/dist/{CommandPalette-C5AMKGN2.css → CommandPalette-HBTNKHZQ.css} +55 -2
  18. package/dist/DataTable/index.js +4 -4
  19. package/dist/DataView/index.js +11 -11
  20. package/dist/DateTimePicker/index.js +2 -2
  21. package/dist/Document/Document.d.ts +56 -0
  22. package/dist/Document/index.d.ts +3 -0
  23. package/dist/Document/index.js +2 -0
  24. package/dist/Document-PQGXJHB7.css +288 -0
  25. package/dist/EntityChip/index.js +1 -1
  26. package/dist/Highlight/Highlight.d.ts +45 -0
  27. package/dist/Highlight/index.d.ts +3 -0
  28. package/dist/Highlight/index.js +2 -0
  29. package/dist/Highlight-DXLV2R6L.css +29 -0
  30. package/dist/HuePicker/index.js +3 -3
  31. package/dist/ImageUpload/index.js +2 -2
  32. package/dist/{ImageUpload-L2GE4JUM.css → ImageUpload-NVGMN7DP.css} +9 -6
  33. package/dist/InboxList/InboxList.d.ts +116 -0
  34. package/dist/InboxList/index.d.ts +3 -0
  35. package/dist/InboxList/index.js +5 -0
  36. package/dist/InboxList-YMFWKGGS.css +252 -0
  37. package/dist/KeyChip/KeyChip.d.ts +21 -0
  38. package/dist/KeyChip/index.d.ts +3 -0
  39. package/dist/KeyChip/index.js +2 -0
  40. package/dist/KeyChip-4XJU4BBE.css +30 -0
  41. package/dist/Link/Link.d.ts +17 -2
  42. package/dist/Link/index.js +1 -1
  43. package/dist/{Link-6BEFNMN2.css → Link-WPMVLKLY.css} +36 -1
  44. package/dist/ListInput/index.js +2 -2
  45. package/dist/Listbox/index.d.ts +2 -0
  46. package/dist/Listbox/index.js +1 -1
  47. package/dist/Listbox/useHighlightRest.d.ts +35 -0
  48. package/dist/MarkdownEditor/MarkdownEditor.d.ts +93 -0
  49. package/dist/MarkdownEditor/format.d.ts +34 -0
  50. package/dist/MarkdownEditor/index.d.ts +5 -0
  51. package/dist/MarkdownEditor/index.js +12 -0
  52. package/dist/MarkdownEditor-ZLSHTOIZ.css +146 -0
  53. package/dist/Modal/Modal.d.ts +5 -1
  54. package/dist/Modal/index.js +1 -1
  55. package/dist/{Modal-BNT3TDV2.css → Modal-3YJ2GG2T.css} +33 -0
  56. package/dist/OrgLabel/index.js +1 -1
  57. package/dist/OrgNode/index.js +1 -1
  58. package/dist/OrgTable/index.js +5 -5
  59. package/dist/Outline/Outline.d.ts +72 -0
  60. package/dist/Outline/index.d.ts +3 -0
  61. package/dist/Outline/index.js +2 -0
  62. package/dist/Outline-VCXKKU3E.css +78 -0
  63. package/dist/Popover/Popover.d.ts +21 -1
  64. package/dist/Popover/index.js +1 -1
  65. package/dist/ReadingPane/ReadingPane.d.ts +95 -0
  66. package/dist/ReadingPane/index.d.ts +3 -0
  67. package/dist/ReadingPane/index.js +4 -0
  68. package/dist/ReadingPane-5UOLPWY5.css +194 -0
  69. package/dist/SegmentedControl/SegmentedControl.d.ts +9 -1
  70. package/dist/SegmentedControl/index.js +3 -3
  71. package/dist/{SegmentedControl-76HODRBT.css → SegmentedControl-PKBLJVSA.css} +18 -0
  72. package/dist/Select/Select.d.ts +22 -0
  73. package/dist/Select/index.js +2 -2
  74. package/dist/{Select-4ANVLHUY.css → Select-XMZ44RWY.css} +24 -0
  75. package/dist/SidebarNav/index.js +2 -1
  76. package/dist/{SidebarNav-X5NLZNWA.css → SidebarNav-KF66MLOM.css} +4 -19
  77. package/dist/Switcher/Switcher.d.ts +151 -0
  78. package/dist/Switcher/index.d.ts +3 -0
  79. package/dist/Switcher/index.js +16 -0
  80. package/dist/Switcher-B44Q4WF4.css +346 -0
  81. package/dist/Tabs/Tabs.d.ts +7 -1
  82. package/dist/Tabs/index.js +1 -1
  83. package/dist/{Tabs-5I5Q5KKM.css → Tabs-ITJYJFMU.css} +49 -0
  84. package/dist/TagsInput/index.js +3 -3
  85. package/dist/ThemeGallery/ThemeGallery.d.ts +65 -0
  86. package/dist/ThemeGallery/index.d.ts +3 -0
  87. package/dist/ThemeGallery/index.js +6 -0
  88. package/dist/ThemeGallery-NITASULQ.css +93 -0
  89. package/dist/ThemeSwatch/ThemeSwatch.d.ts +71 -0
  90. package/dist/ThemeSwatch/index.d.ts +3 -0
  91. package/dist/ThemeSwatch/index.js +2 -0
  92. package/dist/ThemeSwatch-4DL457ZB.css +256 -0
  93. package/dist/ThemeSwitcher/ThemeSwitcher.d.ts +5 -1
  94. package/dist/ThemeSwitcher/index.js +4 -4
  95. package/dist/ThemeToggle/ThemeToggle.d.ts +1 -1
  96. package/dist/ThemeToggle/index.js +1 -1
  97. package/dist/TimeWindowPicker/index.js +4 -4
  98. package/dist/Toaster/index.js +1 -1
  99. package/dist/{Toaster-NVDCEIP5.css → Toaster-KZ7YE2HA.css} +1 -0
  100. package/dist/Tree/index.d.ts +1 -1
  101. package/dist/Tree/index.js +1 -1
  102. package/dist/Tree/useTreeState.d.ts +37 -7
  103. package/dist/TreeCanvas/index.js +2 -2
  104. package/dist/TreeGrid/TreeGrid.d.ts +22 -2
  105. package/dist/TreeGrid/index.js +2 -2
  106. package/dist/{TreeGrid-NSW7VMOX.css → TreeGrid-FBX3MSJA.css} +19 -0
  107. package/dist/{chunk-EBBAAQDY.js → chunk-26PEPONN.js} +3 -1
  108. package/dist/{chunk-HV6IZFBK.js → chunk-4FFHJBEN.js} +2 -2
  109. package/dist/{chunk-JHQUT4WJ.js → chunk-4LDTQWS7.js} +1 -1
  110. package/dist/{chunk-GXYNEDNA.js → chunk-4Y7WKXAW.js} +1 -1
  111. package/dist/{chunk-MCPKHYMC.js → chunk-6LXMLQUM.js} +17 -4
  112. package/dist/{chunk-3UK4VFSK.js → chunk-6NIZCQUM.js} +1 -1
  113. package/dist/{chunk-ORNOOMVY.js → chunk-6TSZCBID.js} +2 -2
  114. package/dist/{chunk-ZMJHK7DR.js → chunk-75WOCYGC.js} +2 -2
  115. package/dist/chunk-77IEHFY4.js +299 -0
  116. package/dist/chunk-A5YKNRG6.js +94 -0
  117. package/dist/{chunk-HLBRPES3.js → chunk-B2CITO6R.js} +2 -2
  118. package/dist/{chunk-TEEJSTFG.js → chunk-CF6DTETT.js} +1 -1
  119. package/dist/{chunk-LAQK4V3Z.js → chunk-DJMLHCLD.js} +1 -1
  120. package/dist/{chunk-6MU7HNB4.js → chunk-F73MC7EZ.js} +47 -2
  121. package/dist/{chunk-ZPSOF6VL.js → chunk-FBSEJO2C.js} +1 -1
  122. package/dist/chunk-FYTIJM2T.js +40 -0
  123. package/dist/{chunk-ZHOM65MI.js → chunk-GG2QEZO2.js} +2 -2
  124. package/dist/chunk-H6VKCIWV.js +41 -0
  125. package/dist/{chunk-XV65NTIT.js → chunk-HKGS67VG.js} +94 -41
  126. package/dist/chunk-IG75YMUN.js +22 -0
  127. package/dist/{chunk-JCNUQ3US.js → chunk-JDPAA2JU.js} +1 -1
  128. package/dist/chunk-KLIEPFEZ.js +95 -0
  129. package/dist/{chunk-7LD6P32I.js → chunk-L3CTFFLZ.js} +2 -18
  130. package/dist/{chunk-UG5ZMAXW.js → chunk-LELD5KPA.js} +1 -1
  131. package/dist/chunk-MNFU2RNT.js +74 -0
  132. package/dist/{chunk-S3BCVWTU.js → chunk-MY374UUD.js} +49 -31
  133. package/dist/{chunk-K5Q2DK3P.js → chunk-NXDEPUVG.js} +1 -1
  134. package/dist/{chunk-PDUZO35I.js → chunk-O2ZDZZS7.js} +15 -6
  135. package/dist/{chunk-5WEVLIB6.js → chunk-RZ7RGECB.js} +1 -1
  136. package/dist/chunk-S3IENQ3E.js +336 -0
  137. package/dist/{chunk-WEIINZPA.js → chunk-SBRE7IML.js} +6 -2
  138. package/dist/{chunk-IVFJD7WY.js → chunk-UCCC7YVZ.js} +3 -2
  139. package/dist/{chunk-IINFVK3W.js → chunk-UZPHPW3Z.js} +6 -6
  140. package/dist/chunk-VWNC2L6W.js +110 -0
  141. package/dist/chunk-VYY56AMX.js +2 -0
  142. package/dist/chunk-WI6K5M5X.js +24 -0
  143. package/dist/{chunk-GOUKOXJK.js → chunk-WY2NLRZN.js} +3 -3
  144. package/dist/{chunk-SEFMO2EV.js → chunk-XFE5W2I4.js} +44 -5
  145. package/dist/chunk-XYCADWZN.js +100 -0
  146. package/dist/chunk-YSJC7AZC.js +62 -0
  147. package/dist/chunk-ZAJHGDAW.js +93 -0
  148. package/dist/generated/index.d.ts +12 -0
  149. package/dist/generated/index.js +65 -52
  150. package/dist/index.js +65 -52
  151. package/dist/styles.css +2449 -161
  152. package/dist/utils/highlight.d.ts +17 -0
  153. package/dist/utils/useMediaMatch.d.ts +12 -0
  154. package/package.json +3 -3
  155. package/dist/chunk-UAQIUSSK.js +0 -2
  156. package/dist/{chunk-74OVQWA5.js → chunk-6AR2T5Y4.js} +0 -0
  157. package/dist/{chunk-GARACZAH.js → chunk-LZP7FGT7.js} +1 -1
@@ -0,0 +1,336 @@
1
+ import { Skeleton } from './chunk-W5J7R5EB.js';
2
+ import { Modal } from './chunk-CF6DTETT.js';
3
+ import { useListbox, useOptionKeys } from './chunk-F73MC7EZ.js';
4
+ import { Input } from './chunk-FTEJPHBI.js';
5
+ import { KeyChip, highlightMatch } from './chunk-FYTIJM2T.js';
6
+ import { useMediaMatch } from './chunk-IG75YMUN.js';
7
+ import { announce } from './chunk-RSAQOKH3.js';
8
+ import { Popover } from './chunk-O2ZDZZS7.js';
9
+ import { focusables, isComposing } from './chunk-R25RGPUB.js';
10
+ import { VisuallyHidden } from './chunk-BZFWDIV6.js';
11
+ import { cx } from './chunk-ODEQTPFM.js';
12
+ import './Switcher-B44Q4WF4.css';
13
+ import { forwardRef, useId, useRef, useCallback, useMemo, useEffect } from 'react';
14
+ import { ChevronDownGlyph, SearchGlyph } from '@crewlethq/icons/glyphs';
15
+ import { breakpoint } from '@crewlethq/tokens';
16
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
17
+
18
+ var PHONE = `(width < ${breakpoint.phone})`;
19
+ var TOUCH = "(pointer: coarse)";
20
+ var DROP = 6;
21
+ var SKELETON_NAMES = [72, 128, 96, 150, 110, 84];
22
+ function Switcher({ label, open, onOpenChange, trigger, className, ...panel }) {
23
+ const phone = useMediaMatch(PHONE);
24
+ const touch = useMediaMatch(TOUCH);
25
+ const triggerId = useId();
26
+ const body = useRef(null);
27
+ const shift = useRef(0);
28
+ const place = phone ? "sheet" : trigger ? "anchored" : "centered";
29
+ const close = useCallback(() => onOpenChange(false), [onOpenChange]);
30
+ const initialFocus = useCallback(() => touch ? body.current : null, [touch]);
31
+ const alignUnder = useCallback((surface, anchor) => {
32
+ const mark = anchor.querySelector(".crewlet-key-chip, .crewlet-switcher-trigger__label");
33
+ const lead = surface.querySelector(".crewlet-switcher__lead");
34
+ if (mark && lead) {
35
+ const from = mark.getBoundingClientRect().left - anchor.getBoundingClientRect().left;
36
+ const to = lead.getBoundingClientRect().left - surface.getBoundingClientRect().left;
37
+ shift.current = from - to;
38
+ }
39
+ return shift.current;
40
+ }, []);
41
+ function done() {
42
+ close();
43
+ if (place === "anchored") document.getElementById(triggerId)?.focus();
44
+ }
45
+ function leave(back) {
46
+ const button = document.getElementById(triggerId);
47
+ close();
48
+ if (!button) return;
49
+ const surface = body.current;
50
+ const next = back ? void 0 : focusables(document.body).find(
51
+ (el) => (button.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0 && !button.contains(el) && !surface?.contains(el)
52
+ );
53
+ (next ?? button).focus();
54
+ }
55
+ const content = /* @__PURE__ */ jsx(
56
+ SwitcherPanel,
57
+ {
58
+ ...panel,
59
+ panelRef: body,
60
+ onDone: done,
61
+ onLeave: place === "anchored" ? leave : void 0
62
+ }
63
+ );
64
+ if (place === "anchored" && trigger) {
65
+ return /* @__PURE__ */ jsx(
66
+ Popover,
67
+ {
68
+ label,
69
+ open,
70
+ onOpenChange: (next) => onOpenChange(next),
71
+ alignOffset: alignUnder,
72
+ sideOffset: DROP,
73
+ initialFocus,
74
+ className: cx("crewlet-switcher", "crewlet-switcher--anchored", className),
75
+ trigger: (isOpen, toggle) => /* @__PURE__ */ jsx(SwitcherButton, { ...trigger, id: triggerId, expanded: isOpen, onToggle: toggle, onOpen: () => onOpenChange(true) }),
76
+ children: content
77
+ }
78
+ );
79
+ }
80
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
81
+ trigger ? /* @__PURE__ */ jsx(
82
+ SwitcherButton,
83
+ {
84
+ ...trigger,
85
+ id: triggerId,
86
+ expanded: open,
87
+ onToggle: () => onOpenChange(!open),
88
+ onOpen: () => onOpenChange(true)
89
+ }
90
+ ) : null,
91
+ /* @__PURE__ */ jsx(
92
+ Modal,
93
+ {
94
+ open,
95
+ onClose: close,
96
+ placement: place === "sheet" ? "bottom" : "top",
97
+ title: place === "sheet" ? label : void 0,
98
+ ariaLabel: label,
99
+ showCloseButton: place === "sheet",
100
+ flush: true,
101
+ initialFocus,
102
+ className: cx("crewlet-switcher", `crewlet-switcher--${place}`, className),
103
+ children: content
104
+ }
105
+ )
106
+ ] });
107
+ }
108
+ var SwitcherButton = forwardRef(function SwitcherButton2({ label, keyChip, tag, mono, title, id, expanded, onToggle, onOpen, ...rest }, ref) {
109
+ return /* @__PURE__ */ jsxs(
110
+ "button",
111
+ {
112
+ type: "button",
113
+ "aria-haspopup": "dialog",
114
+ "aria-expanded": expanded,
115
+ ...rest,
116
+ ref,
117
+ id,
118
+ title,
119
+ className: cx("crewlet-switcher-trigger", mono && "is-mono"),
120
+ onClick: onToggle,
121
+ onKeyDown: (event) => {
122
+ if (event.key !== "ArrowDown" || expanded || event.altKey || event.ctrlKey || event.metaKey) return;
123
+ event.preventDefault();
124
+ onOpen();
125
+ },
126
+ children: [
127
+ keyChip ? /* @__PURE__ */ jsxs(Fragment, { children: [
128
+ /* @__PURE__ */ jsx(KeyChip, { children: keyChip }),
129
+ " "
130
+ ] }) : null,
131
+ /* @__PURE__ */ jsx("span", { className: "crewlet-switcher-trigger__label", children: label }),
132
+ tag !== void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
133
+ " ",
134
+ /* @__PURE__ */ jsx("span", { className: "crewlet-switcher-trigger__tag", children: tag })
135
+ ] }) : null,
136
+ /* @__PURE__ */ jsx(ChevronDownGlyph, { size: "sm", className: "crewlet-switcher-trigger__chevron", "aria-hidden": "true" })
137
+ ]
138
+ }
139
+ );
140
+ });
141
+ function SwitcherPanel({
142
+ query,
143
+ onQueryChange,
144
+ placeholder = "Search",
145
+ listLabel,
146
+ pinned,
147
+ note,
148
+ message,
149
+ groups = [],
150
+ footer = [],
151
+ loading = false,
152
+ loadingLabel = "Loading",
153
+ announcement,
154
+ panelRef,
155
+ onDone,
156
+ onLeave
157
+ }) {
158
+ const id = useId();
159
+ const footerId = `${id}-footer`;
160
+ const noteId = `${id}-note`;
161
+ const titleId = `${id}-message`;
162
+ const bodyId = `${id}-message-body`;
163
+ const flat = useMemo(
164
+ () => [...pinned ? [pinned] : [], ...message?.actions ?? [], ...groups.flatMap((group) => group.options), ...footer],
165
+ [pinned, message, groups, footer]
166
+ );
167
+ const held = useCallback((index2) => Boolean(flat[index2]?.held), [flat]);
168
+ const listbox = useListbox({
169
+ id,
170
+ open: true,
171
+ count: flat.length,
172
+ // The surface around the list owns Escape and a press outside it.
173
+ popup: false,
174
+ // ENTER TAKES A ROW THE WAY A PRESS DOES: through the row's own click, so
175
+ // a link with nothing else to do is followed as a link.
176
+ onCommit: (index2) => document.getElementById(listbox.optionId(index2))?.click(),
177
+ onClose: onDone
178
+ });
179
+ const keys = useOptionKeys({ listbox, count: flat.length, disabled: held, ends: false });
180
+ const { active, setActive } = listbox;
181
+ const { step } = keys;
182
+ useEffect(() => {
183
+ if (active >= 0 && held(active)) setActive(step(active, 1));
184
+ }, [active, held, setActive, step]);
185
+ useEffect(() => setActive(loading ? -1 : 0), [loading, setActive]);
186
+ useEffect(() => {
187
+ if (announcement) announce(announcement);
188
+ }, [announcement]);
189
+ function onKeyDown(event) {
190
+ if (isComposing(event)) return;
191
+ if (keys.onKeyDown(event)) return;
192
+ if (event.key === "Enter" && (event.metaKey || event.ctrlKey) && !event.altKey && !event.shiftKey) {
193
+ const option = flat[active];
194
+ if (option?.href && !option.held) {
195
+ event.preventDefault();
196
+ window.open(option.href, "_blank", "noopener");
197
+ }
198
+ return;
199
+ }
200
+ if (listbox.onKeyDown(event)) return;
201
+ if (event.key === "Tab" && onLeave && !event.altKey && !event.ctrlKey && !event.metaKey) {
202
+ event.preventDefault();
203
+ onLeave(event.shiftKey);
204
+ }
205
+ }
206
+ function onRowClick(event, option) {
207
+ if (option.held) {
208
+ event.preventDefault();
209
+ return;
210
+ }
211
+ if (option.href && (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) return;
212
+ if (option.onSelect) event.preventDefault();
213
+ if (!option.keepsOpen) onDone();
214
+ option.onSelect?.();
215
+ }
216
+ let index = -1;
217
+ function row(option, highlight) {
218
+ index += 1;
219
+ const mine = index;
220
+ const name = option.accessibleLabel && option.held ? `${option.accessibleLabel}, ${option.held}` : option.accessibleLabel;
221
+ const props = {
222
+ id: listbox.optionId(mine),
223
+ role: "option",
224
+ className: cx("crewlet-listbox__option", "crewlet-switcher__option", option.current && "is-current"),
225
+ "aria-selected": mine === active,
226
+ "aria-current": option.current ? "page" : void 0,
227
+ "aria-disabled": option.held ? true : void 0,
228
+ "aria-label": name,
229
+ // The press is prevented so focus stays in the field; a middle press is
230
+ // left alone, because it is what opens a link in a new tab.
231
+ onMouseDown: (event) => {
232
+ if (event.button === 0) event.preventDefault();
233
+ },
234
+ onMouseEnter: () => {
235
+ if (!option.held) setActive(mine);
236
+ },
237
+ onClick: (event) => onRowClick(event, option)
238
+ };
239
+ const words = /* @__PURE__ */ jsxs(Fragment, { children: [
240
+ /* @__PURE__ */ jsx("span", { className: "crewlet-switcher__lead", "aria-hidden": option.keyChip ? void 0 : true, children: option.keyChip ? /* @__PURE__ */ jsx(KeyChip, { highlight: highlight ? query : void 0, children: option.keyChip }) : option.icon }),
241
+ " ",
242
+ /* @__PURE__ */ jsx("span", { className: "crewlet-switcher__label", children: highlight ? highlightMatch(option.label, query, "word", "crewlet-switcher__match") : option.label }),
243
+ option.meta !== void 0 || option.held ? /* @__PURE__ */ jsxs(Fragment, { children: [
244
+ " ",
245
+ /* @__PURE__ */ jsxs("span", { className: "crewlet-switcher__meta", children: [
246
+ option.meta,
247
+ option.held
248
+ ] })
249
+ ] }) : null
250
+ ] });
251
+ return option.href && !option.held ? /* @__PURE__ */ jsx("a", { ...props, href: option.href, tabIndex: -1, children: words }, option.id) : /* @__PURE__ */ jsx("div", { ...props, children: words }, option.id);
252
+ }
253
+ const follows = Boolean(note || message || groups.length > 0 || loading);
254
+ const describedBy = [note ? noteId : "", message ? titleId : "", message?.body ? bodyId : ""].filter(Boolean).join(" ");
255
+ return /* @__PURE__ */ jsxs("div", { ref: panelRef, className: "crewlet-switcher__panel", tabIndex: -1, children: [
256
+ /* @__PURE__ */ jsx(
257
+ Input,
258
+ {
259
+ appearance: "command",
260
+ containerClassName: "crewlet-switcher__search",
261
+ leading: /* @__PURE__ */ jsx(SearchGlyph, { size: "sm" }),
262
+ value: query,
263
+ onChange: (event) => {
264
+ onQueryChange(event.target.value);
265
+ setActive(0);
266
+ },
267
+ onClear: () => {
268
+ onQueryChange("");
269
+ setActive(0);
270
+ },
271
+ clearLabel: "Clear the search",
272
+ onKeyDown,
273
+ placeholder,
274
+ role: "combobox",
275
+ "aria-label": placeholder,
276
+ "aria-expanded": true,
277
+ "aria-controls": listbox.listId,
278
+ "aria-autocomplete": "list",
279
+ "aria-activedescendant": active >= 0 ? listbox.optionId(active) : void 0,
280
+ "aria-describedby": describedBy || void 0,
281
+ autoComplete: "off",
282
+ spellCheck: false
283
+ }
284
+ ),
285
+ /* @__PURE__ */ jsxs("div", { className: "crewlet-switcher__scroller", ref: listbox.listRef, children: [
286
+ /* @__PURE__ */ jsxs(
287
+ "div",
288
+ {
289
+ id: listbox.listId,
290
+ role: "listbox",
291
+ "aria-label": listLabel,
292
+ "aria-owns": footer.length > 0 ? footerId : void 0,
293
+ "aria-busy": loading || void 0,
294
+ className: "crewlet-switcher__list",
295
+ children: [
296
+ pinned ? row(pinned, false) : null,
297
+ pinned && follows ? /* @__PURE__ */ jsx("div", { className: "crewlet-listbox__separator", "aria-hidden": "true" }) : null,
298
+ note ? /* @__PURE__ */ jsx("div", { role: "presentation", id: noteId, className: "crewlet-switcher__note", children: note }) : null,
299
+ message ? (
300
+ // A MESSAGE NAMES WHAT CAN BE DONE ABOUT IT: its actions are a group
301
+ // labelled by it, so a row reached by the arrows is heard with it.
302
+ /* @__PURE__ */ jsxs("div", { role: "group", "aria-labelledby": titleId, "aria-describedby": message.body ? bodyId : void 0, children: [
303
+ /* @__PURE__ */ jsxs("div", { role: "presentation", className: "crewlet-switcher__message", children: [
304
+ /* @__PURE__ */ jsx("span", { id: titleId, className: "crewlet-switcher__message-title", children: message.title }),
305
+ message.body ? /* @__PURE__ */ jsx("span", { id: bodyId, className: "crewlet-switcher__message-body", children: message.body }) : null
306
+ ] }),
307
+ (message.actions ?? []).map((option) => row(option, false))
308
+ ] })
309
+ ) : null,
310
+ groups.map((group) => (
311
+ // Named by its heading's own words, not the figure beside them.
312
+ /* @__PURE__ */ jsxs("div", { role: "group", "aria-labelledby": `${id}-group-${group.id}`, children: [
313
+ /* @__PURE__ */ jsxs("div", { role: "presentation", className: "crewlet-listbox__heading crewlet-switcher__heading", children: [
314
+ /* @__PURE__ */ jsx("span", { id: `${id}-group-${group.id}`, children: group.label }),
315
+ group.count !== void 0 ? /* @__PURE__ */ jsx("span", { className: "crewlet-switcher__count", children: group.count }) : null
316
+ ] }),
317
+ group.options.map((option) => row(option, true))
318
+ ] }, group.id)
319
+ ))
320
+ ]
321
+ }
322
+ ),
323
+ loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
324
+ /* @__PURE__ */ jsx("div", { className: "crewlet-switcher__skeleton", "aria-hidden": "true", children: SKELETON_NAMES.map((width) => /* @__PURE__ */ jsxs("div", { className: "crewlet-switcher__skeleton-row", children: [
325
+ /* @__PURE__ */ jsx("span", { className: "crewlet-switcher__lead", children: /* @__PURE__ */ jsx(Skeleton, { variant: "box", width: 34, height: 10, borderRadius: 4 }) }),
326
+ /* @__PURE__ */ jsx(Skeleton, { variant: "box", width, height: 10, borderRadius: 4 }),
327
+ /* @__PURE__ */ jsx("span", { className: "crewlet-switcher__meta", children: /* @__PURE__ */ jsx(Skeleton, { variant: "box", width: 14, height: 10, borderRadius: 4 }) })
328
+ ] }, width)) }),
329
+ /* @__PURE__ */ jsx(VisuallyHidden, { children: /* @__PURE__ */ jsx("span", { role: "status", children: loadingLabel }) })
330
+ ] }) : null
331
+ ] }),
332
+ footer.length > 0 ? /* @__PURE__ */ jsx("div", { id: footerId, role: "group", className: "crewlet-switcher__footer", children: footer.map((option) => row(option, false)) }) : null
333
+ ] });
334
+ }
335
+
336
+ export { Switcher };
@@ -1,5 +1,5 @@
1
1
  import { useThemePreference, useDensityPreference } from './chunk-K3OWD7BJ.js';
2
- import { SegmentedControl } from './chunk-EBBAAQDY.js';
2
+ import { SegmentedControl } from './chunk-26PEPONN.js';
3
3
  import { SunGlyph, MonitorGlyph, MoonGlyph } from '@crewlethq/icons/glyphs';
4
4
  import { jsx } from 'react/jsx-runtime';
5
5
 
@@ -32,11 +32,12 @@ function StoredThemeSwitcher({
32
32
  function ThemeRow({
33
33
  value,
34
34
  onChange,
35
- label = "Theme",
35
+ label = "Mode",
36
36
  lightLabel = "Light",
37
37
  systemLabel = "Follow the system",
38
38
  darkLabel = "Dark",
39
39
  size = "sm",
40
+ fill,
40
41
  className
41
42
  }) {
42
43
  return /* @__PURE__ */ jsx(
@@ -45,6 +46,7 @@ function ThemeRow({
45
46
  label,
46
47
  semantics: "radio",
47
48
  size,
49
+ fill,
48
50
  className,
49
51
  value,
50
52
  onValueChange: onChange,
@@ -93,6 +95,7 @@ function DensityRow({
93
95
  normalMark = "M",
94
96
  comfortableMark = "L",
95
97
  size = "sm",
98
+ fill,
96
99
  className
97
100
  }) {
98
101
  return /* @__PURE__ */ jsx(
@@ -101,6 +104,7 @@ function DensityRow({
101
104
  label,
102
105
  semantics: "radio",
103
106
  size,
107
+ fill,
104
108
  className,
105
109
  value,
106
110
  onValueChange: onChange,
@@ -1,6 +1,7 @@
1
+ import { KeyChip } from './chunk-FYTIJM2T.js';
1
2
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
2
3
  import { cx } from './chunk-ODEQTPFM.js';
3
- import './SidebarNav-X5NLZNWA.css';
4
+ import './SidebarNav-KF66MLOM.css';
4
5
  import { useId, useState } from 'react';
5
6
  import { ChevronDownGlyph } from '@crewlethq/icons/glyphs';
6
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -66,7 +67,7 @@ function NavItem({
66
67
  }
67
68
  const inside = /* @__PURE__ */ jsxs(Fragment, { children: [
68
69
  icon ? /* @__PURE__ */ jsx("span", { className: "crewlet-nav-item__icon", "aria-hidden": true, children: icon }) : null,
69
- lead ? /* @__PURE__ */ jsx("span", { className: "crewlet-nav-item__lead", children: lead }) : null,
70
+ lead ? /* @__PURE__ */ jsx(KeyChip, { className: "crewlet-nav-item__lead", children: lead }) : null,
70
71
  lead ? " " : null,
71
72
  /* @__PURE__ */ jsxs("span", { className: "crewlet-nav-item__label", children: [
72
73
  label,
@@ -1,15 +1,15 @@
1
- import { Toolbar } from './chunk-PGRPNDWM.js';
2
1
  import { PageHeader } from './chunk-HOXGBLEY.js';
3
2
  import { RelativeTime } from './chunk-3CZ7XAVY.js';
4
- import { DateTimePicker } from './chunk-JCNUQ3US.js';
5
- import { useListbox, useOptionKeys } from './chunk-6MU7HNB4.js';
3
+ import { Toolbar } from './chunk-PGRPNDWM.js';
4
+ import { DataTable } from './chunk-WY2NLRZN.js';
5
+ import { Menu } from './chunk-TFH6SYWB.js';
6
+ import { DateTimePicker } from './chunk-JDPAA2JU.js';
7
+ import { useListbox, useOptionKeys } from './chunk-F73MC7EZ.js';
6
8
  import { Kbd } from './chunk-SMBKXWML.js';
7
9
  import { Input } from './chunk-FTEJPHBI.js';
8
- import { DataTable } from './chunk-GOUKOXJK.js';
9
- import { Menu } from './chunk-TFH6SYWB.js';
10
10
  import { Button } from './chunk-OG6CL3EB.js';
11
11
  import { IconButton } from './chunk-4FQT6ZAY.js';
12
- import { Popover } from './chunk-PDUZO35I.js';
12
+ import { Popover } from './chunk-O2ZDZZS7.js';
13
13
  import { Count } from './chunk-BHHIIK3M.js';
14
14
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
15
15
  import { cx } from './chunk-ODEQTPFM.js';
@@ -0,0 +1,110 @@
1
+ import { KeyChip } from './chunk-FYTIJM2T.js';
2
+ import { cx } from './chunk-ODEQTPFM.js';
3
+ import './Breadcrumb-YMWVM3SV.css';
4
+ import { useRef, useState, useLayoutEffect } from 'react';
5
+ import { ChevronRightGlyph } from '@crewlethq/icons/glyphs';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
+
8
+ function Breadcrumb({ crumbs, current, label = "Breadcrumb", className, style, ...rest }) {
9
+ const trail = useRef(null);
10
+ const signature = crumbs.map((crumb) => [crumb.label, crumb.keyChip ?? "", crumb.mono ? "m" : "", crumb.href ? "l" : ""].join("\0")).join("");
11
+ const overflows = useOverflows(trail, signature);
12
+ const floor = useFloor(trail, signature);
13
+ return /* @__PURE__ */ jsx(
14
+ "nav",
15
+ {
16
+ ...rest,
17
+ ref: trail,
18
+ className: cx("crewlet-breadcrumb", className),
19
+ "aria-label": label,
20
+ tabIndex: overflows ? 0 : void 0,
21
+ style: floor > 0 ? { ...style, "--crewlet-breadcrumb-floor": `${floor}px` } : style,
22
+ children: crumbs.map((crumb, i) => {
23
+ const last = i === crumbs.length - 1;
24
+ const words = /* @__PURE__ */ jsxs(Fragment, { children: [
25
+ crumb.icon !== void 0 ? /* @__PURE__ */ jsx("span", { className: "crewlet-breadcrumb__icon", "aria-hidden": "true", children: crumb.icon }) : null,
26
+ crumb.keyChip ? /* @__PURE__ */ jsxs(Fragment, { children: [
27
+ /* @__PURE__ */ jsx(KeyChip, { className: "crewlet-breadcrumb__key", children: crumb.keyChip }),
28
+ " "
29
+ ] }) : null,
30
+ crumb.badge !== void 0 ? /* @__PURE__ */ jsx("span", { className: "crewlet-breadcrumb__badge", "aria-hidden": "true", children: crumb.badge }) : null,
31
+ /* @__PURE__ */ jsx("span", { className: "crewlet-breadcrumb__text", children: crumb.label })
32
+ ] });
33
+ const face = crumb.mono ? "is-mono" : void 0;
34
+ return (
35
+ // KEYED BY DEPTH, not by words: the crumb at a depth is the same place
36
+ // in the trail whatever it is called, so a heading whose subject is
37
+ // switched keeps its element, and the control in it keeps focus.
38
+ /* @__PURE__ */ jsxs("span", { className: "crewlet-breadcrumb__part", children: [
39
+ i > 0 ? /* @__PURE__ */ jsx(ChevronRightGlyph, { size: "xs", className: "crewlet-breadcrumb__separator", "aria-hidden": "true" }) : null,
40
+ last ? /* @__PURE__ */ jsx(
41
+ "h1",
42
+ {
43
+ className: cx("crewlet-breadcrumb__here", current !== void 0 && "crewlet-breadcrumb__here--control", face),
44
+ "aria-current": "page",
45
+ children: current !== void 0 ? current : words
46
+ }
47
+ ) : crumb.href ? /* @__PURE__ */ jsx("a", { className: cx("crewlet-breadcrumb__crumb", face), href: crumb.href, children: words }) : (
48
+ // AN ANCESTOR WITH NO PAGE OF ITS OWN, or one the reader is
49
+ // already on ("Work" on Work's landing): its words in the
50
+ // ancestors' quiet ink, with nothing to press.
51
+ /* @__PURE__ */ jsx("span", { className: cx("crewlet-breadcrumb__crumb", face), children: words })
52
+ )
53
+ ] }, i)
54
+ );
55
+ })
56
+ }
57
+ );
58
+ }
59
+ function useFloor(el, content) {
60
+ const [floor, setFloor] = useState(0);
61
+ useLayoutEffect(() => {
62
+ const node = el.current;
63
+ if (!node) return;
64
+ const bar = node.parentElement;
65
+ const measure = () => {
66
+ const last = node.querySelector(".crewlet-breadcrumb__part:last-child > .crewlet-breadcrumb__here");
67
+ if (!last) return setFloor(0);
68
+ const start = node.getBoundingClientRect().left - node.scrollLeft;
69
+ const offset = last.getBoundingClientRect().left - start;
70
+ const stub = parseFloat(getComputedStyle(last).minWidth) || 0;
71
+ const wanted = Math.max(0, Math.ceil(offset + stub));
72
+ setFloor(bar ? Math.min(wanted, roomBeside(bar, node)) : wanted);
73
+ };
74
+ measure();
75
+ if (typeof ResizeObserver !== "function") return;
76
+ const watch = new ResizeObserver(measure);
77
+ for (const part of node.querySelectorAll(".crewlet-breadcrumb__part")) watch.observe(part);
78
+ if (bar) watch.observe(bar);
79
+ return () => watch.disconnect();
80
+ }, [el, content]);
81
+ return floor;
82
+ }
83
+ function roomBeside(bar, item) {
84
+ const style = getComputedStyle(bar);
85
+ const content = bar.clientWidth - (parseFloat(style.paddingLeft) || 0) - (parseFloat(style.paddingRight) || 0);
86
+ if (content <= 0) return Infinity;
87
+ const gap = parseFloat(style.columnGap) || 0;
88
+ let before = 0;
89
+ for (let sib = item.previousElementSibling; sib; sib = sib.previousElementSibling) {
90
+ const width = sib.getBoundingClientRect().width;
91
+ if (width > 0) before += width + gap;
92
+ }
93
+ return Math.max(0, Math.floor(content - before));
94
+ }
95
+ function useOverflows(el, content) {
96
+ const [overflows, setOverflows] = useState(false);
97
+ useLayoutEffect(() => {
98
+ const node = el.current;
99
+ if (!node) return;
100
+ const measure = () => setOverflows(node.scrollWidth > node.clientWidth);
101
+ measure();
102
+ if (typeof ResizeObserver !== "function") return;
103
+ const watch = new ResizeObserver(measure);
104
+ watch.observe(node);
105
+ return () => watch.disconnect();
106
+ }, [el, content]);
107
+ return overflows;
108
+ }
109
+
110
+ export { Breadcrumb };
@@ -0,0 +1,2 @@
1
+ import './Tabs-ITJYJFMU.css';
2
+ import './SegmentedControl-PKBLJVSA.css';
@@ -0,0 +1,24 @@
1
+ import { cx } from './chunk-ODEQTPFM.js';
2
+ import './Document-PQGXJHB7.css';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ function Document({ as: Tag = "div", size = "md", measure = "normal", className, children, ...rest }) {
6
+ return /* @__PURE__ */ jsx(
7
+ Tag,
8
+ {
9
+ ...rest,
10
+ className: cx(
11
+ "crewlet-document",
12
+ `crewlet-document--${size}`,
13
+ measure === "normal" && "crewlet-document--measure",
14
+ className
15
+ ),
16
+ children
17
+ }
18
+ );
19
+ }
20
+ function DocumentTitle({ as: Tag = "h1", className, children, ...rest }) {
21
+ return /* @__PURE__ */ jsx(Tag, { ...rest, className: cx("crewlet-document-title", className), children });
22
+ }
23
+
24
+ export { Document, DocumentTitle };
@@ -1,10 +1,10 @@
1
- import { useCardHeaderSlot } from './chunk-AV2DXAEL.js';
2
1
  import { Menu } from './chunk-TFH6SYWB.js';
3
2
  import { EmptyValue } from './chunk-EUCC37WY.js';
4
- import { Modal } from './chunk-TEEJSTFG.js';
3
+ import { Checkbox } from './chunk-YCTMKKRA.js';
5
4
  import { useClipboard } from './chunk-5VNUYO76.js';
5
+ import { Modal } from './chunk-CF6DTETT.js';
6
6
  import { Button } from './chunk-OG6CL3EB.js';
7
- import { Checkbox } from './chunk-YCTMKKRA.js';
7
+ import { useCardHeaderSlot } from './chunk-AV2DXAEL.js';
8
8
  import { IconButton } from './chunk-4FQT6ZAY.js';
9
9
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
10
10
  import { cx } from './chunk-ODEQTPFM.js';