@pixerate/editor-react 0.16.0 → 0.18.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 (46) hide show
  1. package/README.md +4 -2
  2. package/dist/canvas/index.cjs +1094 -143
  3. package/dist/canvas/index.cjs.map +1 -1
  4. package/dist/canvas/index.d.cts +277 -3
  5. package/dist/canvas/index.d.ts +277 -3
  6. package/dist/canvas/index.js +976 -0
  7. package/dist/canvas/index.js.map +1 -1
  8. package/dist/chunk-34HEZX7J.js +2144 -0
  9. package/dist/chunk-34HEZX7J.js.map +1 -0
  10. package/dist/chunk-I3E4KSA2.js +515 -0
  11. package/dist/chunk-I3E4KSA2.js.map +1 -0
  12. package/dist/{chunk-XTFR3IMK.js → chunk-ZU54BMDC.js} +473 -160
  13. package/dist/chunk-ZU54BMDC.js.map +1 -0
  14. package/dist/generation/index.cjs +290 -0
  15. package/dist/generation/index.cjs.map +1 -1
  16. package/dist/generation/index.d.cts +66 -2
  17. package/dist/generation/index.d.ts +66 -2
  18. package/dist/generation/index.js +7 -1
  19. package/dist/image-editor/index.cjs +490 -187
  20. package/dist/image-editor/index.cjs.map +1 -1
  21. package/dist/image-editor/index.d.cts +34 -2
  22. package/dist/image-editor/index.d.ts +34 -2
  23. package/dist/image-editor/index.js +5 -1
  24. package/dist/index.cjs +2792 -823
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +188 -7
  27. package/dist/index.d.ts +188 -7
  28. package/dist/index.js +600 -158
  29. package/dist/index.js.map +1 -1
  30. package/dist/spreadsheet/index.cjs +1099 -142
  31. package/dist/spreadsheet/index.cjs.map +1 -1
  32. package/dist/spreadsheet/index.d.cts +156 -8
  33. package/dist/spreadsheet/index.d.ts +156 -8
  34. package/dist/spreadsheet/index.js +15 -3
  35. package/dist/tokens/index.cjs +395 -0
  36. package/dist/tokens/index.cjs.map +1 -0
  37. package/dist/tokens/index.d.cts +54 -0
  38. package/dist/tokens/index.d.ts +54 -0
  39. package/dist/tokens/index.js +375 -0
  40. package/dist/tokens/index.js.map +1 -0
  41. package/package.json +10 -2
  42. package/dist/chunk-7SPRQZ6H.js +0 -216
  43. package/dist/chunk-7SPRQZ6H.js.map +0 -1
  44. package/dist/chunk-FYIPXHAE.js +0 -1169
  45. package/dist/chunk-FYIPXHAE.js.map +0 -1
  46. package/dist/chunk-XTFR3IMK.js.map +0 -1
@@ -31,12 +31,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var spreadsheet_exports = {};
32
32
  __export(spreadsheet_exports, {
33
33
  FormulaHints: () => FormulaHints,
34
+ NameManager: () => NameManager,
35
+ SheetTabs: () => SheetTabs,
36
+ SpreadsheetConditionalFormatPanel: () => SpreadsheetConditionalFormatPanel,
34
37
  SpreadsheetContextMenu: () => SpreadsheetContextMenu,
38
+ SpreadsheetDataValidationPanel: () => SpreadsheetDataValidationPanel,
35
39
  SpreadsheetEditor: () => SpreadsheetEditor,
36
40
  SpreadsheetGroupRow: () => SpreadsheetGroupRow,
41
+ SpreadsheetValidationDialog: () => SpreadsheetValidationDialog,
37
42
  useFillHandle: () => useFillHandle,
38
43
  useSpreadsheetClipboard: () => useSpreadsheetClipboard,
39
- useSpreadsheetEditor: () => useSpreadsheetEditor
44
+ useSpreadsheetEditor: () => useSpreadsheetEditor,
45
+ useSpreadsheetViewport: () => useSpreadsheetViewport
40
46
  });
41
47
  module.exports = __toCommonJS(spreadsheet_exports);
42
48
 
@@ -69,6 +75,7 @@ function useSpreadsheetEditor(options = {}) {
69
75
  const [selectedRange, setSelectedRange] = (0, import_react.useState)(controller.selectedRange);
70
76
  const [editingCell, setEditingCell] = (0, import_react.useState)(null);
71
77
  const [draftValue, setDraftState] = (0, import_react.useState)("");
78
+ const [validationPrompt, setValidationPrompt] = (0, import_react.useState)(null);
72
79
  settersRef.current = { setDoc, setActiveCell, setSelectedRange };
73
80
  const setDraftValue = (0, import_react.useCallback)((value) => {
74
81
  controller.draftValue = value;
@@ -93,12 +100,28 @@ function useSpreadsheetEditor(options = {}) {
93
100
  setEditingCell(controller.editingCell);
94
101
  setDraftValue(controller.draftValue);
95
102
  }, [controller]);
96
- const commitEditing = (0, import_react.useCallback)((val) => {
97
- controller.commitEditing(val);
98
- setEditingCell(null);
99
- setDraftValue("");
103
+ const syncEditing = (0, import_react.useCallback)(() => {
104
+ setEditingCell(controller.editingCell);
105
+ setDraftValue(controller.draftValue);
106
+ setValidationPrompt(controller.validationPrompt);
107
+ setActiveCell(controller.activeCell);
108
+ setSelectedRange(controller.selectedRange);
100
109
  setDoc({ ...controller.document });
101
110
  }, [controller]);
111
+ const commitEditing = (0, import_react.useCallback)((val) => {
112
+ const result = controller.commitEditing(val);
113
+ syncEditing();
114
+ return result;
115
+ }, [controller, syncEditing]);
116
+ const commitCellInput = (0, import_react.useCallback)((rowId, colId, raw) => {
117
+ const result = controller.commitCellInput(rowId, colId, raw);
118
+ syncEditing();
119
+ return result;
120
+ }, [controller, syncEditing]);
121
+ const resolveValidationPrompt = (0, import_react.useCallback)((action) => {
122
+ controller.resolveValidationPrompt(action);
123
+ syncEditing();
124
+ }, [controller, syncEditing]);
102
125
  const cancelEditing = (0, import_react.useCallback)(() => {
103
126
  controller.cancelEditing();
104
127
  setEditingCell(null);
@@ -148,13 +171,57 @@ function useSpreadsheetEditor(options = {}) {
148
171
  const undo = (0, import_react.useCallback)(() => {
149
172
  controller.undo();
150
173
  setDoc({ ...controller.document });
174
+ setActiveCell(controller.activeCell);
175
+ setSelectedRange(controller.selectedRange);
151
176
  }, [controller]);
152
177
  const redo = (0, import_react.useCallback)(() => {
153
178
  controller.redo();
154
179
  setDoc({ ...controller.document });
180
+ setActiveCell(controller.activeCell);
181
+ setSelectedRange(controller.selectedRange);
155
182
  }, [controller]);
156
183
  const tableActions = (0, import_react.useMemo)(() => (0, import_spreadsheet.bindSpreadsheetTableActions)(controller), [controller]);
157
184
  const viewModel = (0, import_react.useMemo)(() => controller.getViewModel(), [controller, doc]);
185
+ const sheets = (0, import_react.useMemo)(() => controller.getSheets(), [controller, doc]);
186
+ const names = (0, import_react.useMemo)(() => controller.getNames(), [controller, doc]);
187
+ const afterSheetSwitch = (0, import_react.useCallback)(() => {
188
+ setEditingCell(controller.editingCell);
189
+ setDraftValue(controller.draftValue);
190
+ setActiveCell(controller.activeCell);
191
+ setSelectedRange(controller.selectedRange);
192
+ setDoc({ ...controller.document });
193
+ }, [controller, setDraftValue]);
194
+ const workbookActions = (0, import_react.useMemo)(
195
+ () => ({
196
+ setActiveSheet: (idOrName) => {
197
+ const changed = controller.setActiveSheet(idOrName);
198
+ afterSheetSwitch();
199
+ return changed;
200
+ },
201
+ addSheet: (options2) => {
202
+ const id = controller.addSheet(options2);
203
+ afterSheetSwitch();
204
+ return id;
205
+ },
206
+ renameSheet: (idOrName, newName) => controller.renameSheet(idOrName, newName),
207
+ moveSheet: (idOrName, toIndex) => controller.moveSheet(idOrName, toIndex),
208
+ deleteSheet: (idOrName) => {
209
+ const deleted = controller.deleteSheet(idOrName);
210
+ afterSheetSwitch();
211
+ return deleted;
212
+ },
213
+ duplicateSheet: (idOrName, options2) => {
214
+ const id = controller.duplicateSheet(idOrName, options2);
215
+ afterSheetSwitch();
216
+ return id;
217
+ },
218
+ defineName: (name, refersTo, options2) => controller.defineName(name, refersTo, options2),
219
+ renameName: (oldName, newName, options2) => controller.renameName(oldName, newName, options2),
220
+ deleteName: (name, options2) => controller.deleteName(name, options2)
221
+ }),
222
+ [controller, afterSheetSwitch]
223
+ );
224
+ const conditionalFormatOverlay = (0, import_react.useMemo)(() => controller.getConditionalFormatOverlay(), [controller, doc]);
158
225
  return {
159
226
  document: doc,
160
227
  activeCell,
@@ -162,6 +229,9 @@ function useSpreadsheetEditor(options = {}) {
162
229
  editingCell,
163
230
  draftValue,
164
231
  setDraftValue,
232
+ validationPrompt,
233
+ resolveValidationPrompt,
234
+ commitCellInput,
165
235
  controller,
166
236
  getCell: (rowId, colId) => controller.getCell(rowId, colId),
167
237
  setCellValue,
@@ -200,21 +270,28 @@ function useSpreadsheetEditor(options = {}) {
200
270
  },
201
271
  ...tableActions,
202
272
  viewModel,
273
+ /** The sheets in tab order (see `SpreadsheetController.getSheets`). */
274
+ sheets,
275
+ activeSheetId: controller.activeSheetId,
276
+ /** Defined names (see `SpreadsheetController.getNames`). */
277
+ names,
278
+ ...workbookActions,
279
+ conditionalFormatOverlay,
203
280
  undo,
204
281
  redo
205
282
  };
206
283
  }
207
284
 
208
285
  // src/spreadsheet/SpreadsheetEditor.tsx
209
- var import_react5 = require("react");
210
- var import_spreadsheet5 = require("@pixerate/editor/spreadsheet");
286
+ var import_react10 = require("react");
287
+ var import_spreadsheet8 = require("@pixerate/editor/spreadsheet");
211
288
 
212
289
  // src/spreadsheet/SpreadsheetContextMenu.tsx
213
290
  var import_react2 = __toESM(require("react"), 1);
214
291
  var import_spreadsheet2 = require("@pixerate/editor/spreadsheet");
215
292
  var import_jsx_runtime = require("react/jsx-runtime");
216
293
  var VIEWPORT_MARGIN = 4;
217
- var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
294
+ var SpreadsheetContextMenu = ({ items, x, y, onClose, onOpenPanel, ariaLabel = "Table actions" }) => {
218
295
  const menuRef = (0, import_react2.useRef)(null);
219
296
  const onCloseRef = (0, import_react2.useRef)(onClose);
220
297
  onCloseRef.current = onClose;
@@ -250,7 +327,7 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
250
327
  {
251
328
  ref: menuRef,
252
329
  role: "menu",
253
- "aria-label": "Table actions",
330
+ "aria-label": ariaLabel,
254
331
  className: "fixed z-50 min-w-[14rem] rounded-md border border-border bg-popover py-1 text-xs text-popover-foreground shadow-lg",
255
332
  style: {
256
333
  left: position.x,
@@ -274,7 +351,8 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
274
351
  className: `block w-full px-3 py-1 text-left hover:bg-muted disabled:cursor-default disabled:opacity-40 disabled:hover:bg-transparent ${item.checked ? "font-semibold" : ""}`,
275
352
  onClick: () => {
276
353
  onClose();
277
- (0, import_spreadsheet2.runSpreadsheetMenuItem)(item);
354
+ if (item.panel) onOpenPanel?.(item.panel);
355
+ else (0, import_spreadsheet2.runSpreadsheetMenuItem)(item);
278
356
  },
279
357
  children: item.label
280
358
  }
@@ -285,6 +363,7 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
285
363
  };
286
364
 
287
365
  // src/spreadsheet/SpreadsheetGroupRow.tsx
366
+ var import_react3 = __toESM(require("react"), 1);
288
367
  var import_jsx_runtime2 = require("react/jsx-runtime");
289
368
  var formatSummary = (value) => value === null ? "\u2013" : String(value);
290
369
  var SpreadsheetGroupRow = ({
@@ -292,9 +371,14 @@ var SpreadsheetGroupRow = ({
292
371
  columns,
293
372
  groupColId,
294
373
  onToggle,
295
- trailingCell
374
+ trailingCell,
375
+ labelColumns = columns,
376
+ leadingColumns,
377
+ leftSpace = 0,
378
+ rightSpace = 0
296
379
  }) => {
297
- const labelColId = columns.some((c) => c.column.id === groupColId) ? groupColId : columns[0]?.column.id;
380
+ const labelColId = labelColumns.some((c) => c.column.id === groupColId) ? groupColId : labelColumns[0]?.column.id;
381
+ const spacer = (key) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { "aria-hidden": "true", "data-spacer": "", style: { padding: 0 } }, key);
298
382
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("tr", { className: "border-b border-border bg-muted/40 font-semibold", "data-group-key": group.key, style: { height: 28 }, children: [
299
383
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted border-r border-border text-center", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
300
384
  "button",
@@ -310,24 +394,30 @@ var SpreadsheetGroupRow = ({
310
394
  children: group.collapsed ? "\u25B8" : "\u25BE"
311
395
  }
312
396
  ) }),
313
- columns.map(({ column }) => {
397
+ columns.map(({ column }, i) => {
314
398
  const summary = group.summaries[column.id];
315
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { className: "border-r border-border px-2 py-1 truncate max-w-0 text-foreground", style: { maxWidth: 0, overflow: "hidden" }, children: column.id === labelColId ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "truncate", children: [
316
- group.label,
317
- " ",
318
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-normal text-muted-foreground", children: [
319
- "(",
320
- group.rowIndices.length,
321
- ")"
322
- ] })
323
- ] }) : summary !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "truncate", "data-summary": column.id, children: formatSummary(summary) }) : null }, column.id);
399
+ const before = leadingColumns !== void 0 && i === leadingColumns && leftSpace > 0 ? spacer("space:left") : null;
400
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react3.default.Fragment, { children: [
401
+ before,
402
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { className: "border-r border-border px-2 py-1 truncate max-w-0 text-foreground", style: { maxWidth: 0, overflow: "hidden" }, children: column.id === labelColId ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "truncate", children: [
403
+ group.label,
404
+ " ",
405
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-normal text-muted-foreground", children: [
406
+ "(",
407
+ group.rowIndices.length,
408
+ ")"
409
+ ] })
410
+ ] }) : summary !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "truncate", "data-summary": column.id, children: formatSummary(summary) }) : null })
411
+ ] }, column.id);
324
412
  }),
