@crewlethq/ui 0.45.0 → 0.46.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 (123) hide show
  1. package/README.md +22 -0
  2. package/dist/AgentHandoffPanel/index.js +6 -3
  3. package/dist/AppShell/index.js +3 -3
  4. package/dist/ArrangeList/index.js +5 -3
  5. package/dist/ArtifactBar/index.js +5 -5
  6. package/dist/AvatarPicker/index.js +6 -6
  7. package/dist/Canvas/index.js +1 -1
  8. package/dist/CanvasBoard/index.js +1 -1
  9. package/dist/CanvasToolbar/index.js +1 -1
  10. package/dist/CatalogTile/index.js +1 -1
  11. package/dist/{CatalogTile-VPSLCKJE.css → CatalogTile-26XN7L2A.css} +13 -7
  12. package/dist/CharacterCount/CharacterCount.d.ts +63 -0
  13. package/dist/CharacterCount/index.d.ts +3 -0
  14. package/dist/CharacterCount/index.js +4 -0
  15. package/dist/CharacterCount-S735DICR.css +29 -0
  16. package/dist/Checkbox/index.js +5 -2
  17. package/dist/Combobox/index.js +5 -3
  18. package/dist/CommandPalette/index.js +6 -4
  19. package/dist/CommentThread/index.js +6 -3
  20. package/dist/Composer/index.js +5 -2
  21. package/dist/ConfirmField/index.js +5 -2
  22. package/dist/DataTable/index.js +8 -6
  23. package/dist/DataView/index.js +9 -7
  24. package/dist/DateTimePicker/index.js +1 -1
  25. package/dist/DocTabsEditor/index.js +5 -4
  26. package/dist/EditingStatus/index.js +2 -1
  27. package/dist/{Editor-AMO2B54T.js → Editor-6FXHMW64.js} +6 -4
  28. package/dist/FollowBanner/index.js +2 -2
  29. package/dist/HuePicker/index.js +5 -5
  30. package/dist/InlineEditor/InlineEditor.d.ts +86 -0
  31. package/dist/InlineEditor/index.d.ts +3 -0
  32. package/dist/InlineEditor/index.js +9 -0
  33. package/dist/InlineEditor-W55DDKML.css +72 -0
  34. package/dist/Input/FormField.d.ts +18 -1
  35. package/dist/Input/Textarea.d.ts +16 -0
  36. package/dist/Input/index.d.ts +2 -2
  37. package/dist/Input/index.js +4 -1
  38. package/dist/{Input-OQZBCRX5.css → Input-VDSV7VWJ.css} +19 -0
  39. package/dist/LinkInterstitial/index.js +3 -3
  40. package/dist/List/List.d.ts +16 -1
  41. package/dist/List/index.js +1 -1
  42. package/dist/{List-YHLIZFK4.css → List-XY445TKY.css} +31 -0
  43. package/dist/ListInput/ListInput.d.ts +87 -2
  44. package/dist/ListInput/index.d.ts +2 -2
  45. package/dist/ListInput/index.js +5 -2
  46. package/dist/{ListInput-O7LJKOIG.css → ListInput-DP2CSJ22.css} +118 -2
  47. package/dist/MarkdownEditor/index.js +5 -5
  48. package/dist/Menu/index.js +2 -2
  49. package/dist/MergeConflicts/index.js +5 -5
  50. package/dist/Modal/index.js +2 -2
  51. package/dist/PresenterView/index.js +3 -3
  52. package/dist/PrincipalPicker/index.js +6 -4
  53. package/dist/RelatedRow/index.js +2 -2
  54. package/dist/RichEditor/index.js +1 -1
  55. package/dist/SegmentedControl/index.js +4 -4
  56. package/dist/Select/index.js +1 -1
  57. package/dist/ShareDialog/index.js +3 -3
  58. package/dist/SheetColumnEditor/index.js +10 -8
  59. package/dist/SheetGrid/index.js +4 -4
  60. package/dist/SlideCompare/index.js +1 -1
  61. package/dist/SlideFilmstrip/index.js +3 -3
  62. package/dist/Switcher/index.js +5 -4
  63. package/dist/Tabs/index.js +3 -3
  64. package/dist/Text/Text.d.ts +8 -0
  65. package/dist/Text/index.js +1 -1
  66. package/dist/{Text-S3Q4O3IG.css → Text-XOCOU34C.css} +9 -0
  67. package/dist/ThemeSwitcher/index.js +5 -5
  68. package/dist/TileGrid/index.js +1 -1
  69. package/dist/{TileGrid-I6LNYD2H.css → TileGrid-UYMVVVJK.css} +30 -0
  70. package/dist/TimeWindowPicker/index.js +5 -3
  71. package/dist/TreeCanvas/index.js +1 -1
  72. package/dist/VersionCompare/index.js +5 -5
  73. package/dist/{chunk-5AHDGYKN.js → chunk-2KQR6JKE.js} +2 -2
  74. package/dist/{chunk-AGNZIT3Z.js → chunk-2LPNWAN6.js} +1 -1
  75. package/dist/{chunk-MVGCZ655.js → chunk-2UVMIM2J.js} +1 -1
  76. package/dist/{chunk-NF6SWHQI.js → chunk-32MTQ6DN.js} +2 -2
  77. package/dist/{chunk-QOP7QYUW.js → chunk-34RMTXDZ.js} +1 -1
  78. package/dist/{chunk-Q7HLZH32.js → chunk-3QSKDZR5.js} +1 -1
  79. package/dist/{chunk-BVFMLSE3.js → chunk-5EBDOE3C.js} +1 -1
  80. package/dist/{chunk-BF5WV7NM.js → chunk-6FVGSWLS.js} +1 -1
  81. package/dist/chunk-7P336RA5.js +147 -0
  82. package/dist/{chunk-KKUJSMLF.js → chunk-DY4DQ7EB.js} +1 -1
  83. package/dist/{chunk-4DIKLY53.js → chunk-HBSR5DL6.js} +1 -1
  84. package/dist/{chunk-KA75QPFM.js → chunk-ILFEWRLG.js} +1 -1
  85. package/dist/{chunk-Y4GXCKOC.js → chunk-KOJKX3U6.js} +3 -3
  86. package/dist/{chunk-FG243QHY.js → chunk-KZDMAQNJ.js} +1 -1
  87. package/dist/{chunk-TC7DJTXI.js → chunk-MULOZYHL.js} +2 -0
  88. package/dist/{chunk-ZUTUI27P.js → chunk-N4GBDK4L.js} +1 -1
  89. package/dist/{chunk-7CXGSS7D.js → chunk-O6H5XXVK.js} +1 -1
  90. package/dist/{chunk-GI3S6RMM.js → chunk-PWZT6CZY.js} +3 -3
  91. package/dist/{chunk-NNTO3TNC.js → chunk-Q6CQMS6R.js} +1 -1
  92. package/dist/{chunk-IKBK4A5O.js → chunk-QP2P6GDQ.js} +1 -1
  93. package/dist/{chunk-W53CWWX3.js → chunk-RCKRJXFV.js} +2 -2
  94. package/dist/{chunk-U44WGRFW.js → chunk-RFPCHMSX.js} +1 -1
  95. package/dist/{chunk-X6IJ7DAX.js → chunk-RL75QTNT.js} +2 -2
  96. package/dist/{chunk-N3SHQ3VU.js → chunk-RRPQMMCZ.js} +1 -1
  97. package/dist/{chunk-EY7LACIR.js → chunk-RWET7W22.js} +1 -1
  98. package/dist/{chunk-JF3TJUBU.js → chunk-RWWLVDRE.js} +1 -1
  99. package/dist/{chunk-LSRQJDGR.js → chunk-SEPSXOGJ.js} +1 -1
  100. package/dist/{chunk-MCOC3KQA.js → chunk-SQ4JFHO7.js} +1 -1
  101. package/dist/chunk-SQZEVWVC.js +436 -0
  102. package/dist/{chunk-DL5ERIFF.js → chunk-SS4XCUUJ.js} +2 -2
  103. package/dist/{chunk-GPIRKHGS.js → chunk-T7DXSFUX.js} +1 -1
  104. package/dist/{chunk-NZWX27J7.js → chunk-TDQ262TB.js} +19 -3
  105. package/dist/{chunk-5GNA4RKP.js → chunk-TGSIVB6H.js} +1 -1
  106. package/dist/{chunk-UO5VJHWP.js → chunk-U4HOKU6P.js} +2 -2
  107. package/dist/chunk-XCUUI7E7.js +62 -0
  108. package/dist/{chunk-ENHLVF6Q.js → chunk-XS2FJVMT.js} +1 -1
  109. package/dist/{chunk-FV6H7H5I.js → chunk-YBJVIY5E.js} +4 -4
  110. package/dist/{chunk-6MAQ2SVP.js → chunk-YR3ZURBM.js} +3 -3
  111. package/dist/{chunk-ZI5G5RS7.js → chunk-Z734SV4E.js} +55 -8
  112. package/dist/{chunk-OWIGEE2Y.js → chunk-ZZCCNVF3.js} +3 -3
  113. package/dist/generated/index.d.ts +2 -0
  114. package/dist/generated/index.js +78 -76
  115. package/dist/index.js +78 -76
  116. package/dist/styles.css +333 -15
  117. package/package.json +3 -3
  118. package/dist/chunk-NMYGJJJK.js +0 -263
  119. package/dist/{chunk-6IPEPSB5.js → chunk-245IXG5P.js} +0 -0
  120. package/dist/{chunk-OED6LUWE.js → chunk-4A4QG6UC.js} +1 -1
  121. package/dist/{chunk-FYINAYAJ.js → chunk-6AKG2VLR.js} +1 -1
  122. package/dist/{chunk-E6HXFFWO.js → chunk-ISDEXIXH.js} +1 -1
  123. package/dist/{chunk-PCI7ZJ6Z.js → chunk-TMM32U7D.js} +1 -1
