@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.d.cts CHANGED
@@ -137,7 +137,8 @@ interface UseHistoryResult extends HistoryState {
137
137
  }
138
138
  /**
139
139
  * React hook that subscribes to a HistoryManager instance.
140
- * Accepts an existing HistoryManager or config options to create an instance.
140
+ * Accepts an existing HistoryManager or config options to create an instance
141
+ * (options are read on first render only).
141
142
  */
142
143
  declare function useHistory(managerOrOptions?: HistoryManager | HistoryManagerOptions): UseHistoryResult;
143
144
 
@@ -223,7 +224,7 @@ declare function useSpreadsheetEditor(options?: SpreadsheetControllerOptions): {
223
224
  registerDataSource: (ds: SpreadsheetDataSource) => void;
224
225
  setPrimaryDataSource: (id: string | undefined) => void;
225
226
  recalculateAll: () => void;
226
- exportToTsv: () => string;
227
+ exportToTsv: (range?: CellRange | null) => string;
227
228
  importFromTsv: (data: string) => void;
228
229
  undo: () => void;
229
230
  redo: () => void;
package/dist/index.d.ts CHANGED
@@ -137,7 +137,8 @@ interface UseHistoryResult extends HistoryState {
137
137
  }
138
138
  /**
139
139
  * React hook that subscribes to a HistoryManager instance.
140
- * Accepts an existing HistoryManager or config options to create an instance.
140
+ * Accepts an existing HistoryManager or config options to create an instance
141
+ * (options are read on first render only).
141
142
  */
142
143
  declare function useHistory(managerOrOptions?: HistoryManager | HistoryManagerOptions): UseHistoryResult;
143
144
 
@@ -223,7 +224,7 @@ declare function useSpreadsheetEditor(options?: SpreadsheetControllerOptions): {
223
224
  registerDataSource: (ds: SpreadsheetDataSource) => void;
224
225
  setPrimaryDataSource: (id: string | undefined) => void;
225
226
  recalculateAll: () => void;
226
- exportToTsv: () => string;
227
+ exportToTsv: (range?: CellRange | null) => string;
227
228
  importFromTsv: (data: string) => void;
228
229
  undo: () => void;
229
230
  redo: () => void;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ImageEditor,
3
3
  useImageEditor
4
- } from "./chunk-UYT5EQU6.js";
4
+ } from "./chunk-H7HCHLZU.js";
5
5
 
6
6
  // src/hooks/usePromptEditor.ts
7
7
  import { useMemo, useEffect, useRef } from "react";