413
+ leadingColumns !== void 0 && leadingColumns >= columns.length && leftSpace > 0 && spacer("space:left"),
414
+ rightSpace > 0 && spacer("space:right"),
325
415
  trailingCell && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
326
416
  ] });
327
417
  };
328
418
 
329
419
  // src/spreadsheet/useSpreadsheetTableInteractions.ts
330
- var import_react3 = require("react");
420
+ var import_react4 = require("react");
331
421
  var import_spreadsheet3 = require("@pixerate/editor/spreadsheet");
332
422
  function gridHasFocus(grid) {
333
423
  if (!grid || typeof document === "undefined") return false;
@@ -336,12 +426,12 @@ function gridHasFocus(grid) {
336
426
  return !(active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement);
337
427
  }
338
428
  function useSpreadsheetClipboard(gridRef, state, readOnly) {
339
- const stateRef = (0, import_react3.useRef)(state);
429
+ const stateRef = (0, import_react4.useRef)(state);
340
430
  stateRef.current = state;
341
- const readOnlyRef = (0, import_react3.useRef)(readOnly);
431
+ const readOnlyRef = (0, import_react4.useRef)(readOnly);
342
432
  readOnlyRef.current = readOnly;
343
- const copiedRef = (0, import_react3.useRef)(false);
344
- (0, import_react3.useEffect)(() => {
433
+ const copiedRef = (0, import_react4.useRef)(false);
434
+ (0, import_react4.useEffect)(() => {
345
435
  if (typeof document === "undefined") return;
346
436
  const handleCopy = (e) => {
347
437
  const s = stateRef.current;
@@ -364,7 +454,7 @@ function useSpreadsheetClipboard(gridRef, state, readOnly) {
364
454
  document.removeEventListener("paste", handlePaste);
365
455
  };
366
456
  }, [gridRef]);
367
- const copyShortcutFallback = (0, import_react3.useCallback)(() => {
457
+ const copyShortcutFallback = (0, import_react4.useCallback)(() => {
368
458
  copiedRef.current = false;
369
459
  setTimeout(() => {
370
460
  if (copiedRef.current) return;
@@ -376,13 +466,13 @@ function useSpreadsheetClipboard(gridRef, state, readOnly) {
376
466
  return { copyShortcutFallback };
377
467
  }
378
468
  function useFillHandle(state) {
379
- const [drag, setDrag] = (0, import_react3.useState)(null);
380
- const dragRef = (0, import_react3.useRef)(drag);
469
+ const [drag, setDrag] = (0, import_react4.useState)(null);
470
+ const dragRef = (0, import_react4.useRef)(drag);
381
471
  dragRef.current = drag;
382
- const stateRef = (0, import_react3.useRef)(state);
472
+ const stateRef = (0, import_react4.useRef)(state);
383
473
  stateRef.current = state;
384
474
  const dragging = drag !== null;
385
- (0, import_react3.useEffect)(() => {
475
+ (0, import_react4.useEffect)(() => {
386
476
  if (!dragging) return;
387
477
  const handleMouseUp = () => {
388
478
  const current = dragRef.current;
@@ -392,13 +482,13 @@ function useFillHandle(state) {
392
482
  window.addEventListener("mouseup", handleMouseUp);
393
483
  return () => window.removeEventListener("mouseup", handleMouseUp);
394
484
  }, [dragging]);
395
- const startFill = (0, import_react3.useCallback)((e) => {
485
+ const startFill = (0, import_react4.useCallback)((e) => {
396
486
  e.preventDefault();
397
487
  e.stopPropagation();
398
488
  const range = stateRef.current.selectedRange;
399
489
  if (range) setDrag({ source: (0, import_spreadsheet3.normalizeRange)(range), target: null });
400
490
  }, []);
401
- const extendFill = (0, import_react3.useCallback)((row, col) => {
491
+ const extendFill = (0, import_react4.useCallback)((row, col) => {
402
492
  const current = dragRef.current;
403
493
  if (!current) return;
404
494
  setDrag({ source: current.source, target: (0, import_spreadsheet3.computeFillTarget)(current.source, { row, col }) });
@@ -406,35 +496,401 @@ function useFillHandle(state) {
406
496
  return { fillTarget: drag?.target ?? null, isFilling: dragging, startFill, extendFill };
407
497
  }
408
498
 
409
- // src/spreadsheet/FormulaHints.tsx
410
- var import_react4 = require("react");
411
- var import_spreadsheet4 = require("@pixerate/editor/spreadsheet");
499
+ // src/spreadsheet/SpreadsheetValidationDialog.tsx
500
+ var import_react5 = require("react");
412
501
  var import_jsx_runtime3 = require("react/jsx-runtime");
413
- var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
414
- const [caret, setCaret] = (0, import_react4.useState)(null);
415
- const [focused, setFocused] = (0, import_react4.useState)(false);
416
- const [selected, setSelected] = (0, import_react4.useState)(0);
417
- const [dismissed, setDismissed] = (0, import_react4.useState)(false);
418
- const [position, setPosition] = (0, import_react4.useState)(null);
419
- const pendingCaret = (0, import_react4.useRef)(null);
420
- const hints = (0, import_react4.useMemo)(
421
- () => (0, import_spreadsheet4.getFormulaHints)(value, caret ?? value.length, { limit }),
422
- [value, caret, limit]
502
+ var SpreadsheetValidationDialog = ({ prompt, onResolve }) => {
503
+ const style = prompt.result.errorStyle ?? "stop";
504
+ const firstButton = (0, import_react5.useRef)(null);
505
+ (0, import_react5.useEffect)(() => {
506
+ firstButton.current?.focus();
507
+ }, [prompt]);
508
+ const buttons = style === "stop" ? [
509
+ { label: "Retry", action: "retry" },
510
+ { label: "Cancel", action: "cancel" }
511
+ ] : style === "warning" ? [
512
+ { label: "Yes", action: "accept" },
513
+ { label: "No", action: "retry" },
514
+ { label: "Cancel", action: "cancel" }
515
+ ] : [
516
+ { label: "OK", action: "accept" },
517
+ { label: "Cancel", action: "cancel" }
518
+ ];
519
+ const icon = style === "stop" ? "\u26D4" : style === "warning" ? "\u26A0" : "\u2139";
520
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
521
+ "div",
522
+ {
523
+ className: "fixed inset-0 z-[60] flex items-center justify-center bg-black/30",
524
+ onMouseDown: (e) => e.preventDefault(),
525
+ onKeyDown: (e) => {
526
+ e.stopPropagation();
527
+ if (e.key === "Escape") {
528
+ e.preventDefault();
529
+ onResolve("cancel");
530
+ }
531
+ },
532
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
533
+ "div",
534
+ {
535
+ role: "alertdialog",
536
+ "aria-modal": "true",
537
+ "aria-labelledby": "spreadsheet-validation-title",
538
+ "aria-describedby": "spreadsheet-validation-message",
539
+ "data-error-style": style,
540
+ className: "w-[22rem] max-w-[calc(100vw-2rem)] rounded-md border border-border bg-popover p-4 text-xs text-popover-foreground shadow-xl",
541
+ style: { background: "var(--popover, var(--background, #fff))" },
542
+ children: [
543
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-start gap-3", children: [
544
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": "true", className: "text-lg leading-none", children: icon }),
545
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "min-w-0 flex-1", children: [
546
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { id: "spreadsheet-validation-title", className: "mb-1 text-sm font-semibold", children: prompt.result.title }),
547
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { id: "spreadsheet-validation-message", className: "whitespace-pre-wrap", children: prompt.result.message }),
548
+ style === "warning" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mt-2", children: "Continue?" })
549
+ ] })
550
+ ] }),
551
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mt-4 flex justify-end gap-2", children: buttons.map((button, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
552
+ "button",
553
+ {
554
+ ref: i === 0 ? firstButton : void 0,
555
+ type: "button",
556
+ "data-action": button.action,
557
+ className: `rounded border border-border px-3 py-1 ${i === 0 ? "bg-primary text-primary-foreground" : "hover:bg-muted"}`,
558
+ onClick: () => onResolve(button.action),
559
+ children: button.label
560
+ },
561
+ button.action
562
+ )) })
563
+ ]
564
+ }
565
+ )
566
+ }
567
+ );
568
+ };
569
+
570
+ // src/spreadsheet/SpreadsheetRulePanels.tsx
571
+ var import_react6 = require("react");
572
+ var import_spreadsheet4 = require("@pixerate/editor/spreadsheet");
573
+ var import_jsx_runtime4 = require("react/jsx-runtime");
574
+ var inputClass = "w-full rounded border border-border bg-background px-1.5 py-0.5 text-xs text-foreground";
575
+ var buttonClass = "rounded border border-border px-2 py-0.5 hover:bg-muted disabled:opacity-40";
576
+ function selectionText(range) {
577
+ return range ? (0, import_spreadsheet4.formatRuleRanges)([range]) : "A1";
578
+ }
579
+ var PanelFrame = ({
580
+ title,
581
+ label,
582
+ onClose,
583
+ children
584
+ }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
585
+ "div",
586
+ {
587
+ role: "dialog",
588
+ "aria-label": label,
589
+ className: "fixed right-4 top-16 z-40 flex max-h-[calc(100vh-5rem)] w-80 flex-col overflow-y-auto rounded-md border border-border bg-popover p-3 text-xs text-popover-foreground shadow-lg",
590
+ style: { background: "var(--popover, var(--background, #fff))" },
591
+ onKeyDown: (e) => {
592
+ e.stopPropagation();
593
+ if (e.key === "Escape") onClose();
594
+ },
595
+ onContextMenu: (e) => e.stopPropagation(),
596
+ children: [
597
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mb-2 flex items-center justify-between", children: [
598
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "text-sm font-semibold", children: title }),
599
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", "aria-label": "Close", className: "px-1 text-muted-foreground hover:text-foreground", onClick: onClose, children: "\u2715" })
600
+ ] }),
601
+ children
602
+ ]
603
+ }
604
+ );
605
+ function RuleSwatch({ rule }) {
606
+ if (rule.type === "colorScale") {
607
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
608
+ "span",
609
+ {
610
+ "aria-hidden": "true",
611
+ className: "inline-block h-3 w-6 shrink-0 rounded-sm border border-border",
612
+ style: { background: `linear-gradient(90deg, ${rule.stops.map((s) => s.color).join(", ")})` }
613
+ }
614
+ );
615
+ }
616
+ if (rule.type === "dataBar") {
617
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", className: "inline-block h-3 w-6 shrink-0 rounded-sm", style: { background: rule.color ?? "#638ec6" } });
618
+ }
619
+ if (rule.type === "iconSet") return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", className: "shrink-0", children: "\u25CF" });
620
+ const css = (0, import_spreadsheet4.getConditionalFormatCss)(rule.style);
621
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", className: "inline-flex h-4 w-6 shrink-0 items-center justify-center rounded-sm border border-border", style: css, children: "Aa" });
622
+ }
623
+ var SpreadsheetConditionalFormatPanel = ({ state, onClose }) => {
624
+ const rules = state.document.table?.conditionalFormats ?? [];
625
+ const [kind, setKind] = (0, import_react6.useState)("greaterThan");
626
+ const [appliesTo, setAppliesTo] = (0, import_react6.useState)(() => selectionText(state.selectedRange));
627
+ const [value, setValue] = (0, import_react6.useState)("");
628
+ const [value2, setValue2] = (0, import_react6.useState)("");
629
+ const [text, setText] = (0, import_react6.useState)("");
630
+ const [formula, setFormula] = (0, import_react6.useState)("=");
631
+ const [rank, setRank] = (0, import_react6.useState)("10");
632
+ const [period, setPeriod] = (0, import_react6.useState)("today");
633
+ const [iconSet, setIconSet] = (0, import_react6.useState)("3TrafficLights1");
634
+ const [color, setColor] = (0, import_react6.useState)("#638ec6");
635
+ const [styleId, setStyleId] = (0, import_react6.useState)(import_spreadsheet4.CONDITIONAL_FORMAT_STYLE_PRESETS[0].id);
636
+ const preset = (0, import_react6.useMemo)(() => import_spreadsheet4.CONDITIONAL_FORMAT_PRESETS.find((p) => p.kind === kind), [kind]);
637
+ const ranges = (0, import_spreadsheet4.parseRuleRanges)(appliesTo);
638
+ const add = () => {
639
+ if (!ranges) return;
640
+ state.addConditionalFormat(
641
+ (0, import_spreadsheet4.createConditionalFormatRule)({
642
+ kind,
643
+ ranges,
644
+ value,
645
+ value2,
646
+ text,
647
+ formula,
648
+ rank: Number(rank) || 10,
649
+ period,
650
+ iconSet,
651
+ color,
652
+ style: import_spreadsheet4.CONDITIONAL_FORMAT_STYLE_PRESETS.find((s) => s.id === styleId)?.style
653
+ })
654
+ );
655
+ };
656
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(PanelFrame, { title: "Conditional formatting", label: "Conditional formatting rules", onClose, children: [
657
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("ol", { className: "mb-3 flex flex-col gap-1", "aria-label": "Rules in priority order", children: [
658
+ rules.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { className: "text-muted-foreground", children: "No rules on this sheet." }),
659
+ rules.map((rule, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { "data-rule-id": rule.id, className: "flex items-center gap-1.5 rounded border border-border px-1.5 py-1", children: [
660
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RuleSwatch, { rule }),
661
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "min-w-0 flex-1", children: [
662
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "truncate", title: (0, import_spreadsheet4.describeConditionalFormat)(rule), children: (0, import_spreadsheet4.describeConditionalFormat)(rule) }),
663
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "truncate font-mono text-[10px] text-muted-foreground", children: (0, import_spreadsheet4.formatRuleRanges)(rule.ranges) })
664
+ ] }),
665
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-0.5 text-[10px]", title: "Stop if true", children: [
666
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
667
+ "input",
668
+ {
669
+ type: "checkbox",
670
+ "aria-label": "Stop if true",
671
+ checked: !!rule.stopIfTrue,
672
+ onChange: (e) => state.updateConditionalFormat(rule.id, { stopIfTrue: e.target.checked })
673
+ }
674
+ ),
675
+ "Stop"
676
+ ] }),
677
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", "aria-label": "Move up", className: buttonClass, disabled: i === 0, onClick: () => state.moveConditionalFormat(rule.id, { by: -1 }), children: "\u25B2" }),
678
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
679
+ "button",
680
+ {
681
+ type: "button",
682
+ "aria-label": "Move down",
683
+ className: buttonClass,
684
+ disabled: i === rules.length - 1,
685
+ onClick: () => state.moveConditionalFormat(rule.id, { by: 1 }),
686
+ children: "\u25BC"
687
+ }
688
+ ),
689
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", "aria-label": "Delete rule", className: buttonClass, onClick: () => state.deleteConditionalFormat(rule.id), children: "\u2715" })
690
+ ] }, rule.id))
691
+ ] }),
692
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("fieldset", { className: "flex flex-col gap-1.5 border-t border-border pt-2", children: [
693
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("legend", { className: "sr-only", children: "New rule" }),
694
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex flex-col gap-0.5", children: [
695
+ "Format cells",
696
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": "Rule type", className: inputClass, value: kind, onChange: (e) => setKind(e.target.value), children: import_spreadsheet4.CONDITIONAL_FORMAT_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: p.kind, children: p.label }, p.kind)) })
697
+ ] }),
698
+ preset.fields.includes("value") && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { "aria-label": "Value", placeholder: "Value or =formula", className: inputClass, value, onChange: (e) => setValue(e.target.value) }),
699
+ preset.fields.includes("value2") && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { "aria-label": "Second value", placeholder: "and", className: inputClass, value: value2, onChange: (e) => setValue2(e.target.value) }),
700
+ preset.fields.includes("text") && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { "aria-label": "Text", placeholder: "Text", className: inputClass, value: text, onChange: (e) => setText(e.target.value) }),
701
+ preset.fields.includes("formula") && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { "aria-label": "Formula", placeholder: "=$A1>10", className: inputClass, value: formula, onChange: (e) => setFormula(e.target.value) }),
702
+ preset.fields.includes("rank") && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { "aria-label": "Rank", type: "number", min: 1, className: inputClass, value: rank, onChange: (e) => setRank(e.target.value) }),
703
+ preset.fields.includes("period") && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": "Period", className: inputClass, value: period, onChange: (e) => setPeriod(e.target.value), children: import_spreadsheet4.CONDITIONAL_FORMAT_TIME_PERIODS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: p.period, children: p.label }, p.period)) }),
704
+ preset.fields.includes("iconSet") && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": "Icon set", className: inputClass, value: iconSet, onChange: (e) => setIconSet(e.target.value), children: import_spreadsheet4.CONDITIONAL_FORMAT_ICON_SETS.map((set) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: set, children: set }, set)) }),
705
+ preset.fields.includes("color") && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { "aria-label": "Bar colour", type: "color", className: "h-6 w-12", value: color, onChange: (e) => setColor(e.target.value) }),
706
+ preset.usesStyle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": "Style", className: inputClass, value: styleId, onChange: (e) => setStyleId(e.target.value), children: import_spreadsheet4.CONDITIONAL_FORMAT_STYLE_PRESETS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: s.id, children: s.label }, s.id)) }),
707
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex flex-col gap-0.5", children: [
708
+ "Applies to",
709
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { "aria-label": "Applies to", className: `${inputClass} font-mono`, value: appliesTo, onChange: (e) => setAppliesTo(e.target.value) })
710
+ ] }),
711
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex justify-between gap-2", children: [
712
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass, onClick: () => setAppliesTo(selectionText(state.selectedRange)), children: "Use selection" }),
713
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass, disabled: !ranges, onClick: add, children: "Add rule" })
714
+ ] })
715
+ ] })
716
+ ] });
717
+ };
718
+ var SpreadsheetDataValidationPanel = ({ state, onClose }) => {
719
+ const rules = state.document.table?.dataValidations ?? [];
720
+ const [type, setType] = (0, import_react6.useState)("list");
721
+ const [operator, setOperator] = (0, import_react6.useState)("between");
722
+ const [formula1, setFormula1] = (0, import_react6.useState)("");
723
+ const [formula2, setFormula2] = (0, import_react6.useState)("");
724
+ const [errorStyle, setErrorStyle] = (0, import_react6.useState)("stop");
725
+ const [errorMessage, setErrorMessage] = (0, import_react6.useState)("");
726
+ const [inputMessage, setInputMessage] = (0, import_react6.useState)("");
727
+ const [ignoreBlank, setIgnoreBlank] = (0, import_react6.useState)(true);
728
+ const [appliesTo, setAppliesTo] = (0, import_react6.useState)(() => selectionText(state.selectedRange));
729
+ const ranges = (0, import_spreadsheet4.parseRuleRanges)(appliesTo);
730
+ const usesOperator = type === "whole" || type === "decimal" || type === "date" || type === "time" || type === "textLength";
731
+ const needsTwo = usesOperator && (operator === "between" || operator === "notBetween");
732
+ const circles = state.controller.getInvalidDataCircles();
733
+ const apply = () => {
734
+ if (!ranges) return;
735
+ state.addDataValidation({
736
+ ranges,
737
+ type,
738
+ ...usesOperator ? { operator } : {},
739
+ ...formula1 ? { formula1 } : {},
740
+ ...needsTwo && formula2 ? { formula2 } : {},
741
+ errorStyle,
742
+ ...errorMessage ? { errorMessage } : {},
743
+ ...inputMessage ? { inputMessage } : {},
744
+ ignoreBlank
745
+ });
746
+ };
747
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(PanelFrame, { title: "Data validation", label: "Data validation rules", onClose, children: [
748
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("ul", { className: "mb-3 flex flex-col gap-1", "aria-label": "Validation rules", children: [
749
+ rules.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { className: "text-muted-foreground", children: "No validation on this sheet." }),
750
+ rules.map((rule) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { "data-rule-id": rule.id, className: "flex items-center gap-1.5 rounded border border-border px-1.5 py-1", children: [
751
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "min-w-0 flex-1", children: [
752
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "truncate", title: (0, import_spreadsheet4.describeDataValidation)(rule), children: (0, import_spreadsheet4.describeDataValidation)(rule) }),
753
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "truncate font-mono text-[10px] text-muted-foreground", children: [
754
+ (0, import_spreadsheet4.formatRuleRanges)(rule.ranges),
755
+ " \xB7 ",
756
+ rule.errorStyle ?? "stop"
757
+ ] })
758
+ ] }),
759
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", "aria-label": "Delete rule", className: buttonClass, onClick: () => state.deleteDataValidation(rule.id), children: "\u2715" })
760
+ ] }, rule.id))
761
+ ] }),
762
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("fieldset", { className: "flex flex-col gap-1.5 border-t border-border pt-2", children: [
763
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("legend", { className: "sr-only", children: "New validation" }),
764
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex flex-col gap-0.5", children: [
765
+ "Allow",
766
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": "Allow", className: inputClass, value: type, onChange: (e) => setType(e.target.value), children: import_spreadsheet4.DATA_VALIDATION_TYPE_OPTIONS.map((o) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: o.type, children: o.label }, o.type)) })
767
+ ] }),
768
+ usesOperator && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": "Operator", className: inputClass, value: operator, onChange: (e) => setOperator(e.target.value), children: import_spreadsheet4.DATA_VALIDATION_OPERATOR_OPTIONS.map((o) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: o.operator, children: o.label }, o.operator)) }),
769
+ type !== "any" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
770
+ "input",
771
+ {
772
+ "aria-label": type === "list" ? "Source" : type === "custom" ? "Formula" : needsTwo ? "Minimum" : "Value",
773
+ placeholder: type === "list" ? "Low,Medium,High or =$H$1:$H$5" : type === "custom" ? "=A1>0" : "Value or =formula",
774
+ className: inputClass,
775
+ value: formula1,
776
+ onChange: (e) => setFormula1(e.target.value)
777
+ }
778
+ ),
779
+ needsTwo && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { "aria-label": "Maximum", placeholder: "Maximum", className: inputClass, value: formula2, onChange: (e) => setFormula2(e.target.value) }),
780
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex items-center gap-1", children: [
781
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "checkbox", checked: ignoreBlank, onChange: (e) => setIgnoreBlank(e.target.checked) }),
782
+ " Ignore blank"
783
+ ] }),
784
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { "aria-label": "Input message", placeholder: "Input message (optional)", className: inputClass, value: inputMessage, onChange: (e) => setInputMessage(e.target.value) }),
785
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex gap-1", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": "Error style", className: inputClass, value: errorStyle, onChange: (e) => setErrorStyle(e.target.value), children: import_spreadsheet4.DATA_VALIDATION_ERROR_STYLE_OPTIONS.map((o) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: o.style, children: o.label }, o.style)) }) }),
786
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { "aria-label": "Error message", placeholder: "Error message (optional)", className: inputClass, value: errorMessage, onChange: (e) => setErrorMessage(e.target.value) }),
787
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "flex flex-col gap-0.5", children: [
788
+ "Applies to",
789
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { "aria-label": "Applies to", className: `${inputClass} font-mono`, value: appliesTo, onChange: (e) => setAppliesTo(e.target.value) })
790
+ ] }),
791
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-wrap justify-between gap-2", children: [
792
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass, onClick: () => setAppliesTo(selectionText(state.selectedRange)), children: "Use selection" }),
793
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass, disabled: !ranges, onClick: () => ranges && ranges.forEach((r) => state.clearDataValidation(r)), children: "Clear" }),
794
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass, disabled: !ranges, onClick: apply, children: "Apply" })
795
+ ] }),
796
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: buttonClass, "aria-pressed": circles, onClick: () => state.setInvalidDataCircles(!circles), children: circles ? "Clear validation circles" : "Circle invalid data" })
797
+ ] })
798
+ ] });
799
+ };
800
+
801
+ // src/spreadsheet/useSpreadsheetViewport.ts
802
+ var import_react7 = require("react");
803
+ var import_spreadsheet5 = require("@pixerate/editor/spreadsheet");
804
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? import_react7.useLayoutEffect : import_react7.useEffect;
805
+ function useSpreadsheetViewport(scrollRef, view, options = {}) {
806
+ const metrics = (0, import_react7.useMemo)(() => (0, import_spreadsheet5.measureSpreadsheetView)(view), [view]);
807
+ const virtual = (0, import_spreadsheet5.shouldVirtualizeSpreadsheet)(view, options.virtualize ?? "auto");
808
+ const [viewport, setViewport] = (0, import_react7.useState)({ scrollTop: 0, scrollLeft: 0, width: 0, height: 0 });
809
+ const windowOptions = (0, import_react7.useMemo)(
810
+ () => ({ merges: options.merges, overscanRows: options.overscanRows, overscanColumns: options.overscanColumns }),
811
+ [options.merges, options.overscanRows, options.overscanColumns]
812
+ );
813
+ const win = (0, import_react7.useMemo)(
814
+ () => virtual ? (0, import_spreadsheet5.getSpreadsheetViewWindow)(metrics, viewport, windowOptions) : (0, import_spreadsheet5.fullSpreadsheetViewWindow)(metrics),
815
+ [virtual, metrics, viewport, windowOptions]
816
+ );
817
+ const latest = (0, import_react7.useRef)({ metrics, viewport, virtual, windowOptions });
818
+ latest.current = { metrics, viewport, virtual, windowOptions };
819
+ const readViewport = (0, import_react7.useCallback)(() => {
820
+ const el = scrollRef.current;
821
+ if (!el) return null;
822
+ return { scrollTop: el.scrollTop, scrollLeft: el.scrollLeft, width: el.clientWidth, height: el.clientHeight };
823
+ }, [scrollRef]);
824
+ const updateViewport = (0, import_react7.useCallback)((next) => {
825
+ setViewport((prev) => {
826
+ const { metrics: m, virtual: v, windowOptions: o } = latest.current;
827
+ if (!v) return prev;
828
+ const same = prev.width === next.width && prev.height === next.height && (0, import_spreadsheet5.sameSpreadsheetViewWindow)((0, import_spreadsheet5.getSpreadsheetViewWindow)(m, prev, o), (0, import_spreadsheet5.getSpreadsheetViewWindow)(m, next, o));
829
+ return same ? prev : next;
830
+ });
831
+ }, []);
832
+ const onScroll = (0, import_react7.useCallback)(() => {
833
+ const next = readViewport();
834
+ if (next) updateViewport(next);
835
+ }, [readViewport, updateViewport]);
836
+ useIsomorphicLayoutEffect(() => {
837
+ if (!virtual) return;
838
+ onScroll();
839
+ const el = scrollRef.current;
840
+ if (!el || typeof ResizeObserver === "undefined") return;
841
+ const observer = new ResizeObserver(() => onScroll());
842
+ observer.observe(el);
843
+ return () => observer.disconnect();
844
+ }, [virtual, onScroll, scrollRef]);
845
+ const activeRow = options.activeCell?.row;
846
+ const activeCol = options.activeCell?.col;
847
+ useIsomorphicLayoutEffect(() => {
848
+ if (!virtual || activeRow === void 0 || activeCol === void 0) return;
849
+ const el = scrollRef.current;
850
+ const current = latest.current.viewport;
851
+ const measured = readViewport();
852
+ const base = measured ? { ...measured, scrollTop: Math.max(measured.scrollTop, 0), scrollLeft: Math.max(measured.scrollLeft, 0) } : current;
853
+ const target = (0, import_spreadsheet5.getSpreadsheetScrollToCell)(latest.current.metrics, base, { row: activeRow, col: activeCol });
854
+ if (!target) return;
855
+ const next = { ...base, ...target };
856
+ if (el) {
857
+ if (target.scrollTop !== void 0) el.scrollTop = target.scrollTop;
858
+ if (target.scrollLeft !== void 0) el.scrollLeft = target.scrollLeft;
859
+ }
860
+ updateViewport(next);
861
+ }, [virtual, activeRow, activeCol]);
862
+ return { metrics, window: win, onScroll };
863
+ }
864
+
865
+ // src/spreadsheet/FormulaHints.tsx
866
+ var import_react8 = require("react");
867
+ var import_spreadsheet6 = require("@pixerate/editor/spreadsheet");
868
+ var import_jsx_runtime5 = require("react/jsx-runtime");
869
+ var FormulaHints = ({ inputRef, value, onAccept, limit = 8, names }) => {
870
+ const [caret, setCaret] = (0, import_react8.useState)(null);
871
+ const [focused, setFocused] = (0, import_react8.useState)(false);
872
+ const [selected, setSelected] = (0, import_react8.useState)(0);
873
+ const [dismissed, setDismissed] = (0, import_react8.useState)(false);
874
+ const [position, setPosition] = (0, import_react8.useState)(null);
875
+ const pendingCaret = (0, import_react8.useRef)(null);
876
+ const hints = (0, import_react8.useMemo)(
877
+ () => (0, import_spreadsheet6.getFormulaHints)(value, caret ?? value.length, { limit, names }),
878
+ [value, caret, limit, names]
423
879
  );
424
880
  const suggestions = dismissed ? [] : hints.suggestions;
425
881
  const selectedIndex = Math.max(0, Math.min(selected, suggestions.length - 1));
426
882
  const visible = focused && value.trimStart().startsWith("=") && (suggestions.length > 0 || !!hints.activeCall);
427
- const latest = (0, import_react4.useRef)({ suggestions, selected: selectedIndex, value, caret, onAccept });
883
+ const latest = (0, import_react8.useRef)({ suggestions, selected: selectedIndex, value, caret, onAccept });
428
884
  latest.current = { suggestions, selected: selectedIndex, value, caret, onAccept };
429
885
  const accept = (index) => {
430
886
  const { suggestions: list, value: current, caret: at, onAccept: commit } = latest.current;
431
887
  const suggestion = list[index];
432
888
  if (!suggestion) return;
433
- const next = (0, import_spreadsheet4.applyFormulaSuggestion)(current, at ?? current.length, suggestion.name);
889
+ const next = (0, import_spreadsheet6.applyFormulaSuggestion)(current, at ?? current.length, suggestion);
434
890
  pendingCaret.current = next.caret;
435
891
  commit(next.value);
436
892
  };
437
- (0, import_react4.useEffect)(() => {
893
+ (0, import_react8.useEffect)(() => {
438
894
  const input = inputRef.current;
439
895
  if (!input) return;
440
896
  const syncCaret = () => setCaret(input.selectionStart ?? input.value.length);
@@ -484,7 +940,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
484
940
  input.removeEventListener("keydown", onKeyDown);
485
941
  };
486
942
  }, [inputRef]);
487
- (0, import_react4.useLayoutEffect)(() => {
943
+ (0, import_react8.useLayoutEffect)(() => {
488
944
  const input = inputRef.current;
489
945
  const at = pendingCaret.current;
490
946
  if (!input || at === null || input.value !== value) return;
@@ -493,7 +949,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
493
949
  setCaret(at);
494
950
  setSelected(0);
495
951
  }, [value, inputRef]);
496
- (0, import_react4.useLayoutEffect)(() => {
952
+ (0, import_react8.useLayoutEffect)(() => {
497
953
  const input = inputRef.current;
498
954
  if (!visible || !input) return;
499
955
  const rect = input.getBoundingClientRect();
@@ -501,7 +957,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
501
957
  }, [visible, value, inputRef]);
502
958
  if (!visible) return null;
503
959
  const { activeCall } = hints;
504
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
960
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
505
961
  "div",
506
962
  {
507
963
  "data-formula-hints": "",
@@ -517,8 +973,8 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
517
973
  },
518
974
  onMouseDown: (e) => e.preventDefault(),
519
975
  children: [
520
- activeCall && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { "data-formula-signature": "", className: "px-2 py-1.5 border-b border-border bg-muted/30", children: [
521
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "font-mono", children: activeCall.parts.map((part, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
976
+ activeCall && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { "data-formula-signature": "", className: "px-2 py-1.5 border-b border-border bg-muted/30", children: [
977
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "font-mono", children: activeCall.parts.map((part, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
522
978
  "span",
523
979
  {
524
980
  "data-active": part.active ? "" : void 0,
@@ -527,13 +983,13 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
527
983
  },
528
984
  i
529
985
  )) }),
530
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mt-0.5 text-muted-foreground", children: activeCall.argument ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
531
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "font-semibold", children: activeCall.argument.name }),
986
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mt-0.5 text-muted-foreground", children: activeCall.argument ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
987
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-semibold", children: activeCall.argument.name }),
532
988
  ": ",
533
989
  activeCall.argument.description
534
990
  ] }) : activeCall.description })
535
991
  ] }),
536
- suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { role: "listbox", "aria-label": "Formula suggestions", className: "max-h-64 overflow-auto py-1 m-0 list-none p-0", children: suggestions.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
992
+ suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { role: "listbox", "aria-label": "Formula suggestions", className: "max-h-64 overflow-auto py-1 m-0 list-none p-0", children: suggestions.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
537
993
  "li",
538
994
  {
539
995
  role: "option",
@@ -545,19 +1001,162 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
545
1001
  accept(i);
546
1002
  },
547
1003
  children: [
548
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "font-mono font-semibold text-foreground", children: s.signature }),
549
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-muted-foreground truncate", children: s.description })
1004
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "font-mono font-semibold text-foreground", children: s.signature }),
1005
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-muted-foreground truncate", children: s.description })
550
1006
  ]