@@ -0,0 +1,436 @@
1
+ import { useReorder } from './chunk-XJ2QEBMM.js';
2
+ import { FormField, Textarea } from './chunk-Z734SV4E.js';
3
+ import { characterCountState, CharacterCount, countCharacters } from './chunk-XCUUI7E7.js';
4
+ import { Kbd } from './chunk-SMBKXWML.js';
5
+ import { announce } from './chunk-RSAQOKH3.js';
6
+ import { Button } from './chunk-OG6CL3EB.js';
7
+ import { isComposing } from './chunk-H5XTJ7GJ.js';
8
+ import { IconButton } from './chunk-LACA4JGC.js';
9
+ import { VisuallyHidden } from './chunk-BZFWDIV6.js';
10
+ import { cx } from './chunk-ODEQTPFM.js';
11
+ import './ListInput-DP2CSJ22.css';
12
+ import { forwardRef, useState, useRef, useImperativeHandle, useLayoutEffect, useId } from 'react';
13
+ import { PlusGlyph, GripVerticalGlyph, ArrowUpGlyph, ArrowDownGlyph, XGlyph } from '@crewlethq/icons/glyphs';
14
+ import { jsx, jsxs } from 'react/jsx-runtime';
15
+
16
+ function withDraft(value, draft) {
17
+ const text = (draft ?? "").trim();
18
+ return text === "" ? [...value] : [...value, text];
19
+ }
20
+ var minted = 0;
21
+ var mint = () => `item-${minted += 1}`;
22
+ var capitalized = (text) => text.charAt(0).toLocaleUpperCase() + text.slice(1);
23
+ var ListInput = forwardRef(function ListInput2({
24
+ label,
25
+ itemName,
26
+ value,
27
+ onChange,
28
+ draft: ownedDraft,
29
+ onDraftChange,
30
+ hideLabel = false,
31
+ multiline = false,
32
+ helper,
33
+ helperOnFocus,
34
+ error,
35
+ placeholder,
36
+ disabled = false,
37
+ optional,
38
+ enterHint = true,
39
+ focusOnMount = false,
40
+ reorderByDrag = false,
41
+ maxItems,
42
+ maxLength,
43
+ undoRemove = false,
44
+ itemError,
45
+ fullMessage = (max) => `The list is full at ${max}. Remove one to add another.`,
46
+ addLabel = (name) => `Add ${name}`,
47
+ itemLabel = (name, position) => `${name} ${position}`,
48
+ newItemLabel = (name) => `New ${name}`,
49
+ moveUpLabel = (name, position) => `Move ${name} ${position} up`,
50
+ moveDownLabel = (name, position) => `Move ${name} ${position} down`,
51
+ removeLabel = (name, position) => `Remove ${name} ${position}`,
52
+ addedMessage = (name, position) => `Added ${name} ${position}`,
53
+ removedMessage = (name, position) => `Removed ${name} ${position}`,
54
+ movedMessage = (name, position, count) => `Moved ${name} to position ${position} of ${count}`,
55
+ removedNotice = (name, position) => `${name} ${position} removed.`,
56
+ undoLabel = "Undo",
57
+ dismissLabel = "Dismiss",
58
+ restoredMessage = (name, position) => `Restored ${name} ${position}`
59
+ }, ref) {
60
+ const [heldDraft, setHeldDraft] = useState("");
61
+ const draft = ownedDraft ?? heldDraft;
62
+ const setDraft = (next) => {
63
+ if (ownedDraft === void 0) setHeldDraft(next);
64
+ onDraftChange?.(next);
65
+ };
66
+ const [removed, setRemoved] = useState(null);
67
+ const ids = useRef([]);
68
+ const emitted = useRef(null);
69
+ if (value !== emitted.current && value.length !== ids.current.length) {
70
+ ids.current = value.map(mint);
71
+ if (removed !== null) setRemoved(null);
72
+ }
73
+ const inputs = useRef(/* @__PURE__ */ new Map());
74
+ const ups = useRef(/* @__PURE__ */ new Map());
75
+ const downs = useRef(/* @__PURE__ */ new Map());
76
+ const addBox = useRef(null);
77
+ const undoButton = useRef(null);
78
+ const focusNext = useRef(null);
79
+ const focusField = (el) => {
80
+ if (!el) return;
81
+ el.focus();
82
+ el.setSelectionRange(el.value.length, el.value.length);
83
+ };
84
+ useImperativeHandle(ref, () => ({
85
+ focusItem: (index) => {
86
+ const id = ids.current[index];
87
+ if (id !== void 0) focusField(inputs.current.get(id));
88
+ },
89
+ focusNew: () => addBox.current?.focus()
90
+ }));
91
+ const focusAtMount = useRef(focusOnMount);
92
+ useLayoutEffect(() => {
93
+ if (focusAtMount.current) addBox.current?.focus();
94
+ }, []);
95
+ useLayoutEffect(() => {
96
+ const want = focusNext.current;
97
+ if (!want) return;
98
+ focusNext.current = null;
99
+ if (want.target === "add") {
100
+ addBox.current?.focus();
101
+ return;
102
+ }
103
+ if (want.target === "undo") {
104
+ undoButton.current?.focus();
105
+ return;
106
+ }
107
+ const up = ups.current.get(want.id);
108
+ const down = downs.current.get(want.id);
109
+ const box = inputs.current.get(want.id);
110
+ const order2 = want.target === "up" ? [up, down, box] : want.target === "down" ? [down, up, box] : [box];
111
+ order2.find((el) => el && !el.disabled)?.focus();
112
+ });
113
+ function emit(next, nextIds, said, leaving = null) {
114
+ ids.current = nextIds;
115
+ emitted.current = next;
116
+ setRemoved(leaving);
117
+ onChange(next);
118
+ if (said) announce(said);
119
+ }
120
+ const name = capitalized(itemName);
121
+ const count = value.length;
122
+ const listId = useId();
123
+ const full = maxItems !== void 0 && count >= maxItems;
124
+ const measured = (text) => countCharacters(text.trim());
125
+ const overLimit = (text) => maxLength !== void 0 && characterCountState(measured(text), maxLength) === "over";
126
+ const draftRefused = draft.trim() === "" || overLimit(draft);
127
+ const draftCount = maxLength !== void 0 && characterCountState(measured(draft), maxLength) !== "within";
128
+ const reorder = useReorder({
129
+ keys: ids.current,
130
+ nameOf: () => itemName,
131
+ onReorder: (nextIds) => {
132
+ const byId = new Map(ids.current.map((id, at) => [id, value[at]]));
133
+ emit(
134
+ nextIds.map((id) => byId.get(id)),
135
+ nextIds
136
+ );
137
+ },
138
+ labels: { moved: (_, position, total) => movedMessage(itemName, position, total) }
139
+ });
140
+ const drags = reorderByDrag && !disabled;
141
+ const order = drags ? reorder.order : ids.current;
142
+ function add() {
143
+ const text = draft.trim();
144
+ if (!text || disabled || full || overLimit(text)) return;
145
+ const id = mint();
146
+ const filled = maxItems !== void 0 && count + 1 >= maxItems;
147
+ emit(
148
+ [...value, text],
149
+ [...ids.current, id],
150
+ addedMessage(itemName, `${count + 1} of ${count + 1}`) + (filled ? `. ${fullMessage(maxItems)}` : "")
151
+ );
152
+ setDraft("");
153
+ focusNext.current = filled ? { id, target: "input" } : { id: "", target: "add" };
154
+ }
155
+ function move(index, by, target) {
156
+ const to = index + by;
157
+ if (disabled || to < 0 || to >= count) return;
158
+ const next = [...value];
159
+ const nextIds = [...ids.current];
160
+ [next[index], next[to]] = [next[to], next[index]];
161
+ [nextIds[index], nextIds[to]] = [nextIds[to], nextIds[index]];
162
+ focusNext.current = { id: nextIds[to], target };
163
+ emit(next, nextIds, movedMessage(itemName, to + 1, count));
164
+ }
165
+ const neighbourOf = (nextIds, index) => {
166
+ const neighbour = nextIds[index] ?? nextIds[index - 1];
167
+ return neighbour ? { id: neighbour, target: "input" } : { id: "", target: "add" };
168
+ };
169
+ function remove(index) {
170
+ if (disabled) return;
171
+ const next = value.filter((_, at) => at !== index);
172
+ const nextIds = ids.current.filter((_, at) => at !== index);
173
+ const leaving = undoRemove ? { id: ids.current[index], text: value[index], index } : null;
174
+ focusNext.current = leaving ? { id: "", target: "undo" } : neighbourOf(nextIds, index);
175
+ emit(next, nextIds, leaving ? void 0 : removedMessage(itemName, `${index + 1} of ${count}`), leaving);
176
+ }
177
+ function restore() {
178
+ if (!removed || disabled) return;
179
+ const at = Math.min(removed.index, count);
180
+ const next = [...value];
181
+ const nextIds = [...ids.current];
182
+ next.splice(at, 0, removed.text);
183
+ nextIds.splice(at, 0, removed.id);
184
+ focusNext.current = { id: removed.id, target: "input" };
185
+ emit(next, nextIds, restoredMessage(itemName, `${at + 1} of ${count + 1}`));
186
+ }
187
+ function dismiss() {
188
+ if (!removed) return;
189
+ focusNext.current = neighbourOf(ids.current, removed.index);
190
+ setRemoved(null);
191
+ }
192
+ const acts = (event) => event.key === "Enter" && !event.metaKey && !event.ctrlKey && !(multiline && event.shiftKey) && !isComposing(event);
193
+ function itemKeys(event, index) {
194
+ if (isComposing(event)) return;
195
+ if (event.altKey && (event.key === "ArrowUp" || event.key === "ArrowDown")) {
196
+ event.preventDefault();
197
+ move(index, event.key === "ArrowUp" ? -1 : 1, "input");
198
+ return;
199
+ }
200
+ if (!acts(event)) return;
201
+ event.preventDefault();
202
+ const next = ids.current[index + 1];
203
+ if (next) inputs.current.get(next)?.focus();
204
+ else addBox.current?.focus();
205
+ }
206
+ const control = (props) => (
207
+ /*
208
+ * ONE LINE THAT GROWS, never a fixed block of rows. A list of sentences
209
+ * read as a list wants each item on its own line; a two-row textarea per
210
+ * item made three of them 256px where the console draws 111, and the
211
+ * second row was empty in every one of them. `autoResize` starts at one
212
+ * line and takes as many as the sentence needs.
213
+ *
214
+ * AND A ONE-LINE ITEM WRAPS TOO. A one-line input scrolled a long
215
+ * policy sideways, so while it was edited only its tail could be read.
216
+ * Without `multiline` the field is a `singleLine` textarea: it wraps to
217
+ * be read whole and still holds one line, with no newline typed, pasted
218
+ * or dropped in.
219
+ */
220
+ /* @__PURE__ */ jsx(
221
+ Textarea,
222
+ {
223
+ rows: 1,
224
+ autoResize: true,
225
+ singleLine: !multiline,
226
+ "aria-label": props.label,
227
+ value: props.value,
228
+ placeholder: props.placeholder,
229
+ disabled,
230
+ spellCheck: true,
231
+ trailing: props.trailing,
232
+ error: props.invalid ?? false,
233
+ "aria-describedby": props.describedBy,
234
+ onChange: (event) => props.onChange(event.target.value),
235
+ onKeyDown: props.onKeyDown,
236
+ ref: props.ref
237
+ }
238
+ )
239
+ );
240
+ const addControl = /* @__PURE__ */ jsx(
241
+ IconButton,
242
+ {
243
+ size: "sm",
244
+ variant: "soft-brand",
245
+ label: addLabel(itemName),
246
+ icon: /* @__PURE__ */ jsx(PlusGlyph, {}),
247
+ disabled: disabled || full || draftRefused,
248
+ onClick: add
249
+ }
250
+ );
251
+ return /* @__PURE__ */ jsxs(
252
+ FormField,
253
+ {
254
+ as: "fieldset",
255
+ label: hideLabel ? /* @__PURE__ */ jsx(VisuallyHidden, { children: label }) : label,
256
+ optional,
257
+ helper,
258
+ helperOnFocus,
259
+ error,
260
+ className: "crewlet-list-input",
261
+ labelAction: hideLabel ? void 0 : addControl,
262
+ children: [
263
+ full ? (
264
+ /*
265
+ * A FULL LIST SAYS SO WHERE THE BOX WAS, rather than drawing a box
266
+ * that takes nothing. A disabled box is a control a reader reaches
267
+ * and cannot use with no word of why; the sentence is the why, and
268
+ * every item above it still edits, moves and goes.
269
+ */
270
+ /* @__PURE__ */ jsx("p", { className: "crewlet-list-input__full", children: fullMessage(maxItems) })
271
+ ) : /* @__PURE__ */ jsxs("div", { className: "crewlet-list-input__add", children: [
272
+ control({
273
+ label: newItemLabel(itemName),
274
+ value: draft,
275
+ placeholder,
276
+ invalid: overLimit(draft.trim()),
277
+ describedBy: draftCount ? `${listId}-draft-count` : void 0,
278
+ /*
279
+ * THE COUNT TAKES THE KEYCAP'S PLACE once the limit is near: both
280
+ * say what pressing Enter will do, and past the limit the answer
281
+ * is nothing, which the count says and the keycap would not.
282
+ */
283
+ trailing: draftCount ? /* @__PURE__ */ jsx(CharacterCount, { id: `${listId}-draft-count`, length: measured(draft), max: maxLength }) : enterHint ? /* @__PURE__ */ jsx(Kbd, { subtle: true, children: "Enter" }) : void 0,
284
+ onChange: setDraft,
285
+ onKeyDown: (event) => {
286
+ if (!acts(event)) return;
287
+ event.preventDefault();
288
+ add();
289
+ },
290
+ ref: (el) => {
291
+ addBox.current = el;
292
+ }
293
+ }),
294
+ hideLabel ? /* @__PURE__ */ jsx("span", { className: "crewlet-list-input__add-control", children: addControl }) : null
295
+ ] }),
296
+ count > 0 || removed !== null ? /* @__PURE__ */ jsx("ol", { className: "crewlet-list-input__items", ...drags ? reorder.containerProps : {}, children: withRemoved(
297
+ order.map((key) => {
298
+ const index = ids.current.indexOf(key);
299
+ const item = value[index] ?? "";
300
+ const position = `${index + 1} of ${count}`;
301
+ const length = maxLength === void 0 ? 0 : measured(item);
302
+ const counted = maxLength !== void 0 && characterCountState(length, maxLength) !== "within";
303
+ const refusal = itemError?.(index, item);
304
+ const refused = refusal !== void 0 && refusal !== null && refusal !== false && refusal !== "";
305
+ const countId = `${listId}-${key}-count`;
306
+ const errorId = `${listId}-${key}-error`;
307
+ const described = [counted ? countId : null, refused ? errorId : null].filter(Boolean).join(" ");
308
+ const { draggable, onDragStart, onDragEnd } = reorder.handleProps(key);
309
+ return /* @__PURE__ */ jsxs(
310
+ "li",
311
+ {
312
+ className: cx("crewlet-list-input__item", drags && "crewlet-list-input__item--draggable"),
313
+ ...drags ? reorder.itemProps(key) : {},
314
+ children: [
315
+ drags ? /* @__PURE__ */ jsx(
316
+ "span",
317
+ {
318
+ className: "crewlet-list-input__handle",
319
+ "aria-hidden": "true",
320
+ draggable,
321
+ onDragStart,
322
+ onDragEnd,
323
+ children: /* @__PURE__ */ jsx(GripVerticalGlyph, { size: "sm" })
324
+ }
325
+ ) : null,
326
+ /* @__PURE__ */ jsx("span", { className: "crewlet-list-input__index", "aria-hidden": "true", children: index + 1 }),
327
+ /* @__PURE__ */ jsxs("span", { className: "crewlet-list-input__field", children: [
328
+ control({
329
+ label: itemLabel(name, position),
330
+ value: item,
331
+ invalid: refused || overLimit(item),
332
+ describedBy: described || void 0,
333
+ onChange: (next) => {
334
+ const list = [...value];
335
+ list[index] = next;
336
+ emit(list, ids.current);
337
+ },
338
+ onKeyDown: (event) => itemKeys(event, index),
339
+ ref: (el) => {
340
+ if (el) inputs.current.set(key, el);
341
+ else inputs.current.delete(key);
342
+ }
343
+ }),
344
+ counted || refused ? /* @__PURE__ */ jsxs("span", { className: "crewlet-list-input__note", children: [
345
+ refused ? /* @__PURE__ */ jsx("span", { id: errorId, className: "crewlet-list-input__error", children: refusal }) : /* @__PURE__ */ jsx("span", {}),
346
+ counted ? /* @__PURE__ */ jsx(CharacterCount, { id: countId, length, max: maxLength }) : null
347
+ ] }) : null
348
+ ] }),
349
+ /* @__PURE__ */ jsxs("span", { className: "crewlet-list-input__actions", children: [
350
+ /* @__PURE__ */ jsxs("span", { className: "crewlet-list-input__move", children: [
351
+ /* @__PURE__ */ jsx(
352
+ IconButton,
353
+ {
354
+ size: "sm",
355
+ label: moveUpLabel(itemName, position),
356
+ icon: /* @__PURE__ */ jsx(ArrowUpGlyph, {}),
357
+ disabled: disabled || index === 0,
358
+ onClick: () => move(index, -1, "up"),
359
+ ref: (el) => {
360
+ if (el) ups.current.set(key, el);
361
+ else ups.current.delete(key);
362
+ }
363
+ }
364
+ ),
365
+ /* @__PURE__ */ jsx(
366
+ IconButton,
367
+ {
368
+ size: "sm",
369
+ label: moveDownLabel(itemName, position),
370
+ icon: /* @__PURE__ */ jsx(ArrowDownGlyph, {}),
371
+ disabled: disabled || index === count - 1,
372
+ onClick: () => move(index, 1, "down"),
373
+ ref: (el) => {
374
+ if (el) downs.current.set(key, el);
375
+ else downs.current.delete(key);
376
+ }
377
+ }
378
+ )
379
+ ] }),
380
+ /* @__PURE__ */ jsx(
381
+ IconButton,
382
+ {
383
+ size: "sm",
384
+ variant: "ghost-danger",
385
+ label: removeLabel(itemName, position),
386
+ icon: /* @__PURE__ */ jsx(XGlyph, {}),
387
+ disabled,
388
+ onClick: () => remove(index)
389
+ }
390
+ )
391
+ ] })
392
+ ]
393
+ },
394
+ key
395
+ );
396
+ })
397
+ ) }) : null
398
+ ]
399
+ }
400
+ );
401
+ function withRemoved(rows) {
402
+ if (removed === null) return rows;
403
+ const noticeId = `${listId}-removed`;
404
+ const row = /* @__PURE__ */ jsxs("li", { className: "crewlet-list-input__removed", children: [
405
+ /* @__PURE__ */ jsx("span", { id: noticeId, className: "crewlet-list-input__removed-text", children: removedNotice(name, removed.index + 1) }),
406
+ /* @__PURE__ */ jsxs("span", { className: "crewlet-list-input__actions", children: [
407
+ /* @__PURE__ */ jsx(
408
+ Button,
409
+ {
410
+ ref: undoButton,
411
+ size: "small",
412
+ variant: "ghost",
413
+ "aria-describedby": noticeId,
414
+ disabled,
415
+ onClick: restore,
416
+ children: undoLabel
417
+ }
418
+ ),
419
+ /* @__PURE__ */ jsx(
420
+ IconButton,
421
+ {
422
+ size: "sm",
423
+ label: dismissLabel,
424
+ icon: /* @__PURE__ */ jsx(XGlyph, {}),
425
+ "aria-describedby": noticeId,
426
+ onClick: dismiss
427
+ }
428
+ )
429
+ ] })
430
+ ] }, `removed-${removed.id}`);
431
+ const at = Math.min(removed.index, rows.length);
432
+ return [...rows.slice(0, at), row, ...rows.slice(at)];
433
+ }
434
+ });
435
+
436
+ export { ListInput, withDraft };
@@ -1,6 +1,6 @@
1
1
  import { Toolbar } from './chunk-NR4CPM4T.js';
