@pixerate/editor-react 0.13.0 → 0.15.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 (38) hide show
  1. package/README.md +5 -4
  2. package/dist/canvas/index.cjs +329 -84
  3. package/dist/canvas/index.cjs.map +1 -1
  4. package/dist/canvas/index.d.cts +82 -4
  5. package/dist/canvas/index.d.ts +82 -4
  6. package/dist/canvas/index.js +266 -18
  7. package/dist/canvas/index.js.map +1 -1
  8. package/dist/chunk-4ZC2WCUO.js +196 -0
  9. package/dist/chunk-4ZC2WCUO.js.map +1 -0
  10. package/dist/{chunk-TL27COKM.js → chunk-LPFZMZ3L.js} +45 -2
  11. package/dist/chunk-LPFZMZ3L.js.map +1 -0
  12. package/dist/{chunk-MHTAR3RS.js → chunk-QVQYYHJ5.js} +87 -29
  13. package/dist/chunk-QVQYYHJ5.js.map +1 -0
  14. package/dist/commands/index.cjs +232 -0
  15. package/dist/commands/index.cjs.map +1 -0
  16. package/dist/commands/index.d.cts +69 -0
  17. package/dist/commands/index.d.ts +69 -0
  18. package/dist/commands/index.js +13 -0
  19. package/dist/commands/index.js.map +1 -0
  20. package/dist/image-editor/index.cjs +41 -0
  21. package/dist/image-editor/index.cjs.map +1 -1
  22. package/dist/image-editor/index.d.cts +2 -0
  23. package/dist/image-editor/index.d.ts +2 -0
  24. package/dist/image-editor/index.js +1 -1
  25. package/dist/index.cjs +624 -268
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +57 -9
  28. package/dist/index.d.ts +57 -9
  29. package/dist/index.js +147 -71
  30. package/dist/index.js.map +1 -1
  31. package/dist/spreadsheet/index.cjs +79 -22
  32. package/dist/spreadsheet/index.cjs.map +1 -1
  33. package/dist/spreadsheet/index.d.cts +2 -3
  34. package/dist/spreadsheet/index.d.ts +2 -3
  35. package/dist/spreadsheet/index.js +1 -1
  36. package/package.json +10 -2
  37. package/dist/chunk-MHTAR3RS.js.map +0 -1
  38. package/dist/chunk-TL27COKM.js.map +0 -1