551
1007
  },
552
- s.name
1008
+ `${s.kind ?? "fn"}:${s.name}`
553
1009
  )) })
554
1010
  ]
555
1011
  }
556
1012
  );
557
1013
  };
558
1014
 
1015
+ // src/spreadsheet/SheetTabs.tsx
1016
+ var import_react9 = require("react");
1017
+ var import_spreadsheet7 = require("@pixerate/editor/spreadsheet");
1018
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1019
+ var SheetTabs = ({ state, readOnly = false, className = "" }) => {
1020
+ const { controller, sheets } = state;
1021
+ const [renaming, setRenaming] = (0, import_react9.useState)(null);
1022
+ const [error, setError] = (0, import_react9.useState)(null);
1023
+ const [menu, setMenu] = (0, import_react9.useState)(null);
1024
+ const dragFrom = (0, import_react9.useRef)(null);
1025
+ const [dropTarget, setDropTarget] = (0, import_react9.useState)(null);
1026
+ const commitRename = (cancelOnError) => {
1027
+ if (!renaming) return;
1028
+ const sheet = sheets.find((s) => s.id === renaming.id);
1029
+ const name = renaming.draft.trim();
1030
+ if (!sheet || !name || name === sheet.name) {
1031
+ setRenaming(null);
1032
+ setError(null);
1033
+ return;
1034
+ }
1035
+ try {
1036
+ state.renameSheet(renaming.id, name);
1037
+ setRenaming(null);
1038
+ setError(null);
1039
+ } catch (err) {
1040
+ setError(err?.message ?? String(err));
1041
+ if (cancelOnError) setRenaming(null);
1042
+ }
1043
+ };
1044
+ const dropAfter = (e) => {
1045
+ const rect = e.currentTarget.getBoundingClientRect();
1046
+ return e.clientX > rect.left + rect.width / 2;
1047
+ };
1048
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `flex flex-col ${className}`, "data-sheet-tabs": "", children: [
1049
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1050
+ "div",
1051
+ {
1052
+ role: "tablist",
1053
+ "aria-label": "Sheets",
1054
+ className: "flex items-center gap-0.5 overflow-x-auto border-t border-border bg-muted px-1 py-1 text-xs",
1055
+ children: [
1056
+ sheets.map(
1057
+ (sheet, index) => renaming?.id === sheet.id ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1058
+ "input",
1059
+ {
1060
+ "aria-label": "Sheet name",
1061
+ autoFocus: true,
1062
+ className: "w-32 rounded border border-primary bg-background px-2 py-0.5 text-xs outline-none",
1063
+ value: renaming.draft,
1064
+ onChange: (e) => setRenaming({ id: sheet.id, draft: e.target.value }),
1065
+ onFocus: (e) => e.currentTarget.select(),
1066
+ onKeyDown: (e) => {
1067
+ e.stopPropagation();
1068
+ if (e.key === "Enter") commitRename(false);
1069
+ else if (e.key === "Escape") {
1070
+ setRenaming(null);
1071
+ setError(null);
1072
+ }
1073
+ },
1074
+ onBlur: () => commitRename(true)
1075
+ },
1076
+ sheet.id
1077
+ ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1078
+ "button",
1079
+ {
1080
+ type: "button",
1081
+ role: "tab",
1082
+ "aria-selected": sheet.active,
1083
+ "data-sheet-id": sheet.id,
1084
+ draggable: !readOnly,
1085
+ title: readOnly ? sheet.name : `${sheet.name} \u2014 double-click to rename, drag to move`,
1086
+ className: `whitespace-nowrap rounded px-3 py-0.5 ${sheet.active ? "bg-background font-semibold text-foreground shadow-sm" : "text-muted-foreground hover:bg-background/60"} ${dropTarget?.index === index ? dropTarget.after ? "border-r-2 border-primary" : "border-l-2 border-primary" : ""}`,
1087
+ onClick: () => state.setActiveSheet(sheet.id),
1088
+ onDoubleClick: () => {
1089
+ if (readOnly) return;
1090
+ setError(null);
1091
+ setRenaming({ id: sheet.id, draft: sheet.name });
1092
+ },
1093
+ onContextMenu: (e) => {
1094
+ if (readOnly) return;
1095
+ e.preventDefault();
1096
+ setMenu({ x: e.clientX, y: e.clientY, items: (0, import_spreadsheet7.getSheetTabMenuItems)(controller, sheet.id, { onError: setError }) });
1097
+ },
1098
+ onDragStart: (e) => {
1099
+ dragFrom.current = index;
1100
+ e.dataTransfer?.setData("text/plain", sheet.id);
1101
+ if (e.dataTransfer) e.dataTransfer.effectAllowed = "move";
1102
+ },
1103
+ onDragOver: (e) => {
1104
+ if (dragFrom.current === null) return;
1105
+ e.preventDefault();
1106
+ const after = dropAfter(e);
1107
+ setDropTarget((prev) => prev?.index === index && prev.after === after ? prev : { index, after });
1108
+ },
1109
+ onDragLeave: () => setDropTarget(null),
1110
+ onDrop: (e) => {
1111
+ e.preventDefault();
1112
+ const from = dragFrom.current;
1113
+ dragFrom.current = null;
1114
+ setDropTarget(null);
1115
+ if (from === null) return;
1116
+ state.moveSheet(sheets[from].id, (0, import_spreadsheet7.sheetDropIndex)(from, index, dropAfter(e)));
1117
+ },
1118
+ onDragEnd: () => {
1119
+ dragFrom.current = null;
1120
+ setDropTarget(null);
1121
+ },
1122
+ children: sheet.name
1123
+ },
1124
+ sheet.id
1125
+ )
1126
+ ),
1127
+ !readOnly && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1128
+ "button",
1129
+ {
1130
+ type: "button",
1131
+ "aria-label": "Add sheet",
1132
+ title: "Add sheet",
1133
+ className: "rounded px-2 py-0.5 text-muted-foreground hover:bg-background/60",
1134
+ onClick: () => {
1135
+ setError(null);
1136
+ state.addSheet();
1137
+ },
1138
+ children: "+"
1139
+ }
1140
+ )
1141
+ ]
1142
+ }
1143
+ ),
1144
+ error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { role: "alert", className: "px-2 py-1 text-xs text-destructive", children: error }),
1145
+ menu && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1146
+ SpreadsheetContextMenu,
1147
+ {
1148
+ items: menu.items,
1149
+ x: menu.x,
1150
+ y: menu.y,
1151
+ ariaLabel: "Sheet actions",
1152
+ onClose: () => setMenu(null)
1153
+ }
1154
+ )
1155
+ ] });
1156
+ };
1157
+
559
1158
  // src/spreadsheet/SpreadsheetEditor.tsx