2
- import { SegmentedControl } from './chunk-BF5WV7NM.js';
3
- import { tabId } from './chunk-KA75QPFM.js';
2
+ import { SegmentedControl } from './chunk-6FVGSWLS.js';
3
+ import { tabId } from './chunk-ILFEWRLG.js';
4
4
  import { IconButton } from './chunk-LACA4JGC.js';
5
5
  import { cx } from './chunk-ODEQTPFM.js';
6
6
  import './MarkdownEditor-WOKV7CLK.css';
@@ -1,6 +1,6 @@
1
1
  import { Switch } from './chunk-A722MUNR.js';
2
2
  import { useReorder } from './chunk-XJ2QEBMM.js';
3
- import { Checkbox } from './chunk-EY7LACIR.js';
3
+ import { Checkbox } from './chunk-RWET7W22.js';
4
4
  import { IconButton } from './chunk-LACA4JGC.js';
5
5
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
6
6
  import { cx } from './chunk-ODEQTPFM.js';
@@ -1,19 +1,35 @@
1
1
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
2
2
  import { cx } from './chunk-ODEQTPFM.js';
3
- import './List-YHLIZFK4.css';
3
+ import './List-XY445TKY.css';
4
4
  import { createContext, useContext, Children, isValidElement, cloneElement } from 'react';
