@pixerate/editor-react 0.10.1 → 0.10.3

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.
package/dist/index.cjs CHANGED
@@ -418,12 +418,14 @@ function useDismissGuard({
418
418
  var import_react6 = require("react");
419
419
  var import_editor3 = require("@pixerate/editor");
420
420
  function useHistory(managerOrOptions) {
421
- const manager = (0, import_react6.useMemo)(() => {
422
- if (managerOrOptions instanceof import_editor3.HistoryManager) {
423
- return managerOrOptions;
424
- }
425
- return (0, import_editor3.createHistoryManager)(managerOrOptions);
426
- }, [managerOrOptions]);
421
+ const ownManagerRef = (0, import_react6.useRef)(null);
422
+ let manager;
423
+ if (managerOrOptions instanceof import_editor3.HistoryManager) {
424
+ manager = managerOrOptions;
425
+ } else {
426
+ ownManagerRef.current ??= (0, import_editor3.createHistoryManager)(managerOrOptions);
427
+ manager = ownManagerRef.current;
428
+ }
427
429
  const state = (0, import_react6.useSyncExternalStore)(
428
430
  (0, import_react6.useCallback)(
429
431
  (onStoreChange) => manager.subscribe(onStoreChange),
@@ -645,36 +647,32 @@ var import_editor7 = require("@pixerate/editor");
645
647
  var import_react11 = require("react");
646
648
  var import_editor5 = require("@pixerate/editor");
647
649
  function useSpreadsheetEditor(options = {}) {
648
- const [doc, setDoc] = (0, import_react11.useState)(() => ({
649
- id: options.document?.id || `sheet_${Date.now()}`,
650
- name: options.document?.name || "Sheet 1",
651
- columns: options.document?.columns || [],
652
- rows: options.document?.rows || [],
653
- cells: options.document?.cells || {},
654
- createdAt: options.document?.createdAt || Date.now(),
655
- updatedAt: options.document?.updatedAt || Date.now()
656
- }));
657
- const [activeCell, setActiveCell] = (0, import_react11.useState)(null);
658
- const [selectedRange, setSelectedRange] = (0, import_react11.useState)(null);
659
- const [editingCell, setEditingCell] = (0, import_react11.useState)(null);
660
- const [draftValue, setDraftValue] = (0, import_react11.useState)("");
661
- const controller = (0, import_react11.useMemo)(() => {
662
- return new import_editor5.SpreadsheetController({
650
+ const optionsRef = (0, import_react11.useRef)(options);
651
+ optionsRef.current = options;
652
+ const settersRef = (0, import_react11.useRef)({});
653
+ const [controller] = (0, import_react11.useState)(
654
+ () => new import_editor5.SpreadsheetController({
663
655
  ...options,
664
656
  onDocumentChange: (updated) => {
665
- setDoc({ ...updated });
666
- options.onDocumentChange?.(updated);
657
+ settersRef.current.setDoc?.({ ...updated });
658
+ optionsRef.current.onDocumentChange?.(updated);
667
659
  },
668
660
  onSelectionChange: (active, range) => {
669
- setActiveCell(active);
670
- setSelectedRange(range);
671
- options.onSelectionChange?.(active, range);
661
+ settersRef.current.setActiveCell?.(active);
662
+ settersRef.current.setSelectedRange?.(range);
663
+ optionsRef.current.onSelectionChange?.(active, range);
672
664
  },
673
665
  onCellCommit: (rowId, colId, raw, value) => {
674
- options.onCellCommit?.(rowId, colId, raw, value);
666
+ optionsRef.current.onCellCommit?.(rowId, colId, raw, value);
675
667
  }
676
- });
677
- }, []);
668
+ })
669
+ );
670
+ const [doc, setDoc] = (0, import_react11.useState)(() => ({ ...controller.document }));
671
+ const [activeCell, setActiveCell] = (0, import_react11.useState)(controller.activeCell);
672
+ const [selectedRange, setSelectedRange] = (0, import_react11.useState)(controller.selectedRange);
673
+ const [editingCell, setEditingCell] = (0, import_react11.useState)(null);
674
+ const [draftValue, setDraftValue] = (0, import_react11.useState)("");
675
+ settersRef.current = { setDoc, setActiveCell, setSelectedRange };
678
676
  const setCellValue = (0, import_react11.useCallback)((rowId, colId, raw) => {
679
677
  controller.setCellValue(rowId, colId, raw);
680
678
  setDoc({ ...controller.document });
@@ -792,7 +790,7 @@ function useSpreadsheetEditor(options = {}) {
792
790
  registerDataSource,
793
791
  setPrimaryDataSource,
794
792
  recalculateAll,
795
- exportToTsv: () => controller.exportToTsv(),
793
+ exportToTsv: (range) => controller.exportToTsv(range),
796
794
  importFromTsv: (data) => {
797
795
  controller.importFromTsv(data);
798
796
  setDoc({ ...controller.document });
@@ -833,6 +831,25 @@ var SpreadsheetEditor = ({
833
831
  const colName = (0, import_editor6.indexToColName)(state.activeCell.col);
834
832
  return `${colName}${state.activeCell.row + 1}`;
835
833
  }, [state.activeCell]);
834
+ const [formulaDraft, setFormulaDraft] = (0, import_react12.useState)(null);
835
+ const activeColumn = state.activeCell ? state.document.columns[state.activeCell.col] : void 0;
836
+ const formulaBarReadOnly = readOnly || !state.activeCell || !!activeColumn?.readOnly;
837
+ (0, import_react12.useEffect)(() => {
838
+ setFormulaDraft(null);
839
+ }, [state.activeCell?.row, state.activeCell?.col]);
840
+ const commitFormulaBar = () => {
841
+ if (state.editingCell) {
842
+ state.commitEditing();
843
+ return;
844
+ }
845
+ if (formulaDraft === null || !state.activeCell) return;
846
+ const row = state.document.rows[state.activeCell.row];
847
+ const col = state.document.columns[state.activeCell.col];
848
+ if (row && col && !formulaBarReadOnly && formulaDraft !== activeRawValue) {
849
+ state.setCellValue(row.id, col.id, formulaDraft);
850
+ }
851
+ setFormulaDraft(null);
852
+ };
836
853
  const handleCellClick = (0, import_react12.useCallback)((rIdx, cIdx, e) => {
837
854
  if (state.editingCell && (state.editingCell.row !== rIdx || state.editingCell.col !== cIdx)) {
838
855
  state.commitEditing();
@@ -891,16 +908,19 @@ var SpreadsheetEditor = ({
891
908
  e.preventDefault();
892
909
  state.setCellValue(row.id, col.id, "");
893
910
  }
894
- } else if ((e.metaKey || e.ctrlKey) && e.key === "z") {
911
+ } else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "z") {
895
912
  e.preventDefault();
896
913
  if (e.shiftKey) {
897
914
  state.redo();
898
915
  } else {
899
916
  state.undo();
900
917
  }
901
- } else if ((e.metaKey || e.ctrlKey) && e.key === "c") {
918
+ } else if (e.ctrlKey && !e.metaKey && e.key.toLowerCase() === "y") {
902
919
  e.preventDefault();
903
- const tsv = state.exportToTsv();
920
+ state.redo();
921
+ } else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "c") {
922
+ e.preventDefault();
923
+ const tsv = state.exportToTsv(state.selectedRange);
904
924
  navigator.clipboard?.writeText(tsv);
905
925
  } else if (!readOnly && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
906
926
  const col = state.document.columns[state.activeCell.col];
@@ -909,6 +929,10 @@ var SpreadsheetEditor = ({
909
929
  }
910
930
  }
911
931
  }, [readOnly, state]);
932
+ const stateRef = (0, import_react12.useRef)(state);
933
+ stateRef.current = state;
934
+ const onColumnResizeRef = (0, import_react12.useRef)(onColumnResize);
935
+ onColumnResizeRef.current = onColumnResize;
912
936
  (0, import_react12.useEffect)(() => {
913
937
  if (!resizingColId) return;
914
938
  if (typeof document !== "undefined") {
@@ -927,7 +951,7 @@ var SpreadsheetEditor = ({
927
951
  if (rafId === null) {
928
952
  rafId = requestAnimationFrame(() => {
929
953
  rafId = null;
930
- state.setColumnWidth(resizingColId, pendingWidth);
954
+ stateRef.current.setColumnWidth(resizingColId, pendingWidth);
931
955
  });
932
956
  }
933
957
  };
@@ -937,11 +961,8 @@ var SpreadsheetEditor = ({
937
961
  rafId = null;
938
962
  }
939
963
  if (hasDragged) {
940
- state.setColumnWidth(resizingColId, pendingWidth);
941
- const col = state.document.columns.find((c) => c.id === resizingColId);
942
- if (col && onColumnResize) {
943
- onColumnResize(col.id, col.width || pendingWidth);
944
- }
964
+ stateRef.current.setColumnWidth(resizingColId, pendingWidth);
965
+ onColumnResizeRef.current?.(resizingColId, pendingWidth);
945
966
  }
946
967
  setResizingColId(null);
947
968
  if (typeof document !== "undefined") {
@@ -962,7 +983,7 @@ var SpreadsheetEditor = ({
962
983
  window.removeEventListener("mousemove", handleMouseMove);
963
984
  window.removeEventListener("mouseup", handleMouseUp);
964
985
  };
965
- }, [resizingColId, resizeStartX, resizeStartWidth, state, onColumnResize]);
986
+ }, [resizingColId, resizeStartX, resizeStartWidth]);
966
987
  const measureElementContentWidth = (0, import_react12.useCallback)((el, container) => {
967
988
  if (typeof document === "undefined") return 0;
968
989
  let maxW = el.scrollWidth || 0;
@@ -1094,25 +1115,29 @@ var SpreadsheetEditor = ({
1094
1115
  type: "text",
1095
1116
  className: "flex-1 bg-transparent px-2 py-0.5 outline-none font-sans text-xs text-foreground placeholder:text-muted-foreground/60 focus:bg-background focus:ring-1 focus:ring-primary rounded",
1096
1117
  placeholder: "Enter a value or formula (e.g. =SUM(A1:A5))",
1097
- value: state.editingCell ? state.draftValue : activeRawValue,
1118
+ "aria-label": "Formula bar",
1119
+ readOnly: formulaBarReadOnly,
1120
+ value: state.editingCell ? state.draftValue : formulaDraft ?? activeRawValue,
1121
+ onFocus: () => {
1122
+ if (!state.editingCell) setFormulaDraft(activeRawValue);
1123
+ },
1098
1124
  onChange: (e) => {
1099
- if (state.activeCell) {
1125
+ if (state.editingCell) {
1100
1126
  state.setDraftValue(e.target.value);
1127
+ } else if (state.activeCell) {
1128
+ setFormulaDraft(e.target.value);
1101
1129
  }
1102
1130
  },
1131
+ onBlur: () => commitFormulaBar(),
1103
1132
  onKeyDown: (e) => {
1133
+ e.stopPropagation();
1104
1134
  if (e.key === "Enter") {
1105
1135
  e.preventDefault();
1106
- if (state.activeCell) {
1107
- const row = state.document.rows[state.activeCell.row];
1108
- const col = state.document.columns[state.activeCell.col];
1109
- if (row && col) {
1110
- state.setCellValue(row.id, col.id, e.target.value);
1111
- }
1112
- }
1136
+ commitFormulaBar();
1113
1137
  } else if (e.key === "Escape") {
1114
1138
  e.preventDefault();
1115
- state.cancelEditing();
1139
+ setFormulaDraft(null);
1140
+ if (state.editingCell) state.cancelEditing();
1116
1141
  }
1117
1142
  }
1118
1143
  }
@@ -1287,11 +1312,14 @@ function useImageEditor(options = {}) {
1287
1312
  });
1288
1313
  return unsub;
1289
1314
  }, [controller]);
1315
+ const loadedImageRef = (0, import_react14.useRef)(options.image);
1290
1316
  (0, import_react14.useEffect)(() => {
1291
- if (options.image && options.image !== state.sourceUrl) {
1317
+ if (options.image === loadedImageRef.current) return;
1318
+ loadedImageRef.current = options.image;
1319
+ if (options.image) {
1292
1320
  controller.loadImage(options.image);
1293
1321
  }
1294
- }, [options.image, controller, state.sourceUrl]);
1322
+ }, [options.image, controller]);
1295
1323
  const setTool = (0, import_react14.useCallback)((tool) => controller.setTool(tool), [controller]);
1296
1324
  const setCrop = (0, import_react14.useCallback)(
1297
1325
  (crop, addToHistory = true) => controller.setCrop(crop, addToHistory),