560
- var import_jsx_runtime4 = require("react/jsx-runtime");
1159
+ var import_jsx_runtime7 = require("react/jsx-runtime");
561
1160
  var COLUMN_HEADER_HEIGHT = 28;
562
1161
  var ROW_HEADER_WIDTH = 48;
563
1162
  function frozenCellStyle(viewRow, viewCol) {
@@ -570,7 +1169,7 @@ function frozenCellStyle(viewRow, viewCol) {
570
1169
  background: "var(--background, #fff)"
571
1170
  };
572
1171
  }
573
- var coordKey = (cell) => cell ? `${(0, import_spreadsheet5.indexToColName)(cell.col)}${cell.row + 1}` : "";
1172
+ var coordKey = (cell) => cell ? `${(0, import_spreadsheet8.indexToColName)(cell.col)}${cell.row + 1}` : "";
574
1173
  function spillBorderStyle(range, row, col) {
575
1174
  const line = "1px solid color-mix(in srgb, var(--primary, #6366f1) 60%, transparent)";
576
1175
  return {
@@ -584,18 +1183,34 @@ function spillBorderStyle(range, row, col) {
584
1183
  borderRight: col === range.endCol ? line : void 0
585
1184
  };
586
1185
  }
587
- function CellText({ cell }) {
588
- const shown = (0, import_spreadsheet5.formatCellForDisplay)(cell);
589
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1186
+ function CellText({ cell, conditional }) {
1187
+ const shown = (0, import_spreadsheet8.formatCellForDisplay)(cell);
1188
+ const color = conditional?.color ?? (shown.color ? (0, import_spreadsheet8.numberFormatColorToCss)(shown.color) : void 0);
1189
+ const decorated = !!conditional?.dataBar || !!conditional?.icon;
1190
+ const text = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
590
1191
  "span",
591
1192
  {
592
1193
  className: `truncate block max-w-full ${cell.error ? "text-destructive font-semibold" : ""}`,
593
1194
  title: cell.error ? cell.errorMessage ?? cell.error : void 0,
594
1195
  "data-numeric": shown.numeric ? "" : void 0,
595
- style: shown.numeric || shown.color ? { ...shown.numeric ? { textAlign: "right" } : {}, ...shown.color ? { color: (0, import_spreadsheet5.numberFormatColorToCss)(shown.color) } : {} } : void 0,
596
- children: shown.text
1196
+ style: shown.numeric || color || decorated ? {
1197
+ ...decorated ? { position: "relative" } : {},
1198
+ ...conditional?.icon ? { flex: 1, minWidth: 0 } : {},
1199
+ ...shown.numeric ? { textAlign: "right" } : {},
1200
+ ...color ? { color } : {}
1201
+ } : void 0,
1202
+ children: conditional?.hideValue ? "" : shown.text
597
1203
  }
598
1204
  );
1205
+ if (!conditional || !decorated) return text;
1206
+ const icon = conditional.icon ? (0, import_spreadsheet8.getConditionalFormatIcon)(conditional.icon.set, conditional.icon.index) : null;
1207
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1208
+ conditional.dataBar && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "aria-hidden": "true", className: "spreadsheet-data-bar", style: (0, import_spreadsheet8.getDataBarCss)(conditional.dataBar) }),
1209
+ icon ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "flex items-center gap-1 min-w-0", style: { position: "relative" }, children: [
1210
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "spreadsheet-cf-icon shrink-0", role: "img", "aria-label": icon.label, "data-icon": conditional.icon.id, style: { color: icon.color }, children: icon.glyph }),
1211
+ text
1212
+ ] }) : text
1213
+ ] });
599
1214
  }
