@crewlethq/ui 0.34.2 → 0.35.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 (96) hide show
  1. package/README.md +47 -0
  2. package/dist/AppShell/index.js +2 -2
  3. package/dist/ArtifactBar/ArtifactBar.d.ts +6 -0
  4. package/dist/ArtifactBar/index.d.ts +1 -0
  5. package/dist/ArtifactBar/index.js +2 -2
  6. package/dist/{ArtifactBar-C462VD4C.css → ArtifactBar-HV5AT4RG.css} +39 -2
  7. package/dist/ArtifactCard/index.d.ts +1 -0
  8. package/dist/ArtifactCard/index.js +2 -2
  9. package/dist/{ArtifactCard-XHRLAL7C.css → ArtifactCard-OGTMO2KI.css} +15 -16
  10. package/dist/ArtifactKindIllustration/ArtifactKindIllustration.d.ts +14 -1
  11. package/dist/ArtifactKindIllustration/index.d.ts +1 -0
  12. package/dist/ArtifactKindIllustration/index.js +1 -1
  13. package/dist/{ArtifactKindIllustration-4NG6IE3C.css → ArtifactKindIllustration-5FPRJQ3P.css} +32 -26
  14. package/dist/ArtifactKinds-CVD3FCYO.css +15 -0
  15. package/dist/AvatarPicker/index.js +2 -2
  16. package/dist/CanvasBoard/CanvasBoard.d.ts +30 -12
  17. package/dist/CanvasBoard/index.js +1 -1
  18. package/dist/ChangeMark/index.js +2 -2
  19. package/dist/ChoiceList/ChoiceList.d.ts +7 -1
  20. package/dist/ChoiceList/index.js +1 -1
  21. package/dist/{ChoiceList-2PQJS4DX.css → ChoiceList-RBL7FNQL.css} +39 -0
  22. package/dist/DataTable/index.js +2 -2
  23. package/dist/DataView/index.js +3 -3
  24. package/dist/DocTabsEditor/index.js +2 -2
  25. package/dist/{Editor-JMRD6AZW.js → Editor-BVQHBF7L.js} +4 -4
  26. package/dist/FileDrop/FileDrop.d.ts +64 -0
  27. package/dist/FileDrop/index.d.ts +3 -0
  28. package/dist/FileDrop/index.js +4 -0
  29. package/dist/FileDrop-WE7BNZGV.css +80 -0
  30. package/dist/FindingList/FindingList.d.ts +35 -0
  31. package/dist/FindingList/index.d.ts +3 -0
  32. package/dist/FindingList/index.js +3 -0
  33. package/dist/FindingList-PMYA2R6Q.css +71 -0
  34. package/dist/HuePicker/HuePicker.d.ts +9 -0
  35. package/dist/HuePicker/index.d.ts +1 -1
  36. package/dist/HuePicker/index.js +1 -1
  37. package/dist/Markdown/index.js +1 -1
  38. package/dist/Menu/Menu.d.ts +6 -1
  39. package/dist/Menu/index.js +1 -1
  40. package/dist/RichEditor/index.js +3 -3
  41. package/dist/RichEditorMarkdown/index.js +3 -3
  42. package/dist/SheetColumnEditor/SheetColumnEditor.d.ts +94 -0
  43. package/dist/SheetColumnEditor/index.d.ts +3 -0
  44. package/dist/SheetColumnEditor/index.js +23 -0
  45. package/dist/SheetColumnEditor-BO2HKFRB.css +73 -0
  46. package/dist/SheetGrid/SheetGrid.d.ts +19 -1
  47. package/dist/SheetGrid/index.js +5 -4
  48. package/dist/{SheetGrid-GDPCCZ4V.css → SheetGrid-YRDXTIIZ.css} +48 -0
  49. package/dist/SheetModel/index.js +1 -1
  50. package/dist/SheetModel/labels.d.ts +6 -0
  51. package/dist/SlideFilmstrip/SlideFilmstrip.d.ts +30 -1
  52. package/dist/SlideFilmstrip/index.js +5 -1
  53. package/dist/{SlideFilmstrip-5U6Z55GS.css → SlideFilmstrip-4ITZLMO4.css} +49 -0
  54. package/dist/SourceEditor/SourceEditor.d.ts +62 -0
  55. package/dist/SourceEditor/index.d.ts +3 -0
  56. package/dist/SourceEditor/index.js +3 -0
  57. package/dist/SourceEditor-G2C3STPX.css +160 -0
  58. package/dist/ThemeGallery/ThemeGallery.d.ts +10 -3
  59. package/dist/ThemeGallery/index.js +1 -1
  60. package/dist/{ThemeGallery-NITASULQ.css → ThemeGallery-HSUKBDWY.css} +9 -0
  61. package/dist/{chunk-UXXJDIYE.js → chunk-22CQQDFW.js} +1 -1
  62. package/dist/{chunk-EQ7P7DGN.js → chunk-3VD3G3IL.js} +2 -2
  63. package/dist/{chunk-EWAFN2IU.js → chunk-5VCG26CV.js} +281 -234
  64. package/dist/{chunk-7T5LSYZ2.js → chunk-7UYONN6K.js} +4 -3
  65. package/dist/{chunk-KK5OYNAF.js → chunk-BAKPMH3S.js} +8 -4
  66. package/dist/{chunk-3EB2AV3H.js → chunk-BUVCV3WI.js} +1 -1
  67. package/dist/{chunk-4B6LSFDS.js → chunk-CSQTDGTS.js} +1 -1
  68. package/dist/{chunk-WPX6A5BV.js → chunk-E35CMI5D.js} +22 -6
  69. package/dist/{chunk-OMLACFPP.js → chunk-FJJHD73S.js} +1 -1
  70. package/dist/{chunk-SGHIAGPR.js → chunk-HAKTRTRP.js} +1 -1
  71. package/dist/chunk-MAD6WPER.js +342 -0
  72. package/dist/{chunk-ZADVT7IL.js → chunk-NKZCJRKO.js} +9 -4
  73. package/dist/chunk-NTB5LRRD.js +52 -0
  74. package/dist/{chunk-VI5FWDZW.js → chunk-OOMXLJ3S.js} +4 -1
  75. package/dist/{chunk-UOR7TOAU.js → chunk-OSFA5ROT.js} +86 -1
  76. package/dist/{chunk-T46NR3WP.js → chunk-PGZ2R3ON.js} +1 -1
  77. package/dist/chunk-PI7HKLIT.js +245 -0
  78. package/dist/{chunk-YZFLH463.js → chunk-QEFHYUCY.js} +1 -1
  79. package/dist/{chunk-NPQWKZTB.js → chunk-QFNWMWWY.js} +2 -2
  80. package/dist/{chunk-C7DKX453.js → chunk-QH6VYJ3X.js} +1 -1
  81. package/dist/{chunk-XV32RKQZ.js → chunk-QIXMNOBD.js} +67 -6
  82. package/dist/{chunk-GPHWCK7M.js → chunk-SUXP4OBT.js} +6 -3
  83. package/dist/{chunk-TRQCHF2F.js → chunk-SWD4QBL6.js} +1 -1
  84. package/dist/chunk-THDGBD6Y.js +154 -0
  85. package/dist/{chunk-C2FRTWKO.js → chunk-W3UVOZKL.js} +3 -2
  86. package/dist/{chunk-UDQW6UK7.js → chunk-W7QBBKUE.js} +34 -7
  87. package/dist/{chunk-QBXZLGK2.js → chunk-ZU3DUXD2.js} +3 -2
  88. package/dist/generated/index.d.ts +4 -0
  89. package/dist/generated/index.js +56 -52
  90. package/dist/index.js +56 -52
  91. package/dist/styles.css +652 -51
  92. package/dist/utils/csv.d.ts +35 -0
  93. package/dist/utils/index.d.ts +2 -1
  94. package/dist/utils/index.js +1 -1
  95. package/package.json +3 -3
  96. /package/dist/{chunk-VFRSSP3W.js → chunk-3WCNHYXE.js} +0 -0