5
5
  import { CircleAlertGlyph } from '@crewlethq/icons/glyphs';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
7
 
8
8
  var ListContext = createContext({ variant: "divided", templated: false });
9
- function List({ variant = "divided", template, className, style, children, ...rest }) {
9
+ function List({
10
+ variant = "divided",
11
+ template,
12
+ density = "comfortable",
13
+ measure,
14
+ className,
15
+ style,
16
+ children,
17
+ ...rest
18
+ }) {
10
19
  const Tag = variant === "numbered" || variant === "numbered-divided" ? "ol" : "ul";
11
20
  const templated = template !== void 0;
12
21
  return /* @__PURE__ */ jsx(ListContext.Provider, { value: { variant, templated }, children: /* @__PURE__ */ jsx(
13
22
  Tag,
14
23
  {
15
24
  ...rest,
16
- className: cx("crewlet-list", `crewlet-list--${variant}`, templated && "crewlet-list--templated", className),
25
+ className: cx(
26
+ "crewlet-list",
27
+ `crewlet-list--${variant}`,
28
+ templated && "crewlet-list--templated",
29
+ density === "compact" && "crewlet-list--compact",
30
+ measure && `crewlet-list--measure-${measure}`,
31
+ className
32
+ ),
17
33
  style: templated ? { ...style, "--crewlet-list-template": template } : style,
18
34
  children
19
35
  }
@@ -1,6 +1,6 @@
1
1
  import { useHeadingLevel, headingTag } from './chunk-PQADV6P6.js';
2
2
  import { cx } from './chunk-ODEQTPFM.js';
3
- import './CatalogTile-VPSLCKJE.css';
3
+ import './CatalogTile-26XN7L2A.css';
4
4
  import { useId } from 'react';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
6
6
 
@@ -1,8 +1,8 @@
1
- import { SegmentedControl } from './chunk-BF5WV7NM.js';
1
+ import { SegmentedControl } from './chunk-6FVGSWLS.js';
2
2
  import { Tooltip } from './chunk-I33UIUZD.js';
3
3
  import { ArtifactIcon } from './chunk-ILDNJHRU.js';
4
4
  import { isApplePlatform, Kbd } from './chunk-SMBKXWML.js';
5
- import { Menu } from './chunk-PCI7ZJ6Z.js';
5
+ import { Menu } from './chunk-TMM32U7D.js';
6
6
  import { Button } from './chunk-OG6CL3EB.js';
7
7
  import { IconButton } from './chunk-LACA4JGC.js';
8
8
  import { returnFocus } from './chunk-NUZHC6MM.js';
@@ -0,0 +1,62 @@
1
+ import { announce } from './chunk-RSAQOKH3.js';
2
+ import { VisuallyHidden } from './chunk-BZFWDIV6.js';
3
+ import { cx } from './chunk-ODEQTPFM.js';
4
+ import './CharacterCount-S735DICR.css';
5
+ import { useRef, useEffect } from 'react';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
7
+
8
+ function countCharacters(text) {
9
+ return Array.from(text).length;
10
+ }
11
+ var CHARACTER_COUNT_NEAR = 0.9;
12
+ function characterCountState(length, max) {
13
+ if (length > max) return "over";
14
+ return length >= Math.ceil(max * CHARACTER_COUNT_NEAR) ? "near" : "within";
15
+ }
16
+ var characters = (n) => `${n} ${n === 1 ? "character" : "characters"}`;
17
+ var CHARACTER_COUNT_LABELS = {
18
+ count: (length, max) => `${length} of ${characters(max)}`,
19
+ left: (left) => `${characters(left)} left`,
20
+ over: (over, max) => `${characters(over)} over the limit of ${max}`,
21
+ within: "Within the limit again"
22
+ };
23
+ function CharacterCount({
24
+ length,
25
+ max,
26
+ announce: says = true,
27
+ labels: overrides,
28
+ className,
29
+ ...rest
30
+ }) {
31
+ const labels = { ...CHARACTER_COUNT_LABELS, ...overrides };
32
+ const state = characterCountState(length, max);
33
+ const was = useRef(state);
34
+ const sentence = useRef(labels);
35
+ sentence.current = labels;
36
+ useEffect(() => {
37
+ const before = was.current;
38
+ was.current = state;
39
+ if (!says || before === state) return;
40
+ const text = sentence.current;
41
+ if (state === "over") announce(text.over(length - max, max));
42
+ else if (state === "near" && before === "within") announce(text.left(max - length));
43
+ else if (before === "over") announce(text.within);
44
+ }, [state, says, length, max]);
45
+ return /* @__PURE__ */ jsxs(
46
+ "span",
47
+ {
48
+ ...rest,
49
+ className: cx("crewlet-character-count", `crewlet-character-count--${state}`, className),
50
+ children: [
51
+ /* @__PURE__ */ jsxs("span", { "aria-hidden": "true", children: [
52
+ length,
53
+ /* @__PURE__ */ jsx("span", { className: "crewlet-character-count__of", children: "/" }),
54
+ max
55
+ ] }),
56
+ /* @__PURE__ */ jsx(VisuallyHidden, { children: state === "over" ? labels.over(length - max, max) : labels.count(length, max) })
57
+ ]
58
+ }
59
+ );
60
+ }
61
+
62
+ export { CHARACTER_COUNT_LABELS, CHARACTER_COUNT_NEAR, CharacterCount, characterCountState, countCharacters };
@@ -1,4 +1,4 @@
1
- import { SegmentedControl } from './chunk-BF5WV7NM.js';
1
+ import { SegmentedControl } from './chunk-6FVGSWLS.js';
2
2
  import { Button } from './chunk-OG6CL3EB.js';
3
3
  import { cx } from './chunk-ODEQTPFM.js';
4
4
  import './MergeConflicts-O44ESH4T.css';
@@ -1,11 +1,11 @@
1
1
  import { Select } from './chunk-XQZ6EWOE.js';
2
2
  import { SHEET_LABELS, SHEET_COLUMN_TYPES, optionHue, retypeColumn } from './chunk-LFSIKGRQ.js';
3
- import { HueSwatch } from './chunk-QOP7QYUW.js';
3
+ import { HueSwatch } from './chunk-34RMTXDZ.js';
4
4
  import { NODE_HUES, NODE_HUE_NAMES } from './chunk-AYKXMIOR.js';
5
- import { Checkbox } from './chunk-EY7LACIR.js';
6
- import { FormField, Input } from './chunk-ZI5G5RS7.js';
5
+ import { Checkbox } from './chunk-RWET7W22.js';
6
+ import { FormField, Input } from './chunk-Z734SV4E.js';
7
7
  import { Kbd } from './chunk-SMBKXWML.js';
8
- import { Menu } from './chunk-PCI7ZJ6Z.js';
8
+ import { Menu } from './chunk-TMM32U7D.js';
9
9
  import { Button } from './chunk-OG6CL3EB.js';
10
10
  import { IconButton } from './chunk-LACA4JGC.js';
11
11
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
@@ -1,10 +1,10 @@
1
- import { Modal } from './chunk-FYINAYAJ.js';
1
+ import { Modal } from './chunk-6AKG2VLR.js';
2
2
  import { useListbox, useHighlightRest } from './chunk-7KHM2FZH.js';
3
- import { Input } from './chunk-ZI5G5RS7.js';
3
+ import { Input } from './chunk-Z734SV4E.js';
4
4
  import { Kbd } from './chunk-SMBKXWML.js';
5
5
  import { Count } from './chunk-BHHIIK3M.js';
6
- import { isComposing } from './chunk-H5XTJ7GJ.js';
7
6
  import { HIT_AREA_CLASS } from './chunk-2YTOOH3A.js';
7
+ import { isComposing } from './chunk-H5XTJ7GJ.js';
8
8
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
9
9
  import { cx } from './chunk-ODEQTPFM.js';
10
10
  import './HitArea-QUOS6ZDJ.css';