600
1215
  var SpreadsheetEditor = ({
601
1216
  state: externalState,
@@ -603,57 +1218,95 @@ var SpreadsheetEditor = ({
603
1218
  customCellRenderer,
604
1219
  onAddColumnClick,
605
1220
  onColumnHeaderClick,
606
- onColumnResize
1221
+ onColumnResize,
1222
+ showSheetTabs = false,
1223
+ virtualize = "auto"
607
1224
  }) => {
608
1225
  const defaultState = useSpreadsheetEditor();
609
1226
  const state = externalState || defaultState;
610
- const gridRef = (0, import_react5.useRef)(null);
611
- const formulaBarRef = (0, import_react5.useRef)(null);
612
- const cellInputRef = (0, import_react5.useRef)(null);
613
- const [resizingColId, setResizingColId] = (0, import_react5.useState)(null);
614
- const [hoveredColId, setHoveredColId] = (0, import_react5.useState)(null);
615
- const [resizeStartX, setResizeStartX] = (0, import_react5.useState)(0);
616
- const [resizeStartWidth, setResizeStartWidth] = (0, import_react5.useState)(0);
1227
+ const gridRef = (0, import_react10.useRef)(null);
1228
+ const scrollRef = (0, import_react10.useRef)(null);
1229
+ const formulaBarRef = (0, import_react10.useRef)(null);
1230
+ const cellInputRef = (0, import_react10.useRef)(null);
1231
+ const [resizingColId, setResizingColId] = (0, import_react10.useState)(null);
1232
+ const [hoveredColId, setHoveredColId] = (0, import_react10.useState)(null);
1233
+ const [resizeStartX, setResizeStartX] = (0, import_react10.useState)(0);
1234
+ const [resizeStartWidth, setResizeStartWidth] = (0, import_react10.useState)(0);
617
1235
  const view = state.viewModel;
1236
+ const { window: viewWindow, onScroll: handleGridScroll } = useSpreadsheetViewport(scrollRef, view, {
1237
+ virtualize,
1238
+ merges: state.document.table?.merges,
1239
+ activeCell: state.activeCell
1240
+ });
1241
+ const columnSlots = (0, import_react10.useMemo)(() => {
1242
+ const col = (viewCol) => ({ kind: "col", viewCol });
1243
+ if (!viewWindow.virtual) return view.columns.map(col);
1244
+ const slots = view.columns.slice(0, viewWindow.frozenColumns).map(col);
1245
+ if (viewWindow.leftSpace > 0) slots.push({ kind: "space", key: "space:left", width: viewWindow.leftSpace });
1246
+ for (let i = viewWindow.colStart; i < viewWindow.colEnd; i++) slots.push(col(view.columns[i]));
1247
+ if (viewWindow.rightSpace > 0) slots.push({ kind: "space", key: "space:right", width: viewWindow.rightSpace });
1248
+ return slots;
1249
+ }, [view, viewWindow]);
1250
+ const renderedColumns = (0, import_react10.useMemo)(
1251
+ () => columnSlots.flatMap((slot) => slot.kind === "col" ? [slot.viewCol] : []),
1252
+ [columnSlots]
1253
+ );
1254
+ const rowSlots = (0, import_react10.useMemo)(() => {
1255
+ const row = (viewRow) => ({ kind: "row", viewRow });
1256
+ if (!viewWindow.virtual) return view.rows.map(row);
1257
+ const slots = view.rows.slice(0, viewWindow.frozenRows).map(row);
1258
+ if (viewWindow.topSpace > 0) slots.push({ kind: "space", key: "space:top", height: viewWindow.topSpace });
1259
+ for (let i = viewWindow.rowStart; i < viewWindow.rowEnd; i++) slots.push(row(view.rows[i]));
1260
+ if (viewWindow.bottomSpace > 0) slots.push({ kind: "space", key: "space:bottom", height: viewWindow.bottomSpace });
1261
+ return slots;
1262
+ }, [view, viewWindow]);
618
1263
  const { copyShortcutFallback } = useSpreadsheetClipboard(gridRef, state, readOnly);
619
1264
  const { fillTarget, startFill, extendFill } = useFillHandle(state);
620
- const [menu, setMenu] = (0, import_react5.useState)(null);
1265
+ const [menu, setMenu] = (0, import_react10.useState)(null);
1266
+ const [panel, setPanel] = (0, import_react10.useState)(null);
1267
+ const [listMenu, setListMenu] = (0, import_react10.useState)(null);
1268
+ const overlay = state.conditionalFormatOverlay;
621
1269
  const filter = state.document.table?.filter;
622
- const filteredColIds = (0, import_react5.useMemo)(
1270
+ const filteredColIds = (0, import_react10.useMemo)(
623
1271
  () => new Set(filter?.enabled ? filter.rules.map((r) => r.colId) : []),
624
1272
  [filter]
625
1273
  );
626
- const handleCellContextMenu = (0, import_react5.useCallback)((rIdx, cIdx, e) => {
1274
+ const handleCellContextMenu = (0, import_react10.useCallback)((rIdx, cIdx, e) => {
627
1275
  if (readOnly) return;
628
1276
  e.preventDefault();
629
1277
  if (state.editingCell) state.commitEditing();
630
- if (!state.selectedRange || !(0, import_spreadsheet5.rangeContains)(state.selectedRange, rIdx, cIdx)) {
1278
+ if (!state.selectedRange || !(0, import_spreadsheet8.rangeContains)(state.selectedRange, rIdx, cIdx)) {
631
1279
  state.selectCell(rIdx, cIdx);
632
1280
  }
633
- setMenu({ x: e.clientX, y: e.clientY, items: (0, import_spreadsheet5.getSpreadsheetMenuItems)(state.controller, { row: rIdx, col: cIdx }) });
1281
+ setMenu({ x: e.clientX, y: e.clientY, items: (0, import_spreadsheet8.getSpreadsheetMenuItems)(state.controller, { row: rIdx, col: cIdx }) });
634
1282
  }, [readOnly, state]);
635
- const activeRawValue = (0, import_react5.useMemo)(() => {
1283
+ const nameSuggestions = (0, import_react10.useMemo)(
1284
+ () => state.controller.getNameSuggestions(),
1285
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1286
+ [state.controller, state.document]
1287
+ );
1288
+ const activeRawValue = (0, import_react10.useMemo)(() => {
636
1289
  if (!state.activeCell) return "";
637
1290
  const row = state.document.rows[state.activeCell.row];
638
1291
  const col = state.document.columns[state.activeCell.col];
639
1292
  if (!row || !col) return "";
640
1293
  return state.getCell(row.id, col.id).raw;
641
1294
  }, [state.activeCell, state.document]);
642
- const activeSpill = (0, import_react5.useMemo)(
1295
+ const activeSpill = (0, import_react10.useMemo)(
643
1296
  () => state.activeCell ? state.controller.getSpillAt(state.activeCell.row, state.activeCell.col) : null,
644
1297
  // eslint-disable-next-line react-hooks/exhaustive-deps
645
1298
  [state.controller, state.activeCell, state.document]
646
1299
  );
647
1300
  const spillFormula = activeSpill && !activeRawValue && activeSpill.key !== coordKey(state.activeCell) ? activeSpill.formula : "";
648
- const coordLabel = (0, import_react5.useMemo)(() => {
1301
+ const coordLabel = (0, import_react10.useMemo)(() => {
649
1302
  if (!state.activeCell) return "--";
650
- const colName = (0, import_spreadsheet5.indexToColName)(state.activeCell.col);
1303
+ const colName = (0, import_spreadsheet8.indexToColName)(state.activeCell.col);
651
1304
  return `${colName}${state.activeCell.row + 1}`;
652
1305
  }, [state.activeCell]);
653
- const [formulaDraft, setFormulaDraft] = (0, import_react5.useState)(null);
1306
+ const [formulaDraft, setFormulaDraft] = (0, import_react10.useState)(null);
654
1307
  const activeColumn = state.activeCell ? state.document.columns[state.activeCell.col] : void 0;
655
1308
  const formulaBarReadOnly = readOnly || !state.activeCell || !!activeColumn?.readOnly;
656
- (0, import_react5.useEffect)(() => {
1309
+ (0, import_react10.useEffect)(() => {
657
1310
  setFormulaDraft(null);
658
1311
  }, [state.activeCell?.row, state.activeCell?.col]);
659
1312
  const commitFormulaBar = () => {
@@ -665,34 +1318,34 @@ var SpreadsheetEditor = ({
665
1318
  const row = state.document.rows[state.activeCell.row];
666
1319
  const col = state.document.columns[state.activeCell.col];
667
1320
  if (row && col && !formulaBarReadOnly && formulaDraft !== activeRawValue) {
668
- state.setCellValue(row.id, col.id, formulaDraft);
1321
+ state.commitCellInput(row.id, col.id, formulaDraft);
669
1322
  }
670
1323
  setFormulaDraft(null);
671
1324
  };
672
- const handleCellClick = (0, import_react5.useCallback)((rIdx, cIdx, e) => {
1325
+ const handleCellClick = (0, import_react10.useCallback)((rIdx, cIdx, e) => {
673
1326
  if (state.editingCell && (state.editingCell.row !== rIdx || state.editingCell.col !== cIdx)) {
674
1327
  state.commitEditing();
675
1328
  }
676
1329
  state.selectCell(rIdx, cIdx, e.shiftKey);
677
1330
  }, [state]);
678
- const handleCellDblClick = (0, import_react5.useCallback)((rIdx, cIdx) => {
1331
+ const handleCellDblClick = (0, import_react10.useCallback)((rIdx, cIdx) => {
679
1332
  if (readOnly) return;
680
1333
  const col = state.document.columns[cIdx];
681
1334
  if (col?.readOnly) return;
682
1335
  state.startEditing(rIdx, cIdx);
683
1336
  }, [readOnly, state]);
684
- const handleKeyDown = (0, import_react5.useCallback)((e) => {
1337
+ const handleKeyDown = (0, import_react10.useCallback)((e) => {
685
1338
  if (state.editingCell) {
686
1339
  if (e.key === "Enter") {
687
1340
  e.preventDefault();
688
- state.commitEditing();
1341
+ if (state.commitEditing().status !== "committed") return;
689
1342
  state.moveCursor("down");
690
1343
  } else if (e.key === "Escape") {
691
1344
  e.preventDefault();
692
1345
  state.cancelEditing();
693
1346
  } else if (e.key === "Tab") {
694
1347
  e.preventDefault();
695
- state.commitEditing();
1348
+ if (state.commitEditing().status !== "committed") return;
696
1349
  state.moveCursor(e.shiftKey ? "left" : "right");
697
1350
  } else {
698
1351
  return;
@@ -701,7 +1354,10 @@ var SpreadsheetEditor = ({
701
1354
  return;
702
1355
  }
703
1356
  if (!state.activeCell) return;
704
- if (e.key === "ArrowUp") {
1357
+ if (e.altKey && e.key === "ArrowDown") {
1358
+ e.preventDefault();
1359
+ openListMenu(state.activeCell.row, state.activeCell.col);
1360
+ } else if (e.key === "ArrowUp") {
705
1361
  e.preventDefault();
706
1362
  state.moveCursor("up", e.shiftKey);
707
1363
  } else if (e.key === "ArrowDown") {
@@ -750,11 +1406,53 @@ var SpreadsheetEditor = ({
750
1406
  }
751
1407
  }
752
1408
  }, [readOnly, state, copyShortcutFallback]);
753
- const stateRef = (0, import_react5.useRef)(state);
1409
+ const activeListValues = (0, import_react10.useMemo)(
1410
+ () => state.activeCell && !readOnly ? state.controller.getValidationListValues(state.activeCell.row, state.activeCell.col) : null,
1411
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1412
+ [state.controller, state.activeCell, state.document, readOnly]
1413
+ );
1414
+ const activeRule = (0, import_react10.useMemo)(
1415
+ () => state.activeCell ? state.controller.getDataValidationAt(state.activeCell.row, state.activeCell.col) : void 0,
1416
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1417
+ [state.controller, state.activeCell, state.document]
1418
+ );
1419
+ const inputMessage = activeRule && activeRule.showInputMessage !== false && (activeRule.inputTitle || activeRule.inputMessage) ? activeRule : null;
1420
+ function openListMenu(row, col, anchor) {
1421
+ if (readOnly || state.document.columns[col]?.readOnly) return;
1422
+ const values = state.controller.getValidationListValues(row, col);
1423
+ if (!values) return;
1424
+ const td = anchor ?? gridRef.current?.querySelector(`td[data-row="${row}"][data-col="${col}"]`);
1425
+ const rect = td?.getBoundingClientRect();
1426
+ setListMenu({ x: rect ? rect.left : 0, y: rect ? rect.bottom : 0, row, col, values });
1427
+ }
1428
+ (0, import_react10.useEffect)(() => {
1429
+ if (!listMenu) return;
1430
+ const close = (e) => {
1431
+ if (!e.target?.closest?.('[aria-label="Allowed values"], .spreadsheet-list-arrow')) setListMenu(null);
1432
+ };
1433
+ window.addEventListener("mousedown", close);
1434
+ return () => window.removeEventListener("mousedown", close);
1435
+ }, [listMenu]);
1436
+ const chooseListValue = (value) => {
1437
+ if (!listMenu) return;
1438
+ const row = state.document.rows[listMenu.row];
1439
+ const col = state.document.columns[listMenu.col];
1440
+ setListMenu(null);
1441
+ if (row && col) state.commitCellInput(row.id, col.id, value);
1442
+ gridRef.current?.focus();
1443
+ };
1444
+ const resolveValidation = (action) => {
1445
+ state.resolveValidationPrompt(action);
1446
+ requestAnimationFrame(() => {
1447
+ if (cellInputRef.current) cellInputRef.current.focus();
1448
+ else gridRef.current?.focus();
1449
+ });
1450
+ };
1451
+ const stateRef = (0, import_react10.useRef)(state);
754
1452
  stateRef.current = state;
755
- const onColumnResizeRef = (0, import_react5.useRef)(onColumnResize);
1453
+ const onColumnResizeRef = (0, import_react10.useRef)(onColumnResize);
756
1454
  onColumnResizeRef.current = onColumnResize;
757
- (0, import_react5.useEffect)(() => {
1455
+ (0, import_react10.useEffect)(() => {
758
1456
  if (!resizingColId) return;
759
1457
  if (typeof document !== "undefined") {
760
1458
  document.body.style.cursor = "col-resize";
@@ -805,7 +1503,7 @@ var SpreadsheetEditor = ({
805
1503
  window.removeEventListener("mouseup", handleMouseUp);
806
1504
  };
807
1505
  }, [resizingColId, resizeStartX, resizeStartWidth]);
808
- const measureElementContentWidth = (0, import_react5.useCallback)((el, container) => {
1506
+ const measureElementContentWidth = (0, import_react10.useCallback)((el, container) => {
809
1507
  if (typeof document === "undefined") return 0;
810
1508
  let maxW = el.scrollWidth || 0;
811
1509
  const firstChild = el.firstElementChild;
@@ -853,7 +1551,7 @@ var SpreadsheetEditor = ({
853
1551
  }
854
1552
  return maxW;
855
1553
  }, []);
856
- const autoFitColumn = (0, import_react5.useCallback)((colId, colIndex) => {
1554
+ const autoFitColumn = (0, import_react10.useCallback)((colId, colIndex) => {
857
1555
  const col = state.document.columns.find((c) => c.id === colId);
858
1556
  if (!col) return;
859
1557
  let calculatedWidth = 0;
@@ -889,12 +1587,12 @@ var SpreadsheetEditor = ({
889
1587
  onColumnResize(updatedCol.id, updatedCol.width || 130);
890
1588
  }
891
1589
  }, [state, measureElementContentWidth, onColumnResize]);
892
- const handleResizeDblClick = (0, import_react5.useCallback)((colId, colIndex, e) => {
1590
+ const handleResizeDblClick = (0, import_react10.useCallback)((colId, colIndex, e) => {
893
1591
  e.preventDefault();
894
1592
  e.stopPropagation();
895
1593
  autoFitColumn(colId, colIndex);
896
1594
  }, [autoFitColumn]);
897
- const handleHeaderDblClick = (0, import_react5.useCallback)((colId, colIndex, e) => {
1595
+ const handleHeaderDblClick = (0, import_react10.useCallback)((colId, colIndex, e) => {
898
1596
  const target = e.currentTarget;
899
1597
  if (target) {
900
1598
  const rect = target.getBoundingClientRect();
@@ -916,7 +1614,7 @@ var SpreadsheetEditor = ({
916
1614
  const isActiveCell = (rIdx, cIdx) => {
917
1615
  return state.activeCell?.row === rIdx && state.activeCell?.col === cIdx;
918
1616
  };
919
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1617
+ const grid = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
920
1618
  "div",
921
1619
  {
922
1620
  ref: gridRef,
@@ -926,10 +1624,10 @@ var SpreadsheetEditor = ({
926
1624
  role: "grid",
927
1625
  "aria-label": "Spreadsheet",
928
1626
  children: [
929
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2 border-b border-border bg-muted/20 px-3 py-1.5 text-xs font-mono", children: [
930
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex h-6 min-w-[3rem] items-center justify-center rounded border border-border bg-background px-1.5 font-semibold text-foreground", children: coordLabel }),
931
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex items-center text-muted-foreground select-none", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "italic font-serif font-bold text-sm text-primary", children: "fx" }) }),
932
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1627
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2 border-b border-border bg-muted/20 px-3 py-1.5 text-xs font-mono", children: [
1628
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex h-6 min-w-[3rem] items-center justify-center rounded border border-border bg-background px-1.5 font-semibold text-foreground", children: coordLabel }),
1629
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex items-center text-muted-foreground select-none", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "italic font-serif font-bold text-sm text-primary", children: "fx" }) }),
1630
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
933
1631
  "input",
934
1632
  {
935
1633
  ref: formulaBarRef,
@@ -965,19 +1663,33 @@ var SpreadsheetEditor = ({
965
1663
  }
966
1664
  }
967
1665
  ),
968
- !formulaBarReadOnly && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1666
+ !formulaBarReadOnly && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
969
1667
  FormulaHints,
970
1668
  {
971
1669
  inputRef: formulaBarRef,
972
1670
  value: state.editingCell ? state.draftValue : formulaDraft ?? activeRawValue,
973
- onAccept: (next) => state.editingCell ? state.setDraftValue(next) : setFormulaDraft(next)
1671
+ onAccept: (next) => state.editingCell ? state.setDraftValue(next) : setFormulaDraft(next),
1672
+ names: nameSuggestions
974
1673
  }
975
1674
  )
976
1675
  ] }),
977
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "relative flex-1 overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("table", { className: "border-collapse table-fixed w-max min-w-full", children: [
978
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("tr", { className: "h-7 bg-muted sticky top-0 z-20 border-b border-border shadow-xs", children: [
979
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { className: "w-12 min-w-[3rem] sticky left-0 z-30 bg-muted border-r border-border text-center text-[10px] text-muted-foreground font-medium", children: "#" }),
980
- view.columns.map(({ column: col, index: cIdx, frozen, stickyLeft }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1676
+ inputMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { role: "note", "data-validation-input-message": "", className: "border-b border-border bg-muted/30 px-3 py-1 text-[11px] text-muted-foreground", children: [
1677
+ inputMessage.inputTitle && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mr-1 font-semibold text-foreground", children: inputMessage.inputTitle }),
1678
+ inputMessage.inputMessage
1679
+ ] }),
1680
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: scrollRef, className: "relative flex-1 overflow-auto", onScroll: handleGridScroll, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("table", { className: "border-separate border-spacing-0 table-fixed w-max min-w-full [&>*>tr:not([data-spacer])>*]:border-b [&>*>tr>*]:border-border", children: [
1681
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("tr", { className: "h-7 bg-muted sticky top-0 z-20 border-b border-border shadow-xs", children: [
1682
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { className: "w-12 min-w-[3rem] sticky left-0 z-30 bg-muted border-r border-border text-center text-[10px] text-muted-foreground font-medium", children: "#" }),
1683
+ columnSlots.map((slot) => slot.kind === "space" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1684
+ "th",
1685
+ {
1686
+ "aria-hidden": "true",
1687
+ "data-spacer": "",
1688
+ className: "p-0",
1689
+ style: { width: slot.width, minWidth: slot.width, maxWidth: slot.width, padding: 0 }
1690
+ },
1691
+ slot.key
1692
+ ) : (({ column: col, index: cIdx, frozen, stickyLeft }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
981
1693
  "th",
982
1694
  {
983
1695
  "data-col": cIdx,
@@ -992,16 +1704,16 @@ var SpreadsheetEditor = ({
992
1704
  onClick: () => onColumnHeaderClick?.(col),
993
1705
  onDoubleClick: (e) => handleHeaderDblClick(col.id, cIdx, e),
994
1706
  children: [
995
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center justify-between gap-1 min-w-0 overflow-hidden truncate", children: [
996
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "truncate font-semibold text-foreground text-xs min-w-0", children: col.title }),
997
- filteredColIds.has(col.id) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[10px] text-primary shrink-0", title: "Filtered", "data-filtered": "true", children: "\u23F7" }),
998
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "text-[10px] text-muted-foreground/70 font-mono font-normal shrink-0", children: [
1707
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between gap-1 min-w-0 overflow-hidden truncate", children: [
1708
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "truncate font-semibold text-foreground text-xs min-w-0", children: col.title }),
1709
+ filteredColIds.has(col.id) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[10px] text-primary shrink-0", title: "Filtered", "data-filtered": "true", children: "\u23F7" }),
1710
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "text-[10px] text-muted-foreground/70 font-mono font-normal shrink-0", children: [
999
1711
  "(",
1000
1712
  col.key,
1001
1713
  ")"
1002
1714
  ] })
1003
1715
  ] }),
1004
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1716
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1005
1717
  "div",
1006
1718
  {
1007
1719
  className: "col-resize-handle",
@@ -1018,7 +1730,7 @@ var SpreadsheetEditor = ({
1018
1730
  onDoubleClick: (e) => handleResizeDblClick(col.id, cIdx, e),
1019
1731
  role: "separator",
1020
1732
  "aria-orientation": "vertical",
1021
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1733
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1022
1734
  "div",
1023
1735
  {
1024
1736
  className: "col-resize-handle-line",
@@ -1036,24 +1748,32 @@ var SpreadsheetEditor = ({
1036
1748
  ]
1037
1749
  },
1038
1750
  col.id
1039
- )),
1040
- onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1751
+ ))(slot.viewCol)),
1752
+ onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1041
1753
  "th",
1042
1754
  {
1043
1755
  className: "w-10 min-w-[2.5rem] px-1 text-center bg-muted/40 hover:bg-muted cursor-pointer transition-colors",
1044
1756
  onClick: onAddColumnClick,
1045
1757
  title: "Add Column",
1046
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-xs font-bold text-muted-foreground", children: "+" })
1758
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-xs font-bold text-muted-foreground", children: "+" })
1047
1759
  }
1048
1760
  )
1049
1761
  ] }) }),
1050
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { children: view.rows.map((viewRow) => {
1762
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tbody", { children: rowSlots.map((rowSlot) => {
1763
+ if (rowSlot.kind === "space") {
1764
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tr", { "aria-hidden": "true", "data-spacer": "", style: { height: rowSlot.height }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { colSpan: columnSlots.length + 2, style: { padding: 0, border: 0 } }) }, rowSlot.key);
1765
+ }
1766
+ const viewRow = rowSlot.viewRow;
1051
1767
  if (viewRow.kind === "group") {
1052
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1768
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1053
1769
  SpreadsheetGroupRow,
1054
1770
  {
1055
1771
  group: viewRow.group,
1056
- columns: view.columns,
1772
+ columns: renderedColumns,
1773
+ labelColumns: view.columns,
1774
+ leadingColumns: viewWindow.virtual ? viewWindow.frozenColumns : void 0,
1775
+ leftSpace: viewWindow.leftSpace,
1776
+ rightSpace: viewWindow.rightSpace,
1057
1777
  groupColId: state.document.table?.grouping?.colId ?? "",
1058
1778
  onToggle: state.toggleGroupCollapsed,
1059
1779
  trailingCell: !!onAddColumnClick
@@ -1062,15 +1782,15 @@ var SpreadsheetEditor = ({
1062
1782
  );
1063
1783
  }
1064
1784
  const displayRowIndex = viewRow.index;
1065
- const selection = state.selectedRange ? (0, import_spreadsheet5.normalizeRange)(state.selectedRange) : null;
1066
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1785
+ const selection = state.selectedRange ? (0, import_spreadsheet8.normalizeRange)(state.selectedRange) : null;
1786
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1067
1787
  "tr",
1068
1788
  {
1069
1789
  "data-row": displayRowIndex,
1070
1790
  className: `border-b border-border hover:bg-muted/10 transition-colors ${viewRow.section !== "body" ? "bg-muted/30 font-semibold" : ""}`,
1071
1791
  style: { height: viewRow.row.height || 32 },
1072
1792
  children: [
1073
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1793
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1074
1794
  "td",
1075
1795
  {
1076
1796
  className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted border-r border-border text-center text-[10px] text-muted-foreground font-mono select-none",
@@ -1078,7 +1798,9 @@ var SpreadsheetEditor = ({
1078
1798
  children: displayRowIndex + 1
1079
1799
  }
1080
1800
  ),
1081
- view.columns.map((viewCol) => {
1801
+ columnSlots.map((slot) => {
1802
+ if (slot.kind === "space") return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { "aria-hidden": "true", "data-spacer": "", style: { padding: 0 } }, slot.key);
1803
+ const viewCol = slot.viewCol;
1082
1804
  const span = view.getCellSpan(displayRowIndex, viewCol.index);
1083
1805
  if (!span) return null;
1084
1806
  const rIdx = span.source.row;
@@ -1089,9 +1811,13 @@ var SpreadsheetEditor = ({
1089
1811
  const selected = isCellSelected(rIdx, cIdx);
1090
1812
  const active = isActiveCell(rIdx, cIdx);
1091
1813
  const isEditing = state.editingCell?.row === rIdx && state.editingCell?.col === cIdx;
1092
- const inFillPreview = !!fillTarget && !selected && (0, import_spreadsheet5.rangeContains)(fillTarget, displayRowIndex, viewCol.index);
1093
- const showFillHandle = !readOnly && !isEditing && !!selection && (span.merge ? (0, import_spreadsheet5.rangeContains)(span.merge, selection.endRow, selection.endCol) : selection.endRow === rIdx && selection.endCol === cIdx);
1094
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1814
+ const conditional = overlay.get(rIdx, cIdx);
1815
+ const conditionalCss = (0, import_spreadsheet8.getConditionalFormatCss)(conditional);
1816
+ const circled = state.controller.isInvalidDataCircled(rIdx, cIdx);
1817
+ const showListArrow = active && !isEditing && !!activeListValues;
1818
+ const inFillPreview = !!fillTarget && !selected && (0, import_spreadsheet8.rangeContains)(fillTarget, displayRowIndex, viewCol.index);
1819
+ const showFillHandle = !readOnly && !isEditing && !!selection && (span.merge ? (0, import_spreadsheet8.rangeContains)(span.merge, selection.endRow, selection.endCol) : selection.endRow === rIdx && selection.endCol === cIdx);
1820
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1095
1821
  "td",
1096
1822
  {
1097
1823
  "data-row": rIdx,
@@ -1103,15 +1829,19 @@ var SpreadsheetEditor = ({
1103
1829
  maxWidth: 0,
1104
1830
  overflow: "hidden",
1105
1831
  ...frozenCellStyle(viewRow, viewCol),
1832
+ ...conditionalCss,
1833
+ // Keep the selection tint visible over a conditional fill.
1834
+ ...conditionalCss.background && selected ? { boxShadow: "inset 0 0 0 9999px color-mix(in srgb, var(--primary, #6366f1) 12%, transparent)" } : {},
1106
1835
  ...inFillPreview ? { outline: "1px dashed var(--primary, #6366f1)", outlineOffset: -1 } : {}
1107
1836
  },
1837
+ "data-cf-fill": conditional?.fill,
1108
1838
  onClick: (e) => handleCellClick(rIdx, cIdx, e),
1109
1839
  onDoubleClick: () => handleCellDblClick(rIdx, cIdx),
1110
1840
  onContextMenu: (e) => handleCellContextMenu(rIdx, cIdx, e),
1111
1841
  onMouseOver: () => extendFill(displayRowIndex, viewCol.index),
1112
1842
  children: [
1113
- isEditing ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1114
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1843
+ isEditing ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1844
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1115
1845
  "input",
1116
1846
  {
1117
1847
  ref: cellInputRef,
@@ -1127,10 +1857,36 @@ var SpreadsheetEditor = ({
1127
1857
  autoFocus: true
1128
1858
  }
1129
1859
  ),
1130
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FormulaHints, { inputRef: cellInputRef, value: state.draftValue, onAccept: state.setDraftValue })
1131
- ] }) : customCellRenderer ? customCellRenderer({ row, col, cell }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CellText, { cell }),
1132
- activeSpill && (0, import_spreadsheet5.rangeContains)(activeSpill.range, rIdx, cIdx) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", className: "spreadsheet-spill-border", style: spillBorderStyle(activeSpill.range, rIdx, cIdx) }),
1133
- showFillHandle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1860
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(FormulaHints, { inputRef: cellInputRef, value: state.draftValue, onAccept: state.setDraftValue, names: nameSuggestions })
1861
+ ] }) : customCellRenderer ? customCellRenderer({ row, col, cell }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CellText, { cell, conditional }),
1862
+ circled && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1863
+ "span",
1864
+ {
1865
+ className: "spreadsheet-invalid-circle",
1866
+ "aria-label": "Invalid value",
1867
+ style: { position: "absolute", inset: "1px 2px", border: "2px solid #e11d48", borderRadius: "50%", pointerEvents: "none", zIndex: 6 }
1868
+ }
1869
+ ),
1870
+ showListArrow && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1871
+ "button",
1872
+ {
1873
+ type: "button",
1874
+ className: "spreadsheet-list-arrow",
1875
+ "aria-label": "Show allowed values",
1876
+ "aria-haspopup": "listbox",
1877
+ tabIndex: -1,
1878
+ style: { position: "absolute", right: 1, top: 1, bottom: 1, width: 16, zIndex: 24, fontSize: 10, lineHeight: 1, background: "var(--muted, #f4f4f5)", border: "1px solid var(--border, #e4e4e7)", borderRadius: 2, cursor: "pointer", padding: 0 },
1879
+ onMouseDown: (e) => e.stopPropagation(),
1880
+ onClick: (e) => {
1881
+ e.stopPropagation();
1882
+ if (listMenu) setListMenu(null);
1883
+ else openListMenu(rIdx, cIdx, e.currentTarget.parentElement);
1884
+ },
1885
+ children: "\u25BE"
1886
+ }
1887
+ ),
1888
+ activeSpill && (0, import_spreadsheet8.rangeContains)(activeSpill.range, rIdx, cIdx) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "aria-hidden": "true", className: "spreadsheet-spill-border", style: spillBorderStyle(activeSpill.range, rIdx, cIdx) }),
1889
+ showFillHandle && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1134
1890
  "div",
1135
1891
  {
1136
1892
  className: "spreadsheet-fill-handle",
@@ -1145,14 +1901,14 @@ var SpreadsheetEditor = ({
1145
1901
  viewCol.column.id
1146
1902
  );
1147
1903
  }),
1148
- onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
1904
+ onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
1149
1905
  ]
1150
1906
  },
1151
1907
  viewRow.row.id
1152
1908
  );
1153
1909
  }) })
1154
1910
  ] }) }),
1155
- menu && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1911
+ menu && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1156
1912
  SpreadsheetContextMenu,
1157
1913
  {
1158
1914
  items: menu.items,
@@ -1161,10 +1917,45 @@ var SpreadsheetEditor = ({
1161
1917
  onClose: () => {
1162
1918
  setMenu(null);
1163
1919
  gridRef.current?.focus();
1164
- }
1920
+ },
1921
+ onOpenPanel: setPanel
1922
+ }
1923
+ ),
1924
+ panel === "conditionalFormatting" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SpreadsheetConditionalFormatPanel, { state, onClose: () => setPanel(null) }),
1925
+ panel === "dataValidation" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SpreadsheetDataValidationPanel, { state, onClose: () => setPanel(null) }),
1926
+ listMenu && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1927
+ "div",
1928
+ {
1929
+ role: "listbox",
1930
+ "aria-label": "Allowed values",
1931
+ className: "fixed z-50 max-h-60 min-w-[8rem] overflow-y-auto rounded-md border border-border bg-popover py-1 text-xs text-popover-foreground shadow-lg",
1932
+ style: { left: listMenu.x, top: listMenu.y, background: "var(--popover, var(--background, #fff))" },
1933
+ onKeyDown: (e) => {
1934
+ e.stopPropagation();
1935
+ if (e.key === "Escape") {
1936
+ setListMenu(null);
1937
+ gridRef.current?.focus();
1938
+ }
1939
+ },
1940
+ children: [
1941
+ listMenu.values.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "px-3 py-1 text-muted-foreground", children: "No values" }),
1942
+ listMenu.values.map((value, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1943
+ "button",
1944
+ {
1945
+ type: "button",
1946
+ role: "option",
1947
+ "aria-selected": false,
1948
+ className: "block w-full px-3 py-1 text-left hover:bg-muted",
1949
+ onClick: () => chooseListValue(value),
1950
+ children: value
1951
+ },
1952
+ `${i}:${value}`
1953
+ ))
1954
+ ]
1165
1955
  }
1166
1956
  ),
1167
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `
1957
+ state.validationPrompt && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SpreadsheetValidationDialog, { prompt: state.validationPrompt, onResolve: resolveValidation }),
1958
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("style", { children: `
1168
1959
  .col-resize-handle:hover .col-resize-handle-line {
1169
1960
  background-color: var(--primary, #6366f1);
1170
1961
  opacity: 0.75;
@@ -1184,15 +1975,181 @@ var SpreadsheetEditor = ({
1184
1975
  ]
1185
1976
  }
1186
1977
  );
1978
+ if (!showSheetTabs) return grid;
1979
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex h-full w-full flex-col", children: [
1980
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "min-h-0 flex-1", children: grid }),
1981
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SheetTabs, { state, readOnly })
1982
+ ] });
1983
+ };
1984
+
1985
+ // src/spreadsheet/NameManager.tsx
1986
+ var import_react11 = require("react");
1987
+ var import_spreadsheet9 = require("@pixerate/editor/spreadsheet");
1988
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1989
+ var EMPTY_DRAFT = { original: null, name: "", refersTo: "=", scope: "", comment: "" };
1990
+ var NameManager = ({ state, readOnly = false, onClose, className = "" }) => {
1991
+ const { controller, sheets } = state;
1992
+ const rows = (0, import_react11.useMemo)(
1993
+ () => (0, import_spreadsheet9.getNameManagerRows)(controller),
1994
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1995
+ [controller, state.document, state.names]
1996
+ );
1997
+ const [draft, setDraft] = (0, import_react11.useState)(EMPTY_DRAFT);
1998
+ const [error, setError] = (0, import_react11.useState)(null);
1999
+ const edit = (row) => {
2000
+ setError(null);
2001
+ setDraft({
2002
+ original: { name: row.name, sheetId: row.sheetId },
2003
+ name: row.name,
2004
+ refersTo: row.refersTo,
2005
+ scope: row.sheetId ?? "",
2006
+ comment: row.comment ?? ""
2007
+ });
2008
+ };
2009
+ const save = (e) => {
2010
+ e.preventDefault();
2011
+ const scope = draft.scope ? { sheet: draft.scope } : {};
2012
+ try {
2013
+ const invalid = controller.validateDefinedName(draft.name.trim());
2014
+ if (invalid) throw new Error(invalid);
2015
+ try {
2016
+ (0, import_spreadsheet9.parseFormula)(draft.refersTo.trim().startsWith("=") ? draft.refersTo.trim() : `=${draft.refersTo.trim()}`);
2017
+ } catch (err) {
2018
+ throw new Error(`There's a problem with this formula: ${err?.message ?? draft.refersTo}`);
2019
+ }
2020
+ const original = draft.original;
2021
+ if (original) {
2022
+ const originalScope = original.sheetId ? { sheet: original.sheetId } : {};
2023
+ if ((original.sheetId ?? "") !== draft.scope) {
2024
+ state.deleteName(original.name, originalScope);
2025
+ } else if (original.name !== draft.name) {
2026
+ state.renameName(original.name, draft.name.trim(), originalScope);
2027
+ }
2028
+ }
2029
+ state.defineName(draft.name.trim(), draft.refersTo, { ...scope, comment: draft.comment || void 0 });
2030
+ setDraft(EMPTY_DRAFT);
2031
+ setError(null);
2032
+ } catch (err) {
2033
+ setError(err?.message ?? String(err));
2034
+ }
2035
+ };
2036
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("section", { "aria-label": "Name Manager", className: `flex flex-col gap-2 text-xs ${className}`, "data-name-manager": "", children: [
2037
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
2038
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { className: "m-0 text-sm font-semibold", children: "Name Manager" }),
2039
+ onClose && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "aria-label": "Close Name Manager", className: "rounded px-2 hover:bg-muted", onClick: onClose, children: "\xD7" })
2040
+ ] }),
2041
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("table", { className: "w-full border-collapse", children: [
2042
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { className: "text-left text-muted-foreground", children: [
2043
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { className: "px-1 font-medium", children: "Name" }),
2044
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { className: "px-1 font-medium", children: "Value" }),
2045
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { className: "px-1 font-medium", children: "Refers to" }),
2046
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { className: "px-1 font-medium", children: "Scope" }),
2047
+ !readOnly && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { className: "px-1" })
2048
+ ] }) }),
2049
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tbody", { children: [
2050
+ rows.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { colSpan: 5, className: "px-1 py-2 text-muted-foreground", children: "No names yet." }) }),
2051
+ rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { "data-name": row.name, className: "border-t border-border", title: row.comment, children: [
2052
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { className: "px-1 py-0.5 font-mono font-semibold", children: row.name }),
2053
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { className: "px-1 py-0.5", children: row.value }),
2054
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { className: "px-1 py-0.5 font-mono", children: row.refersTo }),
2055
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { className: "px-1 py-0.5", children: row.scopeLabel }),
2056
+ !readOnly && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("td", { className: "whitespace-nowrap px-1 py-0.5 text-right", children: [
2057
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "aria-label": `Edit ${row.name}`, className: "rounded px-1 hover:bg-muted", onClick: () => edit(row), children: "Edit" }),
2058
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2059
+ "button",
2060
+ {
2061
+ type: "button",
2062
+ "aria-label": `Delete ${row.name}`,
2063
+ className: "rounded px-1 text-destructive hover:bg-muted",
2064
+ onClick: () => {
2065
+ state.deleteName(row.name, row.sheetId ? { sheet: row.sheetId } : {});
2066
+ if (draft.original?.name === row.name && (draft.original.sheetId ?? "") === (row.sheetId ?? "")) setDraft(EMPTY_DRAFT);
2067
+ },
2068
+ children: "Delete"
2069
+ }
2070
+ )
2071
+ ] })
2072
+ ] }, `${row.sheetId ?? ""}!${row.name}`))
2073
+ ] })
2074
+ ] }),
2075
+ !readOnly && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("form", { className: "grid grid-cols-[auto_1fr] items-center gap-x-2 gap-y-1", onSubmit: save, "aria-label": draft.original ? `Edit name ${draft.original.name}` : "New name", children: [
2076
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { htmlFor: "pixerate-name-manager-name", children: "Name" }),
2077
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2078
+ "input",
2079
+ {
2080
+ id: "pixerate-name-manager-name",
2081
+ className: "rounded border border-border bg-background px-1 py-0.5",
2082
+ value: draft.name,
2083
+ onChange: (e) => setDraft({ ...draft, name: e.target.value })
2084
+ }
2085
+ ),
2086
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { htmlFor: "pixerate-name-manager-refers", children: "Refers to" }),
2087
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2088
+ "input",
2089
+ {
2090
+ id: "pixerate-name-manager-refers",
2091
+ className: "rounded border border-border bg-background px-1 py-0.5 font-mono",
2092
+ value: draft.refersTo,
2093
+ onChange: (e) => setDraft({ ...draft, refersTo: e.target.value })
2094
+ }
2095
+ ),
2096
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { htmlFor: "pixerate-name-manager-scope", children: "Scope" }),
2097
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2098
+ "select",
2099
+ {
2100
+ id: "pixerate-name-manager-scope",
2101
+ className: "rounded border border-border bg-background px-1 py-0.5",
2102
+ value: draft.scope,
2103
+ onChange: (e) => setDraft({ ...draft, scope: e.target.value }),
2104
+ children: [
2105
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "", children: "Workbook" }),
2106
+ sheets.map((sheet) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: sheet.id, children: sheet.name }, sheet.id))
2107
+ ]
2108
+ }
2109
+ ),
2110
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { htmlFor: "pixerate-name-manager-comment", children: "Comment" }),
2111
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2112
+ "input",
2113
+ {
2114
+ id: "pixerate-name-manager-comment",
2115
+ className: "rounded border border-border bg-background px-1 py-0.5",
2116
+ value: draft.comment,
2117
+ onChange: (e) => setDraft({ ...draft, comment: e.target.value })
2118
+ }
2119
+ ),
2120
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "col-span-2 flex gap-2", children: [
2121
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "submit", className: "rounded bg-primary px-2 py-0.5 text-primary-foreground", children: draft.original ? "Save" : "Add name" }),
2122
+ draft.original && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2123
+ "button",
2124
+ {
2125
+ type: "button",
2126
+ className: "rounded px-2 py-0.5 hover:bg-muted",
2127
+ onClick: () => {
2128
+ setDraft(EMPTY_DRAFT);
2129
+ setError(null);
2130
+ },
2131
+ children: "Cancel"
2132
+ }
2133
+ )
2134
+ ] })
2135
+ ] }),
2136
+ error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { role: "alert", className: "text-destructive", children: error })
2137
+ ] });
1187
2138
  };
1188
2139
  // Annotate the CommonJS export names for ESM import in node:
1189
2140
  0 && (module.exports = {
1190
2141
  FormulaHints,
2142
+ NameManager,
2143
+ SheetTabs,
2144
+ SpreadsheetConditionalFormatPanel,
1191
2145
  SpreadsheetContextMenu,
2146
+ SpreadsheetDataValidationPanel,
1192
2147
  SpreadsheetEditor,
1193
2148
  SpreadsheetGroupRow,
2149
+ SpreadsheetValidationDialog,
1194
2150
  useFillHandle,
1195
2151
  useSpreadsheetClipboard,
1196
- useSpreadsheetEditor
2152
+ useSpreadsheetEditor,
2153
+ useSpreadsheetViewport
1197
2154
  });
1198
2155
  //# sourceMappingURL=index.cjs.map