@@ -68,8 +68,12 @@ function useSpreadsheetEditor(options = {}) {
68
68
  const [activeCell, setActiveCell] = (0, import_react.useState)(controller.activeCell);
69
69
  const [selectedRange, setSelectedRange] = (0, import_react.useState)(controller.selectedRange);
70
70
  const [editingCell, setEditingCell] = (0, import_react.useState)(null);
71
- const [draftValue, setDraftValue] = (0, import_react.useState)("");
71
+ const [draftValue, setDraftState] = (0, import_react.useState)("");
72
72
  settersRef.current = { setDoc, setActiveCell, setSelectedRange };
73
+ const setDraftValue = (0, import_react.useCallback)((value) => {
74
+ controller.draftValue = value;
75
+ setDraftState(value);
76
+ }, [controller]);
73
77
  const setCellValue = (0, import_react.useCallback)((rowId, colId, raw) => {
74
78
  controller.setCellValue(rowId, colId, raw);
75
79
  setDoc({ ...controller.document });
@@ -203,15 +207,30 @@ function useSpreadsheetEditor(options = {}) {
203
207
 
204
208
  // src/spreadsheet/SpreadsheetEditor.tsx
205
209
  var import_react5 = require("react");
206
- var import_spreadsheet4 = require("@pixerate/editor/spreadsheet");
210
+ var import_spreadsheet5 = require("@pixerate/editor/spreadsheet");
207
211
 
208
212
  // src/spreadsheet/SpreadsheetContextMenu.tsx
209
213
  var import_react2 = __toESM(require("react"), 1);
214
+ var import_spreadsheet2 = require("@pixerate/editor/spreadsheet");
210
215
  var import_jsx_runtime = require("react/jsx-runtime");
216
+ var VIEWPORT_MARGIN = 4;
211
217
  var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
212
218
  const menuRef = (0, import_react2.useRef)(null);
213
219
  const onCloseRef = (0, import_react2.useRef)(onClose);
214
220
  onCloseRef.current = onClose;
221
+ const [position, setPosition] = (0, import_react2.useState)({ x, y });
222
+ (0, import_react2.useLayoutEffect)(() => {
223
+ const menu = menuRef.current;
224
+ if (!menu || typeof window === "undefined") return;
225
+ const { width, height } = menu.getBoundingClientRect();
226
+ const next = (0, import_spreadsheet2.clampMenuPosition)(
227
+ { x, y },
228
+ { width, height },
229
+ { width: window.innerWidth, height: window.innerHeight },
230
+ VIEWPORT_MARGIN
231
+ );
232
+ setPosition((prev) => prev.x === next.x && prev.y === next.y ? prev : next);
233
+ }, [x, y, items]);
215
234
  (0, import_react2.useEffect)(() => {
216
235
  const handlePointer = (e) => {
217
236
  if (!menuRef.current?.contains(e.target)) onCloseRef.current();
@@ -233,7 +252,13 @@ var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
233
252
  role: "menu",
234
253
  "aria-label": "Table actions",
235
254
  className: "fixed z-50 min-w-[14rem] rounded-md border border-border bg-popover py-1 text-xs text-popover-foreground shadow-lg",
236
- style: { left: x, top: y, background: "var(--popover, var(--background, #fff))" },
255
+ style: {
256
+ left: position.x,
257
+ top: position.y,
258
+ maxHeight: `calc(100vh - ${VIEWPORT_MARGIN * 2}px)`,
259
+ overflowY: "auto",
260
+ background: "var(--popover, var(--background, #fff))"
261
+ },
237
262
  onContextMenu: (e) => e.preventDefault(),
238
263
  onKeyDown: (e) => e.stopPropagation(),
239
264
  children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.default.Fragment, { children: [
@@ -270,7 +295,7 @@ var SpreadsheetGroupRow = ({
270
295
  }) => {
271
296
  const labelColId = columns.some((c) => c.column.id === groupColId) ? groupColId : columns[0]?.column.id;
272
297
  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: [
273
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted/60 border-r border-border text-center", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
298
+ /* @__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)(
274
299
  "button",
275
300
  {
276
301
  type: "button",
@@ -302,7 +327,7 @@ var SpreadsheetGroupRow = ({
302
327
 
303
328
  // src/spreadsheet/useSpreadsheetTableInteractions.ts
304
329
  var import_react3 = require("react");
305
- var import_spreadsheet2 = require("@pixerate/editor/spreadsheet");
330
+ var import_spreadsheet3 = require("@pixerate/editor/spreadsheet");
306
331
  function gridHasFocus(grid) {
307
332
  if (!grid || typeof document === "undefined") return false;
308
333
  const active = document.activeElement;
@@ -343,7 +368,8 @@ function useSpreadsheetClipboard(gridRef, state, readOnly) {
343
368
  setTimeout(() => {
344
369
  if (copiedRef.current) return;
345
370
  const s = stateRef.current;
346
- navigator.clipboard?.writeText(s.exportToTsv(s.selectedRange));
371
+ Promise.resolve(navigator.clipboard?.writeText(s.exportToTsv(s.selectedRange))).catch(() => {
372
+ });
347
373
  }, 0);
348
374
  }, []);
349
375
  return { copyShortcutFallback };
@@ -369,19 +395,19 @@ function useFillHandle(state) {
369
395
  e.preventDefault();
370
396
  e.stopPropagation();
371
397
  const range = stateRef.current.selectedRange;
372
- if (range) setDrag({ source: (0, import_spreadsheet2.normalizeRange)(range), target: null });
398
+ if (range) setDrag({ source: (0, import_spreadsheet3.normalizeRange)(range), target: null });
373
399
  }, []);
374
400
  const extendFill = (0, import_react3.useCallback)((row, col) => {
375
401
  const current = dragRef.current;
376
402
  if (!current) return;
377
- setDrag({ source: current.source, target: (0, import_spreadsheet2.computeFillTarget)(current.source, { row, col }) });
403
+ setDrag({ source: current.source, target: (0, import_spreadsheet3.computeFillTarget)(current.source, { row, col }) });
378
404
  }, []);
379
405
  return { fillTarget: drag?.target ?? null, isFilling: dragging, startFill, extendFill };
380
406
  }
381
407
 
382
408
  // src/spreadsheet/FormulaHints.tsx
383
409
  var import_react4 = require("react");
384
- var import_spreadsheet3 = require("@pixerate/editor/spreadsheet");
410
+ var import_spreadsheet4 = require("@pixerate/editor/spreadsheet");
385
411
  var import_jsx_runtime3 = require("react/jsx-runtime");
386
412
  var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
387
413
  const [caret, setCaret] = (0, import_react4.useState)(null);
@@ -391,7 +417,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
391
417
  const [position, setPosition] = (0, import_react4.useState)(null);
392
418
  const pendingCaret = (0, import_react4.useRef)(null);
393
419
  const hints = (0, import_react4.useMemo)(
394
- () => (0, import_spreadsheet3.getFormulaHints)(value, caret ?? value.length, { limit }),
420
+ () => (0, import_spreadsheet4.getFormulaHints)(value, caret ?? value.length, { limit }),
395
421
  [value, caret, limit]
396
422
  );
397
423
  const suggestions = dismissed ? [] : hints.suggestions;
@@ -403,7 +429,7 @@ var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
403
429
  const { suggestions: list, value: current, caret: at, onAccept: commit } = latest.current;
404
430
  const suggestion = list[index];
405
431
  if (!suggestion) return;
406
- const next = (0, import_spreadsheet3.applyFormulaSuggestion)(current, at ?? current.length, suggestion.name);
432
+ const next = (0, import_spreadsheet4.applyFormulaSuggestion)(current, at ?? current.length, suggestion.name);
407
433
  pendingCaret.current = next.caret;
408
434
  commit(next.value);
409
435
  };
@@ -543,6 +569,20 @@ function frozenCellStyle(viewRow, viewCol) {
543
569
  background: "var(--background, #fff)"
544
570
  };
545
571
  }
572
+ var coordKey = (cell) => cell ? `${(0, import_spreadsheet5.indexToColName)(cell.col)}${cell.row + 1}` : "";
573
+ function spillBorderStyle(range, row, col) {
574
+ const line = "1px solid color-mix(in srgb, var(--primary, #6366f1) 60%, transparent)";
575
+ return {
576
+ position: "absolute",
577
+ inset: 0,
578
+ pointerEvents: "none",
579
+ zIndex: 5,
580
+ borderTop: row === range.startRow ? line : void 0,
581
+ borderBottom: row === range.endRow ? line : void 0,
582
+ borderLeft: col === range.startCol ? line : void 0,
583
+ borderRight: col === range.endCol ? line : void 0
584
+ };
585
+ }
546
586
  var SpreadsheetEditor = ({
547
587
  state: externalState,
548
588
  readOnly = false,
@@ -573,10 +613,10 @@ var SpreadsheetEditor = ({
573
613
  if (readOnly) return;
574
614
  e.preventDefault();
575
615
  if (state.editingCell) state.commitEditing();
576
- if (!state.selectedRange || !(0, import_spreadsheet4.rangeContains)(state.selectedRange, rIdx, cIdx)) {
616
+ if (!state.selectedRange || !(0, import_spreadsheet5.rangeContains)(state.selectedRange, rIdx, cIdx)) {
577
617
  state.selectCell(rIdx, cIdx);
578
618
  }
579
- setMenu({ x: e.clientX, y: e.clientY, items: (0, import_spreadsheet4.getSpreadsheetMenuItems)(state.controller, { row: rIdx, col: cIdx }) });
619
+ setMenu({ x: e.clientX, y: e.clientY, items: (0, import_spreadsheet5.getSpreadsheetMenuItems)(state.controller, { row: rIdx, col: cIdx }) });
580
620
  }, [readOnly, state]);
581
621
  const activeRawValue = (0, import_react5.useMemo)(() => {
582
622
  if (!state.activeCell) return "";
@@ -585,9 +625,15 @@ var SpreadsheetEditor = ({
585
625
  if (!row || !col) return "";
586
626
  return state.getCell(row.id, col.id).raw;
587
627
  }, [state.activeCell, state.document]);
628
+ const activeSpill = (0, import_react5.useMemo)(
629
+ () => state.activeCell ? state.controller.getSpillAt(state.activeCell.row, state.activeCell.col) : null,
630
+ // eslint-disable-next-line react-hooks/exhaustive-deps
631
+ [state.controller, state.activeCell, state.document]
632
+ );
633
+ const spillFormula = activeSpill && !activeRawValue && activeSpill.key !== coordKey(state.activeCell) ? activeSpill.formula : "";
588
634
  const coordLabel = (0, import_react5.useMemo)(() => {
589
635
  if (!state.activeCell) return "--";
590
- const colName = (0, import_spreadsheet4.indexToColName)(state.activeCell.col);
636
+ const colName = (0, import_spreadsheet5.indexToColName)(state.activeCell.col);
591
637
  return `${colName}${state.activeCell.row + 1}`;
592
638
  }, [state.activeCell]);
593
639
  const [formulaDraft, setFormulaDraft] = (0, import_react5.useState)(null);
@@ -634,7 +680,10 @@ var SpreadsheetEditor = ({
634
680
  e.preventDefault();
635
681
  state.commitEditing();
636
682
  state.moveCursor(e.shiftKey ? "left" : "right");
683
+ } else {
684
+ return;
637
685
  }
686
+ gridRef.current?.focus();
638
687
  return;
639
688
  }
640
689
  if (!state.activeCell) return;
@@ -682,6 +731,7 @@ var SpreadsheetEditor = ({
682
731
  } else if (!readOnly && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
683
732
  const col = state.document.columns[state.activeCell.col];
684
733
  if (!col?.readOnly) {
734
+ e.preventDefault();
685
735
  state.startEditing(state.activeCell.row, state.activeCell.col, e.key);
686
736
  }
687
737
  }
@@ -874,7 +924,9 @@ var SpreadsheetEditor = ({
874
924
  placeholder: "Enter a value or formula (e.g. =SUM(A1:A5))",
875
925
  "aria-label": "Formula bar",
876
926
  readOnly: formulaBarReadOnly,
877
- value: state.editingCell ? state.draftValue : formulaDraft ?? activeRawValue,
927
+ "data-spill-formula": !state.editingCell && formulaDraft === null && spillFormula ? "true" : void 0,
928
+ style: !state.editingCell && formulaDraft === null && spillFormula ? { color: "var(--muted-foreground, #94a3b8)" } : void 0,
929
+ value: state.editingCell ? state.draftValue : formulaDraft ?? (activeRawValue || spillFormula),
878
930
  onFocus: () => {
879
931
  if (!state.editingCell) setFormulaDraft(activeRawValue);
880
932
  },
@@ -909,8 +961,8 @@ var SpreadsheetEditor = ({
909
961
  )
910
962
  ] }),
911
963
  /* @__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: [
912
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("tr", { className: "h-7 bg-muted/60 sticky top-0 z-20 border-b border-border shadow-xs", children: [
913
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { className: "w-12 min-w-[3rem] sticky left-0 z-30 bg-muted/90 border-r border-border text-center text-[10px] text-muted-foreground font-medium", children: "#" }),
964
+ /* @__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: [
965
+ /* @__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: "#" }),
914
966
  view.columns.map(({ column: col, index: cIdx, frozen, stickyLeft }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
915
967
  "th",
916
968
  {
@@ -996,7 +1048,7 @@ var SpreadsheetEditor = ({
996
1048
  );
997
1049
  }
998
1050
  const displayRowIndex = viewRow.index;
999
- const selection = state.selectedRange ? (0, import_spreadsheet4.normalizeRange)(state.selectedRange) : null;
1051
+ const selection = state.selectedRange ? (0, import_spreadsheet5.normalizeRange)(state.selectedRange) : null;
1000
1052
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1001
1053
  "tr",
1002
1054
  {
@@ -1007,7 +1059,7 @@ var SpreadsheetEditor = ({
1007
1059
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1008
1060
  "td",
1009
1061
  {
1010
- className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted/50 border-r border-border text-center text-[10px] text-muted-foreground font-mono select-none",
1062
+ 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",
1011
1063
  style: viewRow.frozen ? { top: COLUMN_HEADER_HEIGHT + viewRow.stickyTop, zIndex: 15 } : void 0,
1012
1064
  children: displayRowIndex + 1
1013
1065
  }
@@ -1023,8 +1075,8 @@ var SpreadsheetEditor = ({
1023
1075
  const selected = isCellSelected(rIdx, cIdx);
1024
1076
  const active = isActiveCell(rIdx, cIdx);
1025
1077
  const isEditing = state.editingCell?.row === rIdx && state.editingCell?.col === cIdx;
1026
- const inFillPreview = !!fillTarget && !selected && (0, import_spreadsheet4.rangeContains)(fillTarget, displayRowIndex, viewCol.index);
1027
- const showFillHandle = !readOnly && !isEditing && !!selection && (span.merge ? (0, import_spreadsheet4.rangeContains)(span.merge, selection.endRow, selection.endCol) : selection.endRow === rIdx && selection.endCol === cIdx);
1078
+ const inFillPreview = !!fillTarget && !selected && (0, import_spreadsheet5.rangeContains)(fillTarget, displayRowIndex, viewCol.index);
1079
+ const showFillHandle = !readOnly && !isEditing && !!selection && (span.merge ? (0, import_spreadsheet5.rangeContains)(span.merge, selection.endRow, selection.endCol) : selection.endRow === rIdx && selection.endCol === cIdx);
1028
1080
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1029
1081
  "td",
1030
1082
  {
@@ -1054,6 +1106,10 @@ var SpreadsheetEditor = ({
1054
1106
  value: state.draftValue,
1055
1107
  onChange: (e) => state.setDraftValue(e.target.value),
1056
1108
  onBlur: () => state.commitEditing(),
1109
+ onFocus: (e) => {
1110
+ const end = e.currentTarget.value.length;
1111
+ e.currentTarget.setSelectionRange(end, end);
1112
+ },
1057
1113
  autoFocus: true
1058
1114
  }
1059
1115
  ),
@@ -1063,9 +1119,10 @@ var SpreadsheetEditor = ({
1063
1119
  {
1064
1120
  className: `truncate block max-w-full ${cell.error ? "text-destructive font-semibold" : ""}`,
1065
1121
  title: cell.error ? cell.errorMessage ?? cell.error : void 0,
1066
- children: cell.error || (cell.value ?? "")
1122
+ children: (0, import_spreadsheet5.formatCellDisplay)(cell)
1067
1123
  }
1068
1124
  ),
1125
+ 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) }),
1069
1126
  showFillHandle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1070
1127
  "div",
1071
1128
  {