@@ -22,6 +22,91 @@ function text(value) {
22
22
  function quote(written) {
23
23
  return /[",\r\n]/.test(written) ? `"${written.replace(/"/g, '""')}"` : written;
24
24
  }
25
+ var DELIMITERS = [",", ";", " "];
26
+ function parseCsv(text2, options = {}) {
27
+ const source = text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
28
+ const delimiter = options.delimiter ?? sniffDelimiter(source);
29
+ const maxRows = options.maxRows ?? Number.POSITIVE_INFINITY;
30
+ const rows = [];
31
+ let record = [];
32
+ let value = "";
33
+ let quoted = false;
34
+ let wasQuoted = false;
35
+ let line = 1;
36
+ let quoteLine = 1;
37
+ let i = 0;
38
+ const endField = () => {
39
+ record.push(undefused(value));
40
+ value = "";
41
+ wasQuoted = false;
42
+ };
43
+ const endRecord = () => {
44
+ endField();
45
+ rows.push(record);
46
+ record = [];
47
+ return rows.length <= maxRows;
48
+ };
49
+ while (i < source.length) {
50
+ const ch = source[i];
51
+ if (quoted) {
52
+ if (ch === '"') {
53
+ if (source[i + 1] === '"') {
54
+ value += '"';
55
+ i += 2;
56
+ continue;
57
+ }
58
+ quoted = false;
59
+ i += 1;
60
+ continue;
61
+ }
62
+ if (ch === "\n" || ch === "\r" && source[i + 1] !== "\n") line += 1;
63
+ value += ch;
64
+ i += 1;
65
+ continue;
66
+ }
67
+ if (ch === '"' && value === "" && !wasQuoted) {
68
+ quoted = true;
69
+ wasQuoted = true;
70
+ quoteLine = line;
71
+ i += 1;
72
+ continue;
73
+ }
74
+ if (ch === delimiter) {
75
+ endField();
76
+ i += 1;
77
+ continue;
78
+ }
79
+ if (ch === "\r" || ch === "\n") {
80
+ const step = ch === "\r" && source[i + 1] === "\n" ? 2 : 1;
81
+ if (!endRecord()) return { ok: false, problem: "too_many_rows", line };
82
+ line += 1;
83
+ i += step;
84
+ continue;
85
+ }
86
+ value += ch;
87
+ i += 1;
88
+ }
89
+ if (quoted) return { ok: false, problem: "unterminated_quote", line: quoteLine };
90
+ if (value !== "" || wasQuoted || record.length > 0) {
91
+ if (!endRecord()) return { ok: false, problem: "too_many_rows", line };
92
+ }
93
+ return { ok: true, rows, delimiter };
94
+ }
95
+ function sniffDelimiter(source) {
96
+ const counts = new Map(DELIMITERS.map((d) => [d, 0]));
97
+ let quoted = false;
98
+ for (const ch of source) {
99
+ if (ch === '"') quoted = !quoted;
100
+ else if (!quoted && (ch === "\n" || ch === "\r")) break;
101
+ else if (!quoted && counts.has(ch)) counts.set(ch, counts.get(ch) + 1);
102
+ }
103
+ let best = ",";
104
+ for (const d of DELIMITERS) if (counts.get(d) > counts.get(best)) best = d;
105
+ return best;
106
+ }
107
+ function undefused(field2) {
108
+ return field2.length > 1 && field2[0] === "'" && FORMULA_LEAD.test(field2.slice(1)) ? field2.slice(1) : field2;
109
+ }
25
110
 
26
111
  // src/utils/elementToPng.ts
27
112
  var ElementToPngError = class extends Error {
@@ -188,4 +273,4 @@ function toBlob(canvas) {
188
273
  });
189
274
  }
190
275
 
191
- export { ElementToPngError, elementToPng, serializeElement, toCsv };
276
+ export { ElementToPngError, elementToPng, parseCsv, serializeElement, toCsv };
@@ -1,5 +1,5 @@
1
1
  import { useMediaMatch } from './chunk-RTPQDNW3.js';
2
- import { Menu } from './chunk-3EB2AV3H.js';
2
+ import { Menu } from './chunk-BUVCV3WI.js';
3
3
  import { useModalLayer, focusables } from './chunk-R25RGPUB.js';
4
4
  import { IconButton } from './chunk-4FQT6ZAY.js';
5
5
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
@@ -0,0 +1,245 @@
1
+ import { VisuallyHidden } from './chunk-BZFWDIV6.js';
2
+ import { cx } from './chunk-ODEQTPFM.js';
3
+ import './SourceEditor-G2C3STPX.css';
4
+ import { forwardRef, useRef, useId, useState, useImperativeHandle, useLayoutEffect } from 'react';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ var SOURCE_EDITOR_KEYS = "Tab indents and Shift+Tab outdents. Press Escape, then Tab, to move to the next control.";
8
+ function lineStart(text, line) {
9
+ let at = 0;
10
+ for (let n = 1; n < line; n++) {
11
+ const next = text.indexOf("\n", at);
12
+ if (next < 0) return text.length;
13
+ at = next + 1;
14
+ }
15
+ return at;
16
+ }
17
+ function insert(field, text, start, end) {
18
+ field.setSelectionRange(start, end);
19
+ const done = typeof document.execCommand === "function" && document.execCommand("insertText", false, text);
20
+ if (!done) field.setRangeText(text, start, end, "end");
21
+ return field.value;
22
+ }
23
+ var SourceEditor = forwardRef(
24
+ function SourceEditor2({
25
+ value,
26
+ onChange,
27
+ label,
28
+ marks = [],
29
+ onSubmit,
30
+ indent = 2,
31
+ fill = false,
32
+ rows = 12,
33
+ className,
34
+ fieldClassName,
35
+ onKeyDown,
36
+ onScroll,
37
+ "aria-describedby": describedBy,
38
+ ...rest
39
+ }, ref) {
40
+ const field = useRef(null);
41
+ const tabLeaves = useRef(false);
42
+ const keysId = useId();
43
+ const [scrollTop, setScrollTop] = useState(0);
44
+ const lines = countLines(value);
45
+ const tone = toneByLine(marks, lines);
46
+ useImperativeHandle(
47
+ ref,
48
+ () => ({
49
+ revealLine(line) {
50
+ const el = field.current;
51
+ if (!el) return;
52
+ const at = lineStart(el.value, Math.max(1, line));
53
+ el.focus();
54
+ el.setSelectionRange(at, at);
55
+ const height = parseFloat(getComputedStyle(el).lineHeight) || 20;
56
+ const top = (Math.max(1, line) - 1) * height;
57
+ if (top < el.scrollTop || top + height > el.scrollTop + el.clientHeight) {
58
+ el.scrollTop = Math.max(0, top - el.clientHeight / 3);
59
+ }
60
+ },
61
+ focus() {
62
+ field.current?.focus();
63
+ },
64
+ get element() {
65
+ return field.current;
66
+ }
67
+ }),
68
+ []
69
+ );
70
+ useLayoutEffect(() => {
71
+ const el = field.current;
72
+ if (el && el.scrollTop !== scrollTop) setScrollTop(el.scrollTop);
73
+ }, [value, scrollTop]);
74
+ const keyDown = (event) => {
75
+ onKeyDown?.(event);
76
+ if (event.defaultPrevented) return;
77
+ const el = event.currentTarget;
78
+ if (event.key === "Escape") {
79
+ tabLeaves.current = true;
80
+ return;
81
+ }
82
+ if (event.key === "Tab") {
83
+ if (tabLeaves.current || event.altKey || event.metaKey || event.ctrlKey) {
84
+ tabLeaves.current = false;
85
+ return;
86
+ }
87
+ event.preventDefault();
88
+ onChange(
89
+ event.shiftKey ? outdent(el, indent) : indentLines(el, indent)
90
+ );
91
+ return;
92
+ }
93
+ tabLeaves.current = false;
94
+ if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
95
+ if (!onSubmit) return;
96
+ event.preventDefault();
97
+ onSubmit();
98
+ return;
99
+ }
100
+ if (event.key === "Enter" && !event.shiftKey && !event.altKey) {
101
+ const before = el.value.slice(0, el.selectionStart);
102
+ const lead = /^[\t ]*/.exec(before.slice(before.lastIndexOf("\n") + 1))?.[0] ?? "";
103
+ if (!lead) return;
104
+ event.preventDefault();
105
+ onChange(insert(el, `
106
+ ${lead}`, el.selectionStart, el.selectionEnd));
107
+ }
108
+ };
109
+ return /* @__PURE__ */ jsxs(
110
+ "div",
111
+ {
112
+ className: cx("crewlet-source-editor", fill && "is-fill", className),
113
+ style: {
114
+ "--crewlet-source-digits": String(String(lines).length)
115
+ },
116
+ children: [
117
+ /* @__PURE__ */ jsx("div", { className: "crewlet-source-editor__gutter", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
118
+ "div",
119
+ {
120
+ className: "crewlet-source-editor__numbers",
121
+ style: { transform: `translateY(${-scrollTop}px)` },
122
+ children: Array.from({ length: lines }, (_, i) => {
123
+ const line = i + 1;
124
+ const mark = tone.get(line);
125
+ return /* @__PURE__ */ jsx(
126
+ "span",
127
+ {
128
+ className: cx(
129
+ "crewlet-source-editor__number",
130
+ mark && `is-${mark.tone}`
131
+ ),
132
+ title: mark?.message,
133
+ children: line
134
+ },
135
+ line
136
+ );
137
+ })
138
+ }
139
+ ) }),
140
+ /* @__PURE__ */ jsxs("div", { className: "crewlet-source-editor__body", children: [
141
+ /* @__PURE__ */ jsx("div", { className: "crewlet-source-editor__marks", "aria-hidden": "true", children: /* @__PURE__ */ jsx("div", { style: { transform: `translateY(${-scrollTop}px)` }, children: [...tone.entries()].map(([line, mark]) => /* @__PURE__ */ jsx(
142
+ "span",
143
+ {
144
+ className: cx(
145
+ "crewlet-source-editor__mark",
146
+ `is-${mark.tone}`
147
+ ),
148
+ style: {
149
+ "--crewlet-source-line": String(line - 1)
150
+ }
151
+ },
152
+ line
153
+ )) }) }),
154
+ /* @__PURE__ */ jsx(
155
+ "textarea",
156
+ {
157
+ ref: field,
158
+ className: cx("crewlet-source-editor__field", fieldClassName),
159
+ "aria-label": label,
160
+ "aria-describedby": cx(keysId, describedBy) || void 0,
161
+ "aria-invalid": [...tone.values()].some((m) => m.tone === "danger") || void 0,
162
+ value,
163
+ rows,
164
+ wrap: "off",
165
+ spellCheck: false,
166
+ autoCapitalize: "off",
167
+ autoComplete: "off",
168
+ autoCorrect: "off",
169
+ onChange: (event) => onChange(event.target.value),
170
+ onKeyDown: keyDown,
171
+ onBlur: () => {
172
+ tabLeaves.current = false;
173
+ },
174
+ onScroll: (event) => {
175
+ onScroll?.(event);
176
+ setScrollTop(event.currentTarget.scrollTop);
177
+ },
178
+ ...rest
179
+ }
180
+ )
181
+ ] }),
182
+ /* @__PURE__ */ jsx(VisuallyHidden, { id: keysId, children: SOURCE_EDITOR_KEYS })
183
+ ]
184
+ }
185
+ );
186
+ }
187
+ );
188
+ function countLines(text) {
189
+ let n = 1;
190
+ for (let i = text.indexOf("\n"); i >= 0; i = text.indexOf("\n", i + 1)) n++;
191
+ return n;
192
+ }
193
+ function toneByLine(marks, lines) {
194
+ const out = /* @__PURE__ */ new Map();
195
+ for (const mark of marks) {
196
+ if (!Number.isInteger(mark.line) || mark.line < 1 || mark.line > lines)
197
+ continue;
198
+ const held = out.get(mark.line);
199
+ if (!held || held.tone === "warning" && mark.tone === "danger")
200
+ out.set(mark.line, mark);
201
+ }
202
+ return out;
203
+ }
204
+ function selectedLines(el) {
205
+ const text = el.value;
206
+ const from = text.lastIndexOf("\n", el.selectionStart - 1) + 1;
207
+ const endAt = el.selectionEnd > el.selectionStart && text[el.selectionEnd - 1] === "\n" ? el.selectionEnd - 1 : el.selectionEnd;
208
+ const next = text.indexOf("\n", endAt);
209
+ return { from, to: next < 0 ? text.length : next };
210
+ }
211
+ function indentLines(el, size) {
212
+ const pad = " ".repeat(size);
213
+ if (el.selectionStart === el.selectionEnd) {
214
+ const column = el.selectionStart - (el.value.lastIndexOf("\n", el.selectionStart - 1) + 1);
215
+ return insert(
216
+ el,
217
+ " ".repeat(size - column % size),
218
+ el.selectionStart,
219
+ el.selectionEnd
220
+ );
221
+ }
222
+ const { from, to } = selectedLines(el);
223
+ const block = el.value.slice(from, to).replace(/^/gm, pad);
224
+ const value = insert(el, block, from, to);
225
+ el.setSelectionRange(from, from + block.length);
226
+ return value;
227
+ }
228
+ function outdent(el, size) {
229
+ const { from, to } = selectedLines(el);
230
+ const strip = new RegExp(`^(?: {1,${size}}|\\t)`, "gm");
231
+ const original = el.value.slice(from, to);
232
+ const block = original.replace(strip, "");
233
+ if (block === original) return el.value;
234
+ const caret = el.selectionStart === el.selectionEnd;
235
+ const at = el.selectionStart;
236
+ const removed = (/^(?: +|\t)/.exec(original)?.[0].length ?? 0) - (/^(?: +|\t)/.exec(block)?.[0].length ?? 0);
237
+ const value = insert(el, block, from, to);
238
+ if (caret) {
239
+ const back = Math.max(from, at - removed);
240
+ el.setSelectionRange(back, back);
241
+ } else el.setSelectionRange(from, from + block.length);
242
+ return value;
243
+ }
244
+
245
+ export { SOURCE_EDITOR_KEYS, SourceEditor, lineStart };
@@ -1,6 +1,6 @@
1
1
  import { useReorder } from './chunk-XJ2QEBMM.js';
2
2
  import { Input } from './chunk-6QFAK2WV.js';
3
- import { Menu } from './chunk-3EB2AV3H.js';
3
+ import { Menu } from './chunk-BUVCV3WI.js';
4
4
  import { IconButton } from './chunk-4FQT6ZAY.js';
5
5
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
6
6
  import { cx } from './chunk-ODEQTPFM.js';
@@ -1,5 +1,5 @@
1
1
  import { PageHeader } from './chunk-HOXGBLEY.js';
2
- import { DataTable } from './chunk-UXXJDIYE.js';
2
+ import { DataTable } from './chunk-22CQQDFW.js';
3
3
  import { DateTimePicker } from './chunk-73A7D5CE.js';
4
4
  import { useListbox, useOptionKeys } from './chunk-F73MC7EZ.js';
5
5
  import { Toolbar } from './chunk-PGRPNDWM.js';
@@ -8,7 +8,7 @@ import { Input } from './chunk-6QFAK2WV.js';
8
8
  import { Kbd } from './chunk-SMBKXWML.js';
9
9
  import { RelativeTime } from './chunk-3CZ7XAVY.js';
10
10
  import { Count } from './chunk-BHHIIK3M.js';
11
- import { Menu } from './chunk-3EB2AV3H.js';
11
+ import { Menu } from './chunk-BUVCV3WI.js';
12
12
  import { Button } from './chunk-OG6CL3EB.js';
13
13
  import { IconButton } from './chunk-4FQT6ZAY.js';
14
14
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
@@ -1,4 +1,4 @@
1
- import { MARKDOWN_CALLOUT_TONES, parseMarkdownSpans, markdownInline, markdownCodeText } from './chunk-KK5OYNAF.js';
1
+ import { MARKDOWN_CALLOUT_TONES, parseMarkdownSpans, markdownInline, markdownCodeText } from './chunk-BAKPMH3S.js';
2
2
  import { Schema, Fragment } from 'prosemirror-model';
3
3
 
4
4
  function headingTag(level) {
@@ -1,18 +1,24 @@
1
1
  import { SlideStage } from './chunk-LV4APO72.js';
2
2
  import { useReorder } from './chunk-XJ2QEBMM.js';
3
+ import { Kbd, isApplePlatform } from './chunk-SMBKXWML.js';
4
+ import { Menu } from './chunk-BUVCV3WI.js';
3
5
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
4
6
  import { cx } from './chunk-ODEQTPFM.js';
5
- import './SlideFilmstrip-5U6Z55GS.css';
6
- import { useRef, useLayoutEffect } from 'react';
7
- import { EyeOffGlyph, GripVerticalGlyph } from '@crewlethq/icons/glyphs';
7
+ import './SlideFilmstrip-4ITZLMO4.css';
8
+ import { useRef, useLayoutEffect, useState } from 'react';
9
+ import { EyeOffGlyph, GripVerticalGlyph, EllipsisGlyph, CopyGlyph, TrashGlyph } from '@crewlethq/icons/glyphs';
8
10
  import { jsxs, jsx } from 'react/jsx-runtime';
9
11
 
10
12
  var LABELS = {
11
13
  list: "Slides",
12
14
  slide: (position, title) => `Slide ${position}, ${title}`,
13
15
  hidden: "Hidden when presenting",
14
- untitled: "Untitled"
16
+ untitled: "Untitled",
17
+ actions: (position, title) => `Actions for slide ${position}, ${title}`,
18
+ duplicate: "Duplicate slide",
19
+ delete: "Delete slide"
15
20
  };
21
+ var DELETE_KEY = () => isApplePlatform() ? "Backspace" : "Delete";
16
22
  var STEP_KEYS = {
17
23
  vertical: { back: "ArrowUp", forward: "ArrowDown" },
18
24
  horizontal: { back: "ArrowLeft", forward: "ArrowRight" }
@@ -24,6 +30,10 @@ function SlideFilmstrip({
24
30
  renderSlide,
25
31
  onReorder,
26
32
  orientation = "vertical",
33
+ onDuplicate,
34
+ onDelete,
35
+ actions,
36
+ footer,
27
37
  className,
28
38
  labels
29
39
  }) {
@@ -53,7 +63,43 @@ function SlideFilmstrip({
53
63
  onSelect(id);
54
64
  options.current.get(id)?.focus();
55
65
  };
66
+ const [menuFor, setMenuFor] = useState(null);
67
+ const menuOf = (id) => [
68
+ ...onDuplicate ? [{
69
+ key: "duplicate",
70
+ label: text.duplicate,
71
+ icon: /* @__PURE__ */ jsx(CopyGlyph, { size: "sm" }),
72
+ onSelect: () => onDuplicate(id),
73
+ hint: /* @__PURE__ */ jsx(Kbd, { subtle: true, keys: ["Mod", "D"] })
74
+ }] : [],
75
+ ...onDelete ? [{
76
+ key: "delete",
77
+ label: text.delete,
78
+ icon: /* @__PURE__ */ jsx(TrashGlyph, { size: "sm" }),
79
+ onSelect: () => onDelete(id),
80
+ danger: true,
81
+ hint: /* @__PURE__ */ jsx(Kbd, { subtle: true, keys: [DELETE_KEY()] })
82
+ }] : [],
83
+ ...actions?.(id) ?? []
84
+ ];
56
85
  const onKeyDown = (event) => {
86
+ const mod = event.metaKey || event.ctrlKey;
87
+ if (onDuplicate && mod && !event.altKey && !event.shiftKey && event.key.toLowerCase() === "d") {
88
+ event.preventDefault();
89
+ onDuplicate(selected);
90
+ return;
91
+ }
92
+ if (event.key === "ContextMenu" || event.shiftKey && event.key === "F10") {
93
+ if (menuOf(selected).length === 0) return;
94
+ event.preventDefault();
95
+ setMenuFor(selected);
96
+ return;
97
+ }
98
+ if (onDelete && !mod && !event.altKey && (event.key === "Delete" || event.key === "Backspace")) {
99
+ event.preventDefault();
100
+ onDelete(selected);
101
+ return;
102
+ }
57
103
  const at = order.indexOf(selected);
58
104
  const step = STEP_KEYS[orientation];
59
105
  const to = {
@@ -82,6 +128,7 @@ function SlideFilmstrip({
82
128
  children: order.map((id, i) => {
83
129
  const slide = byId.get(id);
84
130
  const isSelected = id === selected;
131
+ const menu = menuOf(id);
85
132
  return /* @__PURE__ */ jsxs(
86
133
  "li",
87
134
  {
@@ -111,14 +158,28 @@ function SlideFilmstrip({
111
158
  ]
112
159
  }
113
160
  ),
114
- onReorder ? /* @__PURE__ */ jsx("button", { type: "button", className: "crewlet-slide-filmstrip__handle", ...reorder.handleProps(id), children: /* @__PURE__ */ jsx(GripVerticalGlyph, { size: "sm" }) }) : null
161
+ onReorder || menu.length > 0 ? /* @__PURE__ */ jsxs("span", { className: "crewlet-slide-filmstrip__controls", children: [
162
+ onReorder ? /* @__PURE__ */ jsx("button", { type: "button", className: "crewlet-slide-filmstrip__handle", ...reorder.handleProps(id), children: /* @__PURE__ */ jsx(GripVerticalGlyph, { size: "sm" }) }) : null,
163
+ menu.length > 0 ? /* @__PURE__ */ jsx(
164
+ Menu,
165
+ {
166
+ label: text.actions(i + 1, titleOf(id)),
167
+ icon: /* @__PURE__ */ jsx(EllipsisGlyph, { size: "sm" }),
168
+ triggerTabIndex: -1,
169
+ items: menu,
170
+ open: menuFor === id,
171
+ onOpenChange: (open) => setMenuFor(open ? id : null)
172
+ }
173
+ ) : null
174
+ ] }) : null
115
175
  ]
116
176
  },
117
177
  id
118
178
  );
119
179
  })
120
180
  }
121
- )
181
+ ),
182
+ footer ? /* @__PURE__ */ jsx("div", { className: "crewlet-slide-filmstrip__footer", children: footer }) : null
122
183
  ]
123
184
  }
124
185
  );
@@ -3,10 +3,13 @@ import { SegmentedControl } from './chunk-CCAXYHQS.js';
3
3
  import { cx } from './chunk-ODEQTPFM.js';
4
4
  import { jsx } from 'react/jsx-runtime';
5
5
 
6
+ function HueSwatch({ hue, className }) {
7
+ return /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cx("crewlet-hue-picker__swatch", `crewlet-hue-picker__swatch--${hue}`, className) });
8
+ }
6
9
  var optionsFor = (disabled) => NODE_HUES.map((hue) => ({
7
10
  value: hue,
8
11
  label: NODE_HUE_NAMES[hue],
9
- icon: /* @__PURE__ */ jsx("span", { className: `crewlet-hue-picker__swatch crewlet-hue-picker__swatch--${hue}` }),
12
+ icon: /* @__PURE__ */ jsx(HueSwatch, { hue }),
10
13
  disabled
11
14
  }));
12
15
  var ENABLED = optionsFor(false);
@@ -18,7 +21,7 @@ function HuePicker(props) {
18
21
  {
19
22
  value: "none",
20
23
  label: props.none,
21
- icon: /* @__PURE__ */ jsx("span", { className: "crewlet-hue-picker__swatch crewlet-hue-picker__swatch--none" }),
24
+ icon: /* @__PURE__ */ jsx(HueSwatch, { hue: "none" }),
22
25
  disabled
23
26
  },
24
27
  ...hues
@@ -42,4 +45,4 @@ function HuePicker(props) {
42
45
  );
43
46
  }
44
47
 
45
- export { HuePicker };
48
+ export { HuePicker, HueSwatch };
@@ -1,4 +1,4 @@
1
- import { readMarkdown, writeMarkdown } from './chunk-C7DKX453.js';
1
+ import { readMarkdown, writeMarkdown } from './chunk-QH6VYJ3X.js';
2
2
 
3
3
  // src/RichEditor/roundtrip.ts
4
4
  function roundTripMarkdown(markdown) {