@@ -373,18 +373,20 @@ function useDismissGuard({
373
373
  }
374
374
 
375
375
  // src/hooks/useHistory.ts
376
- import { useSyncExternalStore, useMemo as useMemo2, useCallback as useCallback3 } from "react";
376
+ import { useSyncExternalStore, useRef as useRef4, useCallback as useCallback3 } from "react";
377
377
  import {
378
378
  HistoryManager,
379
379
  createHistoryManager
380
380
  } from "@pixerate/editor";
381
381
  function useHistory(managerOrOptions) {
382
- const manager = useMemo2(() => {
383
- if (managerOrOptions instanceof HistoryManager) {
384
- return managerOrOptions;
385
- }
386
- return createHistoryManager(managerOrOptions);
387
- }, [managerOrOptions]);
382
+ const ownManagerRef = useRef4(null);
383
+ let manager;
384
+ if (managerOrOptions instanceof HistoryManager) {
385
+ manager = managerOrOptions;
386
+ } else {
387
+ ownManagerRef.current ??= createHistoryManager(managerOrOptions);
388
+ manager = ownManagerRef.current;
389
+ }
388
390
  const state = useSyncExternalStore(
389
391
  useCallback3(
390
392
  (onStoreChange) => manager.subscribe(onStoreChange),
@@ -485,7 +487,7 @@ var EditorContent = ({
485
487
  };
486
488
 
487
489
  // src/components/BubbleMenu.tsx
488
- import { useEffect as useEffect4, useRef as useRef4, useState as useState3 } from "react";
490
+ import { useEffect as useEffect4, useRef as useRef5, useState as useState3 } from "react";
489
491
  import { createPortal } from "react-dom";
490
492
  import {
491
493
  BubbleMenuPlugin
@@ -499,7 +501,7 @@ var BubbleMenu = ({
499
501
  pluginKey = "bubbleMenu",
500
502
  updateDelay
501
503
  }) => {
502
- const menuEl = useRef4(null);
504
+ const menuEl = useRef5(null);
503
505
  const [mounted, setMounted] = useState3(false);
504
506
  if (!menuEl.current && typeof document !== "undefined") {
505
507
  menuEl.current = document.createElement("div");
@@ -535,7 +537,7 @@ var BubbleMenu = ({
535
537
  };
536
538
 
537
539
  // src/components/TemplateRenderer.tsx
538
- import { useMemo as useMemo3 } from "react";
540
+ import { useMemo as useMemo2 } from "react";
539
541
  import {
540
542
  tokenizePrompt as tokenizePrompt2,
541
543
  resolveTemplates
@@ -550,11 +552,11 @@ var TemplateRenderer = ({
550
552
  onTemplateClick,
551
553
  onVariableClick
552
554
  }) => {
553
- const displayText = useMemo3(() => {
555
+ const displayText = useMemo2(() => {
554
556
  if (!resolve || templates.length === 0) return content;
555
557
  return resolveTemplates(content, templates);
556
558
  }, [content, templates, resolve]);
557
- const tokens = useMemo3(() => {
559
+ const tokens = useMemo2(() => {
558
560
  return tokenizePrompt2(displayText);
559
561
  }, [displayText]);
560
562
  return /* @__PURE__ */ jsx2("div", { className, children: tokens.map((token, index) => {
@@ -616,41 +618,37 @@ import {
616
618
  } from "@pixerate/editor";
617
619
 
618
620
  // src/spreadsheet/useSpreadsheetEditor.ts
619
- import { useState as useState4, useMemo as useMemo4, useCallback as useCallback4 } from "react";
621
+ import { useState as useState4, useRef as useRef6, useCallback as useCallback4 } from "react";
620
622
  import {
621
623
  SpreadsheetController
622
624
  } from "@pixerate/editor";
623
625
  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({
626
+ const optionsRef = useRef6(options);
627
+ optionsRef.current = options;
628
+ const settersRef = useRef6({});
629
+ const [controller] = useState4(
630
+ () => new SpreadsheetController({
639
631
  ...options,
640
632
  onDocumentChange: (updated) => {
641
- setDoc({ ...updated });
642
- options.onDocumentChange?.(updated);
633
+ settersRef.current.setDoc?.({ ...updated });
634
+ optionsRef.current.onDocumentChange?.(updated);
643
635
  },
644
636
  onSelectionChange: (active, range) => {
645
- setActiveCell(active);
646
- setSelectedRange(range);
647
- options.onSelectionChange?.(active, range);
637
+ settersRef.current.setActiveCell?.(active);
638
+ settersRef.current.setSelectedRange?.(range);
639
+ optionsRef.current.onSelectionChange?.(active, range);
648
640
  },
649
641
  onCellCommit: (rowId, colId, raw, value) => {
650
- options.onCellCommit?.(rowId, colId, raw, value);
642
+ optionsRef.current.onCellCommit?.(rowId, colId, raw, value);
651
643
  }
652
- });
653
- }, []);
644
+ })
645
+ );
646
+ const [doc, setDoc] = useState4(() => ({ ...controller.document }));
647
+ const [activeCell, setActiveCell] = useState4(controller.activeCell);
648
+ const [selectedRange, setSelectedRange] = useState4(controller.selectedRange);
649
+ const [editingCell, setEditingCell] = useState4(null);
650
+ const [draftValue, setDraftValue] = useState4("");
651
+ settersRef.current = { setDoc, setActiveCell, setSelectedRange };
654
652
  const setCellValue = useCallback4((rowId, colId, raw) => {
655
653
  controller.setCellValue(rowId, colId, raw);
656
654
  setDoc({ ...controller.document });
@@ -768,7 +766,7 @@ function useSpreadsheetEditor(options = {}) {
768
766
  registerDataSource,
769
767
  setPrimaryDataSource,
770
768
  recalculateAll,
771
- exportToTsv: () => controller.exportToTsv(),
769
+ exportToTsv: (range) => controller.exportToTsv(range),
772
770
  importFromTsv: (data) => {
773
771
  controller.importFromTsv(data);
774
772
  setDoc({ ...controller.document });
@@ -779,7 +777,7 @@ function useSpreadsheetEditor(options = {}) {
779
777
  }
780
778
 
781
779
  // src/spreadsheet/SpreadsheetEditor.tsx
782
- import { useRef as useRef5, useState as useState5, useMemo as useMemo5, useEffect as useEffect5, useCallback as useCallback5 } from "react";
780
+ import { useRef as useRef7, useState as useState5, useMemo as useMemo3, useEffect as useEffect5, useCallback as useCallback5 } from "react";
783
781
  import {
784
782
  indexToColName
785
783
  } from "@pixerate/editor";
@@ -794,23 +792,42 @@ var SpreadsheetEditor = ({
794
792
  }) => {
795
793
  const defaultState = useSpreadsheetEditor();
796
794
  const state = externalState || defaultState;
797
- const gridRef = useRef5(null);
795
+ const gridRef = useRef7(null);
798
796
  const [resizingColId, setResizingColId] = useState5(null);
799
797
  const [hoveredColId, setHoveredColId] = useState5(null);
800
798
  const [resizeStartX, setResizeStartX] = useState5(0);
801
799
  const [resizeStartWidth, setResizeStartWidth] = useState5(0);
802
- const activeRawValue = useMemo5(() => {
800
+ const activeRawValue = useMemo3(() => {
803
801
  if (!state.activeCell) return "";
804
802
  const row = state.document.rows[state.activeCell.row];
805
803
  const col = state.document.columns[state.activeCell.col];
806
804
  if (!row || !col) return "";
807
805
  return state.getCell(row.id, col.id).raw;
808
806
  }, [state.activeCell, state.document]);
809
- const coordLabel = useMemo5(() => {
807
+ const coordLabel = useMemo3(() => {
810
808
  if (!state.activeCell) return "--";
811
809
  const colName = indexToColName(state.activeCell.col);
812
810
  return `${colName}${state.activeCell.row + 1}`;
813
811
  }, [state.activeCell]);
812
+ const [formulaDraft, setFormulaDraft] = useState5(null);
813
+ const activeColumn = state.activeCell ? state.document.columns[state.activeCell.col] : void 0;
814
+ const formulaBarReadOnly = readOnly || !state.activeCell || !!activeColumn?.readOnly;
815
+ useEffect5(() => {
816
+ setFormulaDraft(null);
817
+ }, [state.activeCell?.row, state.activeCell?.col]);
818
+ const commitFormulaBar = () => {
819
+ if (state.editingCell) {
820
+ state.commitEditing();
821
+ return;
822
+ }
823
+ if (formulaDraft === null || !state.activeCell) return;
824
+ const row = state.document.rows[state.activeCell.row];
825
+ const col = state.document.columns[state.activeCell.col];
826
+ if (row && col && !formulaBarReadOnly && formulaDraft !== activeRawValue) {
827
+ state.setCellValue(row.id, col.id, formulaDraft);
828
+ }
829
+ setFormulaDraft(null);
830
+ };
814
831
  const handleCellClick = useCallback5((rIdx, cIdx, e) => {
815
832
  if (state.editingCell && (state.editingCell.row !== rIdx || state.editingCell.col !== cIdx)) {
816
833
  state.commitEditing();
@@ -869,16 +886,19 @@ var SpreadsheetEditor = ({
869
886
  e.preventDefault();
870
887
  state.setCellValue(row.id, col.id, "");
871
888
  }
872
- } else if ((e.metaKey || e.ctrlKey) && e.key === "z") {
889
+ } else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "z") {
873
890
  e.preventDefault();
874
891
  if (e.shiftKey) {
875
892
  state.redo();
876
893
  } else {
877
894
  state.undo();
878
895
  }
879
- } else if ((e.metaKey || e.ctrlKey) && e.key === "c") {
896
+ } else if (e.ctrlKey && !e.metaKey && e.key.toLowerCase() === "y") {
897
+ e.preventDefault();
898
+ state.redo();
899
+ } else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "c") {
880
900
  e.preventDefault();
881
- const tsv = state.exportToTsv();
901
+ const tsv = state.exportToTsv(state.selectedRange);
882
902
  navigator.clipboard?.writeText(tsv);
883
903
  } else if (!readOnly && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
884
904
  const col = state.document.columns[state.activeCell.col];
@@ -887,6 +907,10 @@ var SpreadsheetEditor = ({
887
907
  }
888
908
  }
889
909
  }, [readOnly, state]);
910
+ const stateRef = useRef7(state);
911
+ stateRef.current = state;
912
+ const onColumnResizeRef = useRef7(onColumnResize);
913
+ onColumnResizeRef.current = onColumnResize;
890
914
  useEffect5(() => {
891
915
  if (!resizingColId) return;
892
916
  if (typeof document !== "undefined") {
@@ -905,7 +929,7 @@ var SpreadsheetEditor = ({
905
929
  if (rafId === null) {
906
930
  rafId = requestAnimationFrame(() => {
907
931
  rafId = null;
908
- state.setColumnWidth(resizingColId, pendingWidth);
932
+ stateRef.current.setColumnWidth(resizingColId, pendingWidth);
909
933
  });
910
934
  }
911
935
  };
@@ -915,11 +939,8 @@ var SpreadsheetEditor = ({
915
939
  rafId = null;
916
940
  }
917
941
  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
- }
942
+ stateRef.current.setColumnWidth(resizingColId, pendingWidth);
943
+ onColumnResizeRef.current?.(resizingColId, pendingWidth);
923
944
  }
924
945
  setResizingColId(null);
925
946
  if (typeof document !== "undefined") {
@@ -940,7 +961,7 @@ var SpreadsheetEditor = ({
940
961
  window.removeEventListener("mousemove", handleMouseMove);
941
962
  window.removeEventListener("mouseup", handleMouseUp);
942
963
  };
943
- }, [resizingColId, resizeStartX, resizeStartWidth, state, onColumnResize]);
964
+ }, [resizingColId, resizeStartX, resizeStartWidth]);
944
965
  const measureElementContentWidth = useCallback5((el, container) => {
945
966
  if (typeof document === "undefined") return 0;
946
967
  let maxW = el.scrollWidth || 0;
@@ -1072,25 +1093,29 @@ var SpreadsheetEditor = ({
1072
1093
  type: "text",
1073
1094
  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
1095
  placeholder: "Enter a value or formula (e.g. =SUM(A1:A5))",
1075
- value: state.editingCell ? state.draftValue : activeRawValue,
1096
+ "aria-label": "Formula bar",
1097
+ readOnly: formulaBarReadOnly,
1098
+ value: state.editingCell ? state.draftValue : formulaDraft ?? activeRawValue,
1099
+ onFocus: () => {
1100
+ if (!state.editingCell) setFormulaDraft(activeRawValue);
1101
+ },
1076
1102
  onChange: (e) => {
1077
- if (state.activeCell) {
1103
+ if (state.editingCell) {
1078
1104
  state.setDraftValue(e.target.value);
1105
+ } else if (state.activeCell) {
1106
+ setFormulaDraft(e.target.value);
1079
1107
  }
1080
1108
  },
1109
+ onBlur: () => commitFormulaBar(),
1081
1110
  onKeyDown: (e) => {
1111
+ e.stopPropagation();
1082
1112
  if (e.key === "Enter") {
1083
1113
  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
- }
1114
+ commitFormulaBar();
1091
1115
  } else if (e.key === "Escape") {
1092
1116
  e.preventDefault();
1093
- state.cancelEditing();
1117
+ setFormulaDraft(null);
1118
+ if (state.editingCell) state.cancelEditing();
1094
1119
  }
1095
1120
  }
1096
1121
  }
@@ -1216,7 +1241,7 @@ var SpreadsheetEditor = ({
1216
1241
  };
1217
1242
 
1218
1243
  // src/slots.tsx
1219
- import { createContext, useContext, useMemo as useMemo6 } from "react";
1244
+ import { createContext, useContext, useMemo as useMemo4 } from "react";
1220
1245
  import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1221
1246
  var defaultSlots = {
1222
1247
  Button: (props) => /* @__PURE__ */ jsx4(
@@ -1240,7 +1265,7 @@ var defaultSlots = {
1240
1265
  };
1241
1266
  var EditorSlotsContext = createContext(defaultSlots);
1242
1267
  function EditorSlotsProvider({ slots, children }) {
1243
- const mergedSlots = useMemo6(() => ({ ...defaultSlots, ...slots }), [slots]);
1268
+ const mergedSlots = useMemo4(() => ({ ...defaultSlots, ...slots }), [slots]);
1244
1269
  return /* @__PURE__ */ jsx4(EditorSlotsContext.Provider, { value: mergedSlots, children });
1245
1270
  }
1246
1271
  function useEditorSlots() {