@pixerate/editor-react 0.10.1 → 0.10.2

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.d.cts CHANGED
@@ -223,7 +223,7 @@ declare function useSpreadsheetEditor(options?: SpreadsheetControllerOptions): {
223
223
  registerDataSource: (ds: SpreadsheetDataSource) => void;
224
224
  setPrimaryDataSource: (id: string | undefined) => void;
225
225
  recalculateAll: () => void;
226
- exportToTsv: () => string;
226
+ exportToTsv: (range?: CellRange | null) => string;
227
227
  importFromTsv: (data: string) => void;
228
228
  undo: () => void;
229
229
  redo: () => void;
package/dist/index.d.ts CHANGED
@@ -223,7 +223,7 @@ declare function useSpreadsheetEditor(options?: SpreadsheetControllerOptions): {
223
223
  registerDataSource: (ds: SpreadsheetDataSource) => void;
224
224
  setPrimaryDataSource: (id: string | undefined) => void;
225
225
  recalculateAll: () => void;
226
- exportToTsv: () => string;
226
+ exportToTsv: (range?: CellRange | null) => string;
227
227
  importFromTsv: (data: string) => void;
228
228
  undo: () => void;
229
229
  redo: () => void;
package/dist/index.js CHANGED
@@ -616,41 +616,37 @@ import {
616
616
  } from "@pixerate/editor";
617
617
 
618
618
  // src/spreadsheet/useSpreadsheetEditor.ts
619
- import { useState as useState4, useMemo as useMemo4, useCallback as useCallback4 } from "react";
619
+ import { useState as useState4, useRef as useRef5, useCallback as useCallback4 } from "react";
620
620
  import {
621
621
  SpreadsheetController
622
622
  } from "@pixerate/editor";
623
623
  function useSpreadsheetEditor(options = {}) {
624
- const [doc, setDoc] = useState4(() => ({
625
- id: options.document?.id || `sheet_${Date.now()}`,
626
- name: options.document?.name || "Sheet 1",
627
- columns: options.document?.columns || [],
628
- rows: options.document?.rows || [],
629
- cells: options.document?.cells || {},
630
- createdAt: options.document?.createdAt || Date.now(),
631
- updatedAt: options.document?.updatedAt || Date.now()
632
- }));
633
- const [activeCell, setActiveCell] = useState4(null);
634
- const [selectedRange, setSelectedRange] = useState4(null);
635
- const [editingCell, setEditingCell] = useState4(null);
636
- const [draftValue, setDraftValue] = useState4("");
637
- const controller = useMemo4(() => {
638
- return new SpreadsheetController({
624
+ const optionsRef = useRef5(options);
625
+ optionsRef.current = options;
626
+ const settersRef = useRef5({});
627
+ const [controller] = useState4(
628
+ () => new SpreadsheetController({
639
629
  ...options,
640
630
  onDocumentChange: (updated) => {
641
- setDoc({ ...updated });
642
- options.onDocumentChange?.(updated);
631
+ settersRef.current.setDoc?.({ ...updated });
632
+ optionsRef.current.onDocumentChange?.(updated);
643
633
  },
644
634
  onSelectionChange: (active, range) => {
645
- setActiveCell(active);
646
- setSelectedRange(range);
647
- options.onSelectionChange?.(active, range);
635
+ settersRef.current.setActiveCell?.(active);
636
+ settersRef.current.setSelectedRange?.(range);
637
+ optionsRef.current.onSelectionChange?.(active, range);
648
638
  },
649
639
  onCellCommit: (rowId, colId, raw, value) => {
650
- options.onCellCommit?.(rowId, colId, raw, value);
640
+ optionsRef.current.onCellCommit?.(rowId, colId, raw, value);
651
641
  }
652
- });
653
- }, []);
642
+ })
643
+ );
644
+ const [doc, setDoc] = useState4(() => ({ ...controller.document }));
645
+ const [activeCell, setActiveCell] = useState4(controller.activeCell);
646
+ const [selectedRange, setSelectedRange] = useState4(controller.selectedRange);
647
+ const [editingCell, setEditingCell] = useState4(null);
648
+ const [draftValue, setDraftValue] = useState4("");
649
+ settersRef.current = { setDoc, setActiveCell, setSelectedRange };
654
650
  const setCellValue = useCallback4((rowId, colId, raw) => {
655
651
  controller.setCellValue(rowId, colId, raw);
656
652
  setDoc({ ...controller.document });
@@ -768,7 +764,7 @@ function useSpreadsheetEditor(options = {}) {
768
764
  registerDataSource,
769
765
  setPrimaryDataSource,
770
766
  recalculateAll,
771
- exportToTsv: () => controller.exportToTsv(),
767
+ exportToTsv: (range) => controller.exportToTsv(range),
772
768
  importFromTsv: (data) => {
773
769
  controller.importFromTsv(data);
774
770
  setDoc({ ...controller.document });
@@ -779,7 +775,7 @@ function useSpreadsheetEditor(options = {}) {
779
775
  }
780
776
 
781
777
  // src/spreadsheet/SpreadsheetEditor.tsx
782
- import { useRef as useRef5, useState as useState5, useMemo as useMemo5, useEffect as useEffect5, useCallback as useCallback5 } from "react";
778
+ import { useRef as useRef6, useState as useState5, useMemo as useMemo4, useEffect as useEffect5, useCallback as useCallback5 } from "react";
783
779
  import {
784
780
  indexToColName
785
781
  } from "@pixerate/editor";
@@ -794,23 +790,42 @@ var SpreadsheetEditor = ({
794
790
  }) => {
795
791
  const defaultState = useSpreadsheetEditor();
796
792
  const state = externalState || defaultState;
797
- const gridRef = useRef5(null);
793
+ const gridRef = useRef6(null);
798
794
  const [resizingColId, setResizingColId] = useState5(null);
799
795
  const [hoveredColId, setHoveredColId] = useState5(null);
800
796
  const [resizeStartX, setResizeStartX] = useState5(0);
801
797
  const [resizeStartWidth, setResizeStartWidth] = useState5(0);
802
- const activeRawValue = useMemo5(() => {
798
+ const activeRawValue = useMemo4(() => {
803
799
  if (!state.activeCell) return "";
804
800
  const row = state.document.rows[state.activeCell.row];
805
801
  const col = state.document.columns[state.activeCell.col];
806
802
  if (!row || !col) return "";
807
803
  return state.getCell(row.id, col.id).raw;
808
804
  }, [state.activeCell, state.document]);
809
- const coordLabel = useMemo5(() => {
805
+ const coordLabel = useMemo4(() => {
810
806
  if (!state.activeCell) return "--";
811
807
  const colName = indexToColName(state.activeCell.col);
812
808
  return `${colName}${state.activeCell.row + 1}`;
813
809
  }, [state.activeCell]);
810
+ const [formulaDraft, setFormulaDraft] = useState5(null);
811
+ const activeColumn = state.activeCell ? state.document.columns[state.activeCell.col] : void 0;
812
+ const formulaBarReadOnly = readOnly || !state.activeCell || !!activeColumn?.readOnly;
813
+ useEffect5(() => {
814
+ setFormulaDraft(null);
815
+ }, [state.activeCell?.row, state.activeCell?.col]);
816
+ const commitFormulaBar = () => {
817
+ if (state.editingCell) {
818
+ state.commitEditing();
819
+ return;
820
+ }
821
+ if (formulaDraft === null || !state.activeCell) return;
822
+ const row = state.document.rows[state.activeCell.row];
823
+ const col = state.document.columns[state.activeCell.col];
824
+ if (row && col && !formulaBarReadOnly && formulaDraft !== activeRawValue) {
825
+ state.setCellValue(row.id, col.id, formulaDraft);
826
+ }
827
+ setFormulaDraft(null);
828
+ };
814
829
  const handleCellClick = useCallback5((rIdx, cIdx, e) => {
815
830
  if (state.editingCell && (state.editingCell.row !== rIdx || state.editingCell.col !== cIdx)) {
816
831
  state.commitEditing();
@@ -869,16 +884,19 @@ var SpreadsheetEditor = ({
869
884
  e.preventDefault();
870
885
  state.setCellValue(row.id, col.id, "");
871
886
  }
872
- } else if ((e.metaKey || e.ctrlKey) && e.key === "z") {
887
+ } else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "z") {
873
888
  e.preventDefault();
874
889
  if (e.shiftKey) {
875
890
  state.redo();
876
891
  } else {
877
892
  state.undo();
878
893
  }
879
- } else if ((e.metaKey || e.ctrlKey) && e.key === "c") {
894
+ } else if (e.ctrlKey && !e.metaKey && e.key.toLowerCase() === "y") {
895
+ e.preventDefault();
896
+ state.redo();
897
+ } else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "c") {
880
898
  e.preventDefault();
881
- const tsv = state.exportToTsv();
899
+ const tsv = state.exportToTsv(state.selectedRange);
882
900
  navigator.clipboard?.writeText(tsv);
883
901
  } else if (!readOnly && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
884
902
  const col = state.document.columns[state.activeCell.col];
@@ -887,6 +905,10 @@ var SpreadsheetEditor = ({
887
905
  }
888
906
  }
889
907
  }, [readOnly, state]);
908
+ const stateRef = useRef6(state);
909
+ stateRef.current = state;
910
+ const onColumnResizeRef = useRef6(onColumnResize);
911
+ onColumnResizeRef.current = onColumnResize;
890
912
  useEffect5(() => {
891
913
  if (!resizingColId) return;
892
914
  if (typeof document !== "undefined") {
@@ -905,7 +927,7 @@ var SpreadsheetEditor = ({
905
927
  if (rafId === null) {
906
928
  rafId = requestAnimationFrame(() => {
907
929
  rafId = null;
908
- state.setColumnWidth(resizingColId, pendingWidth);
930
+ stateRef.current.setColumnWidth(resizingColId, pendingWidth);
909
931
  });
910
932
  }
911
933
  };
@@ -915,11 +937,8 @@ var SpreadsheetEditor = ({
915
937
  rafId = null;
916
938
  }
917
939
  if (hasDragged) {
918
- state.setColumnWidth(resizingColId, pendingWidth);
919
- const col = state.document.columns.find((c) => c.id === resizingColId);
920
- if (col && onColumnResize) {
921
- onColumnResize(col.id, col.width || pendingWidth);
922
- }
940
+ stateRef.current.setColumnWidth(resizingColId, pendingWidth);
941
+ onColumnResizeRef.current?.(resizingColId, pendingWidth);
923
942
  }
924
943
  setResizingColId(null);
925
944
  if (typeof document !== "undefined") {
@@ -940,7 +959,7 @@ var SpreadsheetEditor = ({
940
959
  window.removeEventListener("mousemove", handleMouseMove);
941
960
  window.removeEventListener("mouseup", handleMouseUp);
942
961
  };
943
- }, [resizingColId, resizeStartX, resizeStartWidth, state, onColumnResize]);
962
+ }, [resizingColId, resizeStartX, resizeStartWidth]);
944
963
  const measureElementContentWidth = useCallback5((el, container) => {
945
964
  if (typeof document === "undefined") return 0;
946
965
  let maxW = el.scrollWidth || 0;
@@ -1072,25 +1091,29 @@ var SpreadsheetEditor = ({
1072
1091
  type: "text",
1073
1092
  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",
1074
1093
  placeholder: "Enter a value or formula (e.g. =SUM(A1:A5))",
1075
- value: state.editingCell ? state.draftValue : activeRawValue,
1094
+ "aria-label": "Formula bar",
1095
+ readOnly: formulaBarReadOnly,
1096
+ value: state.editingCell ? state.draftValue : formulaDraft ?? activeRawValue,
1097
+ onFocus: () => {
1098
+ if (!state.editingCell) setFormulaDraft(activeRawValue);
1099
+ },
1076
1100
  onChange: (e) => {
1077
- if (state.activeCell) {
1101
+ if (state.editingCell) {
1078
1102
  state.setDraftValue(e.target.value);
1103
+ } else if (state.activeCell) {
1104
+ setFormulaDraft(e.target.value);
1079
1105
  }
1080
1106
  },
1107
+ onBlur: () => commitFormulaBar(),
1081
1108
  onKeyDown: (e) => {
1109
+ e.stopPropagation();
1082
1110
  if (e.key === "Enter") {
1083
1111
  e.preventDefault();
1084
- if (state.activeCell) {
1085
- const row = state.document.rows[state.activeCell.row];
1086
- const col = state.document.columns[state.activeCell.col];
1087
- if (row && col) {
1088
- state.setCellValue(row.id, col.id, e.target.value);
1089
- }
1090
- }
1112
+ commitFormulaBar();
1091
1113
  } else if (e.key === "Escape") {
1092
1114
  e.preventDefault();
1093
- state.cancelEditing();
1115
+ setFormulaDraft(null);
1116
+ if (state.editingCell) state.cancelEditing();
1094
1117
  }
1095
1118
  }
1096
1119
  }
@@ -1216,7 +1239,7 @@ var SpreadsheetEditor = ({
1216
1239
  };
1217
1240
 
1218
1241
  // src/slots.tsx
1219
- import { createContext, useContext, useMemo as useMemo6 } from "react";
1242
+ import { createContext, useContext, useMemo as useMemo5 } from "react";
1220
1243
  import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1221
1244
  var defaultSlots = {
1222
1245
  Button: (props) => /* @__PURE__ */ jsx4(
@@ -1240,7 +1263,7 @@ var defaultSlots = {
1240
1263
  };
1241
1264
  var EditorSlotsContext = createContext(defaultSlots);
1242
1265
  function EditorSlotsProvider({ slots, children }) {
1243
- const mergedSlots = useMemo6(() => ({ ...defaultSlots, ...slots }), [slots]);
1266
+ const mergedSlots = useMemo5(() => ({ ...defaultSlots, ...slots }), [slots]);
1244
1267
  return /* @__PURE__ */ jsx4(EditorSlotsContext.Provider, { value: mergedSlots, children });
1245
1268
  }
1246
1269
  function useEditorSlots() {