@pixerate/editor-react 0.12.0 → 0.14.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.
package/dist/index.js CHANGED
@@ -1,3 +1,12 @@
1
+ import {
2
+ FormulaHints,
3
+ SpreadsheetContextMenu,
4
+ SpreadsheetEditor,
5
+ SpreadsheetGroupRow,
6
+ useFillHandle,
7
+ useSpreadsheetClipboard,
8
+ useSpreadsheetEditor
9
+ } from "./chunk-25S5EHH4.js";
1
10
  import {
2
11
  ImageEditor,
3
12
  useImageEditor
@@ -853,1104 +862,11 @@ import {
853
862
  createHistoryManager as createHistoryManager2
854
863
  } from "@pixerate/editor";
855
864
 
856
- // src/spreadsheet/useSpreadsheetEditor.ts
857
- import { useState as useState5, useRef as useRef7, useCallback as useCallback4, useMemo as useMemo6 } from "react";
858
- import {
859
- SpreadsheetController,
860
- bindSpreadsheetTableActions
861
- } from "@pixerate/editor";
862
- function useSpreadsheetEditor(options = {}) {
863
- const optionsRef = useRef7(options);
864
- optionsRef.current = options;
865
- const settersRef = useRef7({});
866
- const [controller] = useState5(
867
- () => new SpreadsheetController({
868
- ...options,
869
- onDocumentChange: (updated) => {
870
- settersRef.current.setDoc?.({ ...updated });
871
- optionsRef.current.onDocumentChange?.(updated);
872
- },
873
- onSelectionChange: (active, range) => {
874
- settersRef.current.setActiveCell?.(active);
875
- settersRef.current.setSelectedRange?.(range);
876
- optionsRef.current.onSelectionChange?.(active, range);
877
- },
878
- onCellCommit: (rowId, colId, raw, value) => {
879
- optionsRef.current.onCellCommit?.(rowId, colId, raw, value);
880
- }
881
- })
882
- );
883
- const [doc, setDoc] = useState5(() => ({ ...controller.document }));
884
- const [activeCell, setActiveCell] = useState5(controller.activeCell);
885
- const [selectedRange, setSelectedRange] = useState5(controller.selectedRange);
886
- const [editingCell, setEditingCell] = useState5(null);
887
- const [draftValue, setDraftValue] = useState5("");
888
- settersRef.current = { setDoc, setActiveCell, setSelectedRange };
889
- const setCellValue = useCallback4((rowId, colId, raw) => {
890
- controller.setCellValue(rowId, colId, raw);
891
- setDoc({ ...controller.document });
892
- }, [controller]);
893
- const selectCell = useCallback4((row, col, extendSelection = false) => {
894
- controller.selectCell(row, col, extendSelection);
895
- setActiveCell(controller.activeCell);
896
- setSelectedRange(controller.selectedRange);
897
- }, [controller]);
898
- const selectRange = useCallback4((range) => {
899
- controller.selectRange(range);
900
- setSelectedRange(controller.selectedRange);
901
- }, [controller]);
902
- const startEditing = useCallback4((row, col, initialVal) => {
903
- controller.startEditing(row, col, initialVal);
904
- setActiveCell(controller.activeCell);
905
- setEditingCell(controller.editingCell);
906
- setDraftValue(controller.draftValue);
907
- }, [controller]);
908
- const commitEditing = useCallback4((val) => {
909
- controller.commitEditing(val);
910
- setEditingCell(null);
911
- setDraftValue("");
912
- setDoc({ ...controller.document });
913
- }, [controller]);
914
- const cancelEditing = useCallback4(() => {
915
- controller.cancelEditing();
916
- setEditingCell(null);
917
- setDraftValue("");
918
- }, [controller]);
919
- const moveCursor = useCallback4((dir, extend = false) => {
920
- controller.moveCursor(dir, extend);
921
- setActiveCell(controller.activeCell);
922
- setSelectedRange(controller.selectedRange);
923
- }, [controller]);
924
- const insertColumn = useCallback4((index, config) => {
925
- controller.insertColumn(index, config);
926
- setDoc({ ...controller.document });
927
- }, [controller]);
928
- const deleteColumn = useCallback4((colId) => {
929
- controller.deleteColumn(colId);
930
- setDoc({ ...controller.document });
931
- }, [controller]);
932
- const insertRow = useCallback4((index, config) => {
933
- controller.insertRow(index, config);
934
- setDoc({ ...controller.document });
935
- }, [controller]);
936
- const deleteRow = useCallback4((rowId) => {
937
- controller.deleteRow(rowId);
938
- setDoc({ ...controller.document });
939
- }, [controller]);
940
- const setColumnWidth = useCallback4((colId, width) => {
941
- controller.setColumnWidth(colId, width);
942
- setDoc({ ...controller.document });
943
- }, [controller]);
944
- const autoFitColumnWidth = useCallback4((colId, customWidth) => {
945
- controller.autoFitColumnWidth(colId, customWidth);
946
- setDoc({ ...controller.document });
947
- }, [controller]);
948
- const registerDataSource = useCallback4((ds) => {
949
- controller.registerDataSource(ds);
950
- setDoc({ ...controller.document });
951
- }, [controller]);
952
- const setPrimaryDataSource = useCallback4((id) => {
953
- controller.setPrimaryDataSource(id);
954
- setDoc({ ...controller.document });
955
- }, [controller]);
956
- const recalculateAll = useCallback4(() => {
957
- controller.recalculateAll();
958
- setDoc({ ...controller.document });
959
- }, [controller]);
960
- const undo = useCallback4(() => {
961
- controller.undo();
962
- setDoc({ ...controller.document });
963
- }, [controller]);
964
- const redo = useCallback4(() => {
965
- controller.redo();
966
- setDoc({ ...controller.document });
967
- }, [controller]);
968
- const tableActions = useMemo6(() => bindSpreadsheetTableActions(controller), [controller]);
969
- const viewModel = useMemo6(() => controller.getViewModel(), [controller, doc]);
970
- return {
971
- document: doc,
972
- activeCell,
973
- selectedRange,
974
- editingCell,
975
- draftValue,
976
- setDraftValue,
977
- controller,
978
- getCell: (rowId, colId) => controller.getCell(rowId, colId),
979
- setCellValue,
980
- selectCell,
981
- selectRange,
982
- startEditing,
983
- commitEditing,
984
- cancelEditing,
985
- moveCursor,
986
- insertColumn,
987
- deleteColumn,
988
- insertRow,
989
- deleteRow,
990
- setColumnWidth,
991
- autoFitColumnWidth,
992
- loadDocument: (document2) => {
993
- controller.loadDocument(document2);
994
- setDoc({ ...controller.document });
995
- setActiveCell(null);
996
- setSelectedRange(null);
997
- setEditingCell(null);
998
- setDraftValue("");
999
- },
1000
- syncWithDataSource: () => {
1001
- controller.syncWithDataSource();
1002
- controller.recalculateAll();
1003
- setDoc({ ...controller.document });
1004
- },
1005
- registerDataSource,
1006
- setPrimaryDataSource,
1007
- recalculateAll,
1008
- exportToTsv: (range) => controller.exportToTsv(range),
1009
- importFromTsv: (data) => {
1010
- controller.importFromTsv(data);
1011
- setDoc({ ...controller.document });
1012
- },
1013
- ...tableActions,
1014
- viewModel,
1015
- undo,
1016
- redo
1017
- };
1018
- }
1019
-
1020
- // src/spreadsheet/SpreadsheetEditor.tsx
1021
- import { useRef as useRef11, useState as useState8, useMemo as useMemo8, useEffect as useEffect10, useCallback as useCallback6 } from "react";
1022
- import {
1023
- indexToColName,
1024
- getSpreadsheetMenuItems,
1025
- normalizeRange as normalizeRange2,
1026
- rangeContains
1027
- } from "@pixerate/editor";
1028
-
1029
- // src/spreadsheet/SpreadsheetContextMenu.tsx
1030
- import React4, { useEffect as useEffect7, useRef as useRef8 } from "react";
1031
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1032
- var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
1033
- const menuRef = useRef8(null);
1034
- const onCloseRef = useRef8(onClose);
1035
- onCloseRef.current = onClose;
1036
- useEffect7(() => {
1037
- const handlePointer = (e) => {
1038
- if (!menuRef.current?.contains(e.target)) onCloseRef.current();
1039
- };
1040
- const handleKey = (e) => {
1041
- if (e.key === "Escape") onCloseRef.current();
1042
- };
1043
- window.addEventListener("mousedown", handlePointer);
1044
- window.addEventListener("keydown", handleKey);
1045
- return () => {
1046
- window.removeEventListener("mousedown", handlePointer);
1047
- window.removeEventListener("keydown", handleKey);
1048
- };
1049
- }, []);
1050
- return /* @__PURE__ */ jsx5(
1051
- "div",
1052
- {
1053
- ref: menuRef,
1054
- role: "menu",
1055
- "aria-label": "Table actions",
1056
- className: "fixed z-50 min-w-[14rem] rounded-md border border-border bg-popover py-1 text-xs text-popover-foreground shadow-lg",
1057
- style: { left: x, top: y, background: "var(--popover, var(--background, #fff))" },
1058
- onContextMenu: (e) => e.preventDefault(),
1059
- onKeyDown: (e) => e.stopPropagation(),
1060
- children: items.map((item) => /* @__PURE__ */ jsxs4(React4.Fragment, { children: [
1061
- item.separatorBefore && /* @__PURE__ */ jsx5("div", { role: "separator", className: "my-1 h-px bg-border" }),
1062
- /* @__PURE__ */ jsx5(
1063
- "button",
1064
- {
1065
- type: "button",
1066
- role: "menuitem",
1067
- "data-action": item.id,
1068
- disabled: item.disabled,
1069
- className: "block w-full px-3 py-1 text-left hover:bg-muted disabled:cursor-default disabled:opacity-40 disabled:hover:bg-transparent",
1070
- onClick: () => {
1071
- onClose();
1072
- item.run();
1073
- },
1074
- children: item.label
1075
- }
1076
- )
1077
- ] }, item.id))
1078
- }
1079
- );
1080
- };
1081
-
1082
- // src/spreadsheet/SpreadsheetGroupRow.tsx
1083
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1084
- var formatSummary = (value) => value === null ? "\u2013" : String(value);
1085
- var SpreadsheetGroupRow = ({
1086
- group,
1087
- columns,
1088
- groupColId,
1089
- onToggle,
1090
- trailingCell
1091
- }) => {
1092
- const labelColId = columns.some((c) => c.column.id === groupColId) ? groupColId : columns[0]?.column.id;
1093
- return /* @__PURE__ */ jsxs5("tr", { className: "border-b border-border bg-muted/40 font-semibold", "data-group-key": group.key, style: { height: 28 }, children: [
1094
- /* @__PURE__ */ jsx6("td", { className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted/60 border-r border-border text-center", children: /* @__PURE__ */ jsx6(
1095
- "button",
1096
- {
1097
- type: "button",
1098
- "aria-label": `${group.collapsed ? "Expand" : "Collapse"} group ${group.label}`,
1099
- "aria-expanded": !group.collapsed,
1100
- className: "h-5 w-5 rounded text-[10px] text-muted-foreground hover:bg-muted",
1101
- onClick: (e) => {
1102
- e.stopPropagation();
1103
- onToggle(group.key);
1104
- },
1105
- children: group.collapsed ? "\u25B8" : "\u25BE"
1106
- }
1107
- ) }),
1108
- columns.map(({ column }) => {
1109
- const summary = group.summaries[column.id];
1110
- return /* @__PURE__ */ jsx6("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__ */ jsxs5("span", { className: "truncate", children: [
1111
- group.label,
1112
- " ",
1113
- /* @__PURE__ */ jsxs5("span", { className: "font-normal text-muted-foreground", children: [
1114
- "(",
1115
- group.rowIndices.length,
1116
- ")"
1117
- ] })
1118
- ] }) : summary !== void 0 ? /* @__PURE__ */ jsx6("span", { className: "truncate", "data-summary": column.id, children: formatSummary(summary) }) : null }, column.id);
1119
- }),
1120
- trailingCell && /* @__PURE__ */ jsx6("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
1121
- ] });
1122
- };
1123
-
1124
- // src/spreadsheet/useSpreadsheetTableInteractions.ts
1125
- import { useCallback as useCallback5, useEffect as useEffect8, useRef as useRef9, useState as useState6 } from "react";
1126
- import {
1127
- computeFillTarget,
1128
- normalizeRange
1129
- } from "@pixerate/editor";
1130
- function gridHasFocus(grid) {
1131
- if (!grid || typeof document === "undefined") return false;
1132
- const active = document.activeElement;
1133
- if (!active || !grid.contains(active)) return false;
1134
- return !(active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement);
1135
- }
1136
- function useSpreadsheetClipboard(gridRef, state, readOnly) {
1137
- const stateRef = useRef9(state);
1138
- stateRef.current = state;
1139
- const readOnlyRef = useRef9(readOnly);
1140
- readOnlyRef.current = readOnly;
1141
- const copiedRef = useRef9(false);
1142
- useEffect8(() => {
1143
- if (typeof document === "undefined") return;
1144
- const handleCopy = (e) => {
1145
- const s = stateRef.current;
1146
- if (!gridHasFocus(gridRef.current) || s.editingCell || !e.clipboardData || !s.selectedRange) return;
1147
- for (const [type, data] of Object.entries(s.copyToClipboardData(s.selectedRange))) {
1148
- e.clipboardData.setData(type, data);
1149
- }
1150
- copiedRef.current = true;
1151
- e.preventDefault();
1152
- };
1153
- const handlePaste = (e) => {
1154
- const s = stateRef.current;
1155
- if (readOnlyRef.current || !gridHasFocus(gridRef.current) || s.editingCell || !e.clipboardData) return;
1156
- if (s.pasteClipboardData(e.clipboardData)) e.preventDefault();
1157
- };
1158
- document.addEventListener("copy", handleCopy);
1159
- document.addEventListener("paste", handlePaste);
1160
- return () => {
1161
- document.removeEventListener("copy", handleCopy);
1162
- document.removeEventListener("paste", handlePaste);
1163
- };
1164
- }, [gridRef]);
1165
- const copyShortcutFallback = useCallback5(() => {
1166
- copiedRef.current = false;
1167
- setTimeout(() => {
1168
- if (copiedRef.current) return;
1169
- const s = stateRef.current;
1170
- navigator.clipboard?.writeText(s.exportToTsv(s.selectedRange));
1171
- }, 0);
1172
- }, []);
1173
- return { copyShortcutFallback };
1174
- }
1175
- function useFillHandle(state) {
1176
- const [drag, setDrag] = useState6(null);
1177
- const dragRef = useRef9(drag);
1178
- dragRef.current = drag;
1179
- const stateRef = useRef9(state);
1180
- stateRef.current = state;
1181
- const dragging = drag !== null;
1182
- useEffect8(() => {
1183
- if (!dragging) return;
1184
- const handleMouseUp = () => {
1185
- const current = dragRef.current;
1186
- setDrag(null);
1187
- if (current?.target) stateRef.current.autofill(current.source, current.target);
1188
- };
1189
- window.addEventListener("mouseup", handleMouseUp);
1190
- return () => window.removeEventListener("mouseup", handleMouseUp);
1191
- }, [dragging]);
1192
- const startFill = useCallback5((e) => {
1193
- e.preventDefault();
1194
- e.stopPropagation();
1195
- const range = stateRef.current.selectedRange;
1196
- if (range) setDrag({ source: normalizeRange(range), target: null });
1197
- }, []);
1198
- const extendFill = useCallback5((row, col) => {
1199
- const current = dragRef.current;
1200
- if (!current) return;
1201
- setDrag({ source: current.source, target: computeFillTarget(current.source, { row, col }) });
1202
- }, []);
1203
- return { fillTarget: drag?.target ?? null, isFilling: dragging, startFill, extendFill };
1204
- }
1205
-
1206
- // src/spreadsheet/FormulaHints.tsx
1207
- import { useEffect as useEffect9, useLayoutEffect, useMemo as useMemo7, useRef as useRef10, useState as useState7 } from "react";
1208
- import { applyFormulaSuggestion, getFormulaHints } from "@pixerate/editor";
1209
- import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1210
- var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
1211
- const [caret, setCaret] = useState7(null);
1212
- const [focused, setFocused] = useState7(false);
1213
- const [selected, setSelected] = useState7(0);
1214
- const [dismissed, setDismissed] = useState7(false);
1215
- const [position, setPosition] = useState7(null);
1216
- const pendingCaret = useRef10(null);
1217
- const hints = useMemo7(
1218
- () => getFormulaHints(value, caret ?? value.length, { limit }),
1219
- [value, caret, limit]
1220
- );
1221
- const suggestions = dismissed ? [] : hints.suggestions;
1222
- const selectedIndex = Math.max(0, Math.min(selected, suggestions.length - 1));
1223
- const visible = focused && value.trimStart().startsWith("=") && (suggestions.length > 0 || !!hints.activeCall);
1224
- const latest = useRef10({ suggestions, selected: selectedIndex, value, caret, onAccept });
1225
- latest.current = { suggestions, selected: selectedIndex, value, caret, onAccept };
1226
- const accept = (index) => {
1227
- const { suggestions: list, value: current, caret: at, onAccept: commit } = latest.current;
1228
- const suggestion = list[index];
1229
- if (!suggestion) return;
1230
- const next = applyFormulaSuggestion(current, at ?? current.length, suggestion.name);
1231
- pendingCaret.current = next.caret;
1232
- commit(next.value);
1233
- };
1234
- useEffect9(() => {
1235
- const input = inputRef.current;
1236
- if (!input) return;
1237
- const syncCaret = () => setCaret(input.selectionStart ?? input.value.length);
1238
- const onInput = () => {
1239
- setDismissed(false);
1240
- setSelected(0);
1241
- syncCaret();
1242
- };
1243
- const onFocus = () => {
1244
- setFocused(true);
1245
- syncCaret();
1246
- };
1247
- const onBlur = () => setFocused(false);
1248
- const onKeyDown = (e) => {
1249
- const { suggestions: list } = latest.current;
1250
- if (list.length === 0) return;
1251
- if (e.key === "ArrowDown" || e.key === "ArrowUp") {
1252
- e.preventDefault();
1253
- e.stopPropagation();
1254
- const step = e.key === "ArrowDown" ? 1 : -1;
1255
- setSelected((i) => (i + step + list.length) % list.length);
1256
- } else if (e.key === "Tab" && !e.shiftKey) {
1257
- e.preventDefault();
1258
- e.stopPropagation();
1259
- accept(latest.current.selected);
1260
- } else if (e.key === "Escape") {
1261
- e.preventDefault();
1262
- e.stopPropagation();
1263
- setDismissed(true);
1264
- }
1265
- };
1266
- input.addEventListener("input", onInput);
1267
- input.addEventListener("keyup", syncCaret);
1268
- input.addEventListener("click", syncCaret);
1269
- input.addEventListener("select", syncCaret);
1270
- input.addEventListener("focus", onFocus);
1271
- input.addEventListener("blur", onBlur);
1272
- input.addEventListener("keydown", onKeyDown);
1273
- if (typeof document !== "undefined" && document.activeElement === input) onFocus();
1274
- return () => {
1275
- input.removeEventListener("input", onInput);
1276
- input.removeEventListener("keyup", syncCaret);
1277
- input.removeEventListener("click", syncCaret);
1278
- input.removeEventListener("select", syncCaret);
1279
- input.removeEventListener("focus", onFocus);
1280
- input.removeEventListener("blur", onBlur);
1281
- input.removeEventListener("keydown", onKeyDown);
1282
- };
1283
- }, [inputRef]);
1284
- useLayoutEffect(() => {
1285
- const input = inputRef.current;
1286
- const at = pendingCaret.current;
1287
- if (!input || at === null || input.value !== value) return;
1288
- pendingCaret.current = null;
1289
- input.setSelectionRange(at, at);
1290
- setCaret(at);
1291
- setSelected(0);
1292
- }, [value, inputRef]);
1293
- useLayoutEffect(() => {
1294
- const input = inputRef.current;
1295
- if (!visible || !input) return;
1296
- const rect = input.getBoundingClientRect();
1297
- setPosition({ top: rect.bottom + 2, left: rect.left, width: rect.width });
1298
- }, [visible, value, inputRef]);
1299
- if (!visible) return null;
1300
- const { activeCall } = hints;
1301
- return /* @__PURE__ */ jsxs6(
1302
- "div",
1303
- {
1304
- "data-formula-hints": "",
1305
- className: "rounded-md border border-border bg-popover text-popover-foreground shadow-md text-xs font-sans overflow-hidden",
1306
- style: {
1307
- position: "fixed",
1308
- top: position?.top ?? 0,
1309
- left: position?.left ?? 0,
1310
- minWidth: Math.max(260, position?.width ?? 0),
1311
- maxWidth: 440,
1312
- zIndex: 50,
1313
- visibility: position ? "visible" : "hidden"
1314
- },
1315
- onMouseDown: (e) => e.preventDefault(),
1316
- children: [
1317
- activeCall && /* @__PURE__ */ jsxs6("div", { "data-formula-signature": "", className: "px-2 py-1.5 border-b border-border bg-muted/30", children: [
1318
- /* @__PURE__ */ jsx7("div", { className: "font-mono", children: activeCall.parts.map((part, i) => /* @__PURE__ */ jsx7(
1319
- "span",
1320
- {
1321
- "data-active": part.active ? "" : void 0,
1322
- className: part.active ? "font-bold text-primary underline" : "",
1323
- children: part.text
1324
- },
1325
- i
1326
- )) }),
1327
- /* @__PURE__ */ jsx7("div", { className: "mt-0.5 text-muted-foreground", children: activeCall.argument ? /* @__PURE__ */ jsxs6(Fragment, { children: [
1328
- /* @__PURE__ */ jsx7("span", { className: "font-semibold", children: activeCall.argument.name }),
1329
- ": ",
1330
- activeCall.argument.description
1331
- ] }) : activeCall.description })
1332
- ] }),
1333
- suggestions.length > 0 && /* @__PURE__ */ jsx7("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__ */ jsxs6(
1334
- "li",
1335
- {
1336
- role: "option",
1337
- "aria-selected": i === selectedIndex,
1338
- className: `cursor-pointer px-2 py-1 ${i === selectedIndex ? "bg-primary/10" : "hover:bg-muted/50"}`,
1339
- onMouseEnter: () => setSelected(i),
1340
- onMouseDown: (e) => {
1341
- e.preventDefault();
1342
- accept(i);
1343
- },
1344
- children: [
1345
- /* @__PURE__ */ jsx7("div", { className: "font-mono font-semibold text-foreground", children: s.signature }),
1346
- /* @__PURE__ */ jsx7("div", { className: "text-muted-foreground truncate", children: s.description })
1347
- ]
1348
- },
1349
- s.name
1350
- )) })
1351
- ]
1352
- }
1353
- );
1354
- };
1355
-
1356
- // src/spreadsheet/SpreadsheetEditor.tsx
1357
- import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1358
- var COLUMN_HEADER_HEIGHT = 28;
1359
- var ROW_HEADER_WIDTH = 48;
1360
- function frozenCellStyle(viewRow, viewCol) {
1361
- if (!viewRow.frozen && !viewCol.frozen) return void 0;
1362
- return {
1363
- position: "sticky",
1364
- ...viewRow.frozen ? { top: COLUMN_HEADER_HEIGHT + viewRow.stickyTop } : {},
1365
- ...viewCol.frozen ? { left: ROW_HEADER_WIDTH + viewCol.stickyLeft } : {},
1366
- zIndex: viewRow.frozen && viewCol.frozen ? 14 : viewRow.frozen ? 12 : 11,
1367
- background: "var(--background, #fff)"
1368
- };
1369
- }
1370
- var SpreadsheetEditor = ({
1371
- state: externalState,
1372
- readOnly = false,
1373
- customCellRenderer,
1374
- onAddColumnClick,
1375
- onColumnHeaderClick,
1376
- onColumnResize
1377
- }) => {
1378
- const defaultState = useSpreadsheetEditor();
1379
- const state = externalState || defaultState;
1380
- const gridRef = useRef11(null);
1381
- const formulaBarRef = useRef11(null);
1382
- const cellInputRef = useRef11(null);
1383
- const [resizingColId, setResizingColId] = useState8(null);
1384
- const [hoveredColId, setHoveredColId] = useState8(null);
1385
- const [resizeStartX, setResizeStartX] = useState8(0);
1386
- const [resizeStartWidth, setResizeStartWidth] = useState8(0);
1387
- const view = state.viewModel;
1388
- const { copyShortcutFallback } = useSpreadsheetClipboard(gridRef, state, readOnly);
1389
- const { fillTarget, startFill, extendFill } = useFillHandle(state);
1390
- const [menu, setMenu] = useState8(null);
1391
- const filter = state.document.table?.filter;
1392
- const filteredColIds = useMemo8(
1393
- () => new Set(filter?.enabled ? filter.rules.map((r) => r.colId) : []),
1394
- [filter]
1395
- );
1396
- const handleCellContextMenu = useCallback6((rIdx, cIdx, e) => {
1397
- if (readOnly) return;
1398
- e.preventDefault();
1399
- if (state.editingCell) state.commitEditing();
1400
- if (!state.selectedRange || !rangeContains(state.selectedRange, rIdx, cIdx)) {
1401
- state.selectCell(rIdx, cIdx);
1402
- }
1403
- setMenu({ x: e.clientX, y: e.clientY, items: getSpreadsheetMenuItems(state.controller, { row: rIdx, col: cIdx }) });
1404
- }, [readOnly, state]);
1405
- const activeRawValue = useMemo8(() => {
1406
- if (!state.activeCell) return "";
1407
- const row = state.document.rows[state.activeCell.row];
1408
- const col = state.document.columns[state.activeCell.col];
1409
- if (!row || !col) return "";
1410
- return state.getCell(row.id, col.id).raw;
1411
- }, [state.activeCell, state.document]);
1412
- const coordLabel = useMemo8(() => {
1413
- if (!state.activeCell) return "--";
1414
- const colName = indexToColName(state.activeCell.col);
1415
- return `${colName}${state.activeCell.row + 1}`;
1416
- }, [state.activeCell]);
1417
- const [formulaDraft, setFormulaDraft] = useState8(null);
1418
- const activeColumn = state.activeCell ? state.document.columns[state.activeCell.col] : void 0;
1419
- const formulaBarReadOnly = readOnly || !state.activeCell || !!activeColumn?.readOnly;
1420
- useEffect10(() => {
1421
- setFormulaDraft(null);
1422
- }, [state.activeCell?.row, state.activeCell?.col]);
1423
- const commitFormulaBar = () => {
1424
- if (state.editingCell) {
1425
- state.commitEditing();
1426
- return;
1427
- }
1428
- if (formulaDraft === null || !state.activeCell) return;
1429
- const row = state.document.rows[state.activeCell.row];
1430
- const col = state.document.columns[state.activeCell.col];
1431
- if (row && col && !formulaBarReadOnly && formulaDraft !== activeRawValue) {
1432
- state.setCellValue(row.id, col.id, formulaDraft);
1433
- }
1434
- setFormulaDraft(null);
1435
- };
1436
- const handleCellClick = useCallback6((rIdx, cIdx, e) => {
1437
- if (state.editingCell && (state.editingCell.row !== rIdx || state.editingCell.col !== cIdx)) {
1438
- state.commitEditing();
1439
- }
1440
- state.selectCell(rIdx, cIdx, e.shiftKey);
1441
- }, [state]);
1442
- const handleCellDblClick = useCallback6((rIdx, cIdx) => {
1443
- if (readOnly) return;
1444
- const col = state.document.columns[cIdx];
1445
- if (col?.readOnly) return;
1446
- state.startEditing(rIdx, cIdx);
1447
- }, [readOnly, state]);
1448
- const handleKeyDown = useCallback6((e) => {
1449
- if (state.editingCell) {
1450
- if (e.key === "Enter") {
1451
- e.preventDefault();
1452
- state.commitEditing();
1453
- state.moveCursor("down");
1454
- } else if (e.key === "Escape") {
1455
- e.preventDefault();
1456
- state.cancelEditing();
1457
- } else if (e.key === "Tab") {
1458
- e.preventDefault();
1459
- state.commitEditing();
1460
- state.moveCursor(e.shiftKey ? "left" : "right");
1461
- }
1462
- return;
1463
- }
1464
- if (!state.activeCell) return;
1465
- if (e.key === "ArrowUp") {
1466
- e.preventDefault();
1467
- state.moveCursor("up", e.shiftKey);
1468
- } else if (e.key === "ArrowDown") {
1469
- e.preventDefault();
1470
- state.moveCursor("down", e.shiftKey);
1471
- } else if (e.key === "ArrowLeft") {
1472
- e.preventDefault();
1473
- state.moveCursor("left", e.shiftKey);
1474
- } else if (e.key === "ArrowRight") {
1475
- e.preventDefault();
1476
- state.moveCursor("right", e.shiftKey);
1477
- } else if (e.key === "Tab") {
1478
- e.preventDefault();
1479
- state.moveCursor(e.shiftKey ? "left" : "right");
1480
- } else if (e.key === "Enter") {
1481
- e.preventDefault();
1482
- const col = state.document.columns[state.activeCell.col];
1483
- if (!readOnly && !col?.readOnly) {
1484
- state.startEditing();
1485
- }
1486
- } else if (e.key === "Backspace" || e.key === "Delete") {
1487
- if (readOnly) return;
1488
- const row = state.document.rows[state.activeCell.row];
1489
- const col = state.document.columns[state.activeCell.col];
1490
- if (row && col && !col.readOnly) {
1491
- e.preventDefault();
1492
- state.setCellValue(row.id, col.id, "");
1493
- }
1494
- } else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "z") {
1495
- e.preventDefault();
1496
- if (e.shiftKey) {
1497
- state.redo();
1498
- } else {
1499
- state.undo();
1500
- }
1501
- } else if (e.ctrlKey && !e.metaKey && e.key.toLowerCase() === "y") {
1502
- e.preventDefault();
1503
- state.redo();
1504
- } else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "c") {
1505
- copyShortcutFallback();
1506
- } else if (!readOnly && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
1507
- const col = state.document.columns[state.activeCell.col];
1508
- if (!col?.readOnly) {
1509
- state.startEditing(state.activeCell.row, state.activeCell.col, e.key);
1510
- }
1511
- }
1512
- }, [readOnly, state, copyShortcutFallback]);
1513
- const stateRef = useRef11(state);
1514
- stateRef.current = state;
1515
- const onColumnResizeRef = useRef11(onColumnResize);
1516
- onColumnResizeRef.current = onColumnResize;
1517
- useEffect10(() => {
1518
- if (!resizingColId) return;
1519
- if (typeof document !== "undefined") {
1520
- document.body.style.cursor = "col-resize";
1521
- document.body.style.userSelect = "none";
1522
- }
1523
- let rafId = null;
1524
- let pendingWidth = resizeStartWidth;
1525
- let hasDragged = false;
1526
- const handleMouseMove = (e) => {
1527
- const delta = e.clientX - resizeStartX;
1528
- if (Math.abs(delta) > 2) {
1529
- hasDragged = true;
1530
- }
1531
- pendingWidth = Math.max(30, resizeStartWidth + delta);
1532
- if (rafId === null) {
1533
- rafId = requestAnimationFrame(() => {
1534
- rafId = null;
1535
- stateRef.current.setColumnWidth(resizingColId, pendingWidth);
1536
- });
1537
- }
1538
- };
1539
- const handleMouseUp = () => {
1540
- if (rafId !== null) {
1541
- cancelAnimationFrame(rafId);
1542
- rafId = null;
1543
- }
1544
- if (hasDragged) {
1545
- stateRef.current.setColumnWidth(resizingColId, pendingWidth);
1546
- onColumnResizeRef.current?.(resizingColId, pendingWidth);
1547
- }
1548
- setResizingColId(null);
1549
- if (typeof document !== "undefined") {
1550
- document.body.style.cursor = "";
1551
- document.body.style.userSelect = "";
1552
- }
1553
- };
1554
- window.addEventListener("mousemove", handleMouseMove);
1555
- window.addEventListener("mouseup", handleMouseUp);
1556
- return () => {
1557
- if (rafId !== null) {
1558
- cancelAnimationFrame(rafId);
1559
- }
1560
- if (typeof document !== "undefined") {
1561
- document.body.style.cursor = "";
1562
- document.body.style.userSelect = "";
1563
- }
1564
- window.removeEventListener("mousemove", handleMouseMove);
1565
- window.removeEventListener("mouseup", handleMouseUp);
1566
- };
1567
- }, [resizingColId, resizeStartX, resizeStartWidth]);
1568
- const measureElementContentWidth = useCallback6((el, container) => {
1569
- if (typeof document === "undefined") return 0;
1570
- let maxW = el.scrollWidth || 0;
1571
- const firstChild = el.firstElementChild;
1572
- if (firstChild && firstChild.scrollWidth > maxW) {
1573
- maxW = firstChild.scrollWidth;
1574
- }
1575
- const truncated = el.querySelectorAll(".truncate, [title], span, div");
1576
- truncated.forEach((node) => {
1577
- if (node.scrollWidth > maxW) {
1578
- maxW = node.scrollWidth;
1579
- }
1580
- });
1581
- try {
1582
- const clone = firstChild ? firstChild.cloneNode(true) : el.cloneNode(true);
1583
- clone.style.cssText = `
1584
- position: absolute !important;
1585
- visibility: hidden !important;
1586
- width: max-content !important;
1587
- min-width: max-content !important;
1588
- max-width: none !important;
1589
- overflow: visible !important;
1590
- white-space: nowrap !important;
1591
- pointer-events: none !important;
1592
- top: -9999px !important;
1593
- left: -9999px !important;
1594
- `;
1595
- const allNodes = clone.querySelectorAll("*");
1596
- allNodes.forEach((node) => {
1597
- node.style.maxWidth = "none";
1598
- node.style.overflow = "visible";
1599
- node.style.textOverflow = "clip";
1600
- node.style.whiteSpace = "nowrap";
1601
- if (node.classList.contains("truncate") || node.classList.contains("min-w-0") || node.classList.contains("w-full") || node.classList.contains("max-w-0") || node.classList.contains("flex-1")) {
1602
- node.style.width = "max-content";
1603
- node.style.minWidth = "max-content";
1604
- }
1605
- });
1606
- container.appendChild(clone);
1607
- const rect = clone.getBoundingClientRect();
1608
- container.removeChild(clone);
1609
- if (rect.width > maxW) {
1610
- maxW = rect.width;
1611
- }
1612
- } catch {
1613
- }
1614
- return maxW;
1615
- }, []);
1616
- const autoFitColumn = useCallback6((colId, colIndex) => {
1617
- const col = state.document.columns.find((c) => c.id === colId);
1618
- if (!col) return;
1619
- let calculatedWidth = 0;
1620
- if (gridRef.current && typeof document !== "undefined") {
1621
- const table = gridRef.current.querySelector("table");
1622
- if (table) {
1623
- const th = table.querySelector(`thead th[data-col="${colIndex}"]`);
1624
- if (th) {
1625
- const headerInner = th.firstElementChild || th;
1626
- const headerW = measureElementContentWidth(headerInner, gridRef.current);
1627
- calculatedWidth = Math.max(calculatedWidth, headerW + 28);
1628
- }
1629
- const rows = table.querySelectorAll("tbody tr");
1630
- const sampleLimit = Math.min(rows.length, 150);
1631
- for (let r = 0; r < sampleLimit; r++) {
1632
- const tr = rows[r];
1633
- const td = tr.querySelector(`td[data-col="${colIndex}"][colspan="1"]`);
1634
- if (td) {
1635
- const cellW = measureElementContentWidth(td, gridRef.current);
1636
- calculatedWidth = Math.max(calculatedWidth, cellW + 20);
1637
- }
1638
- }
1639
- }
1640
- }
1641
- if (calculatedWidth > 0) {
1642
- const finalWidth = Math.min(600, Math.max(50, Math.ceil(calculatedWidth)));
1643
- state.setColumnWidth(colId, finalWidth);
1644
- } else {
1645
- state.autoFitColumnWidth?.(colId);
1646
- }
1647
- const updatedCol = state.document.columns.find((c) => c.id === colId);
1648
- if (updatedCol && onColumnResize) {
1649
- onColumnResize(updatedCol.id, updatedCol.width || 130);
1650
- }
1651
- }, [state, measureElementContentWidth, onColumnResize]);
1652
- const handleResizeDblClick = useCallback6((colId, colIndex, e) => {
1653
- e.preventDefault();
1654
- e.stopPropagation();
1655
- autoFitColumn(colId, colIndex);
1656
- }, [autoFitColumn]);
1657
- const handleHeaderDblClick = useCallback6((colId, colIndex, e) => {
1658
- const target = e.currentTarget;
1659
- if (target) {
1660
- const rect = target.getBoundingClientRect();
1661
- if (rect.right - e.clientX <= 12) {
1662
- e.preventDefault();
1663
- e.stopPropagation();
1664
- autoFitColumn(colId, colIndex);
1665
- }
1666
- }
1667
- }, [autoFitColumn]);
1668
- const isCellSelected = (rIdx, cIdx) => {
1669
- if (!state.selectedRange) return false;
1670
- const minR = Math.min(state.selectedRange.startRow, state.selectedRange.endRow);
1671
- const maxR = Math.max(state.selectedRange.startRow, state.selectedRange.endRow);
1672
- const minC = Math.min(state.selectedRange.startCol, state.selectedRange.endCol);
1673
- const maxC = Math.max(state.selectedRange.startCol, state.selectedRange.endCol);
1674
- return rIdx >= minR && rIdx <= maxR && cIdx >= minC && cIdx <= maxC;
1675
- };
1676
- const isActiveCell = (rIdx, cIdx) => {
1677
- return state.activeCell?.row === rIdx && state.activeCell?.col === cIdx;
1678
- };
1679
- return /* @__PURE__ */ jsxs7(
1680
- "div",
1681
- {
1682
- ref: gridRef,
1683
- className: "flex flex-col h-full w-full overflow-hidden border border-border bg-background select-none outline-none text-foreground font-sans text-xs",
1684
- tabIndex: 0,
1685
- onKeyDown: handleKeyDown,
1686
- role: "grid",
1687
- "aria-label": "Spreadsheet",
1688
- children: [
1689
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 border-b border-border bg-muted/20 px-3 py-1.5 text-xs font-mono", children: [
1690
- /* @__PURE__ */ jsx8("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 }),
1691
- /* @__PURE__ */ jsx8("div", { className: "flex items-center text-muted-foreground select-none", children: /* @__PURE__ */ jsx8("span", { className: "italic font-serif font-bold text-sm text-primary", children: "fx" }) }),
1692
- /* @__PURE__ */ jsx8(
1693
- "input",
1694
- {
1695
- ref: formulaBarRef,
1696
- type: "text",
1697
- 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",
1698
- placeholder: "Enter a value or formula (e.g. =SUM(A1:A5))",
1699
- "aria-label": "Formula bar",
1700
- readOnly: formulaBarReadOnly,
1701
- value: state.editingCell ? state.draftValue : formulaDraft ?? activeRawValue,
1702
- onFocus: () => {
1703
- if (!state.editingCell) setFormulaDraft(activeRawValue);
1704
- },
1705
- onChange: (e) => {
1706
- if (state.editingCell) {
1707
- state.setDraftValue(e.target.value);
1708
- } else if (state.activeCell) {
1709
- setFormulaDraft(e.target.value);
1710
- }
1711
- },
1712
- onBlur: () => commitFormulaBar(),
1713
- onKeyDown: (e) => {
1714
- e.stopPropagation();
1715
- if (e.key === "Enter") {
1716
- e.preventDefault();
1717
- commitFormulaBar();
1718
- } else if (e.key === "Escape") {
1719
- e.preventDefault();
1720
- setFormulaDraft(null);
1721
- if (state.editingCell) state.cancelEditing();
1722
- }
1723
- }
1724
- }
1725
- ),
1726
- !formulaBarReadOnly && /* @__PURE__ */ jsx8(
1727
- FormulaHints,
1728
- {
1729
- inputRef: formulaBarRef,
1730
- value: state.editingCell ? state.draftValue : formulaDraft ?? activeRawValue,
1731
- onAccept: (next) => state.editingCell ? state.setDraftValue(next) : setFormulaDraft(next)
1732
- }
1733
- )
1734
- ] }),
1735
- /* @__PURE__ */ jsx8("div", { className: "relative flex-1 overflow-auto", children: /* @__PURE__ */ jsxs7("table", { className: "border-collapse table-fixed w-max min-w-full", children: [
1736
- /* @__PURE__ */ jsx8("thead", { children: /* @__PURE__ */ jsxs7("tr", { className: "h-7 bg-muted/60 sticky top-0 z-20 border-b border-border shadow-xs", children: [
1737
- /* @__PURE__ */ jsx8("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: "#" }),
1738
- view.columns.map(({ column: col, index: cIdx, frozen, stickyLeft }) => /* @__PURE__ */ jsxs7(
1739
- "th",
1740
- {
1741
- "data-col": cIdx,
1742
- className: `relative border-r border-border px-2 text-left font-medium text-muted-foreground hover:bg-muted/90 transition-colors group overflow-hidden max-w-0 ${onColumnHeaderClick ? "cursor-pointer" : "cursor-default"}`,
1743
- style: {
1744
- width: col.width || 130,
1745
- minWidth: col.width || 130,
1746
- maxWidth: col.width || 130,
1747
- overflow: "hidden",
1748
- ...frozen ? { position: "sticky", left: ROW_HEADER_WIDTH + stickyLeft, zIndex: 25, background: "var(--muted, #f4f4f5)" } : {}
1749
- },
1750
- onClick: () => onColumnHeaderClick?.(col),
1751
- onDoubleClick: (e) => handleHeaderDblClick(col.id, cIdx, e),
1752
- children: [
1753
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between gap-1 min-w-0 overflow-hidden truncate", children: [
1754
- /* @__PURE__ */ jsx8("span", { className: "truncate font-semibold text-foreground text-xs min-w-0", children: col.title }),
1755
- filteredColIds.has(col.id) && /* @__PURE__ */ jsx8("span", { className: "text-[10px] text-primary shrink-0", title: "Filtered", "data-filtered": "true", children: "\u23F7" }),
1756
- /* @__PURE__ */ jsxs7("span", { className: "text-[10px] text-muted-foreground/70 font-mono font-normal shrink-0", children: [
1757
- "(",
1758
- col.key,
1759
- ")"
1760
- ] })
1761
- ] }),
1762
- /* @__PURE__ */ jsx8(
1763
- "div",
1764
- {
1765
- className: "col-resize-handle",
1766
- style: { position: "absolute", top: 0, bottom: 0, width: 12, right: -6, cursor: "col-resize", zIndex: 20, display: "flex", justifyContent: "center", touchAction: "none" },
1767
- onMouseEnter: () => setHoveredColId(col.id),
1768
- onMouseLeave: () => setHoveredColId((cur) => cur === col.id ? null : cur),
1769
- onMouseDown: (e) => {
1770
- e.preventDefault();
1771
- e.stopPropagation();
1772
- setResizingColId(col.id);
1773
- setResizeStartX(e.clientX);
1774
- setResizeStartWidth(col.width || 130);
1775
- },
1776
- onDoubleClick: (e) => handleResizeDblClick(col.id, cIdx, e),
1777
- role: "separator",
1778
- "aria-orientation": "vertical",
1779
- children: /* @__PURE__ */ jsx8(
1780
- "div",
1781
- {
1782
- className: "col-resize-handle-line",
1783
- style: {
1784
- width: 2,
1785
- height: "100%",
1786
- pointerEvents: "none",
1787
- transition: "background-color 150ms ease",
1788
- backgroundColor: resizingColId === col.id ? "var(--primary, #6366f1)" : hoveredColId === col.id ? "color-mix(in srgb, var(--primary, #6366f1) 75%, transparent)" : "transparent"
1789
- }
1790
- }
1791
- )
1792
- }
1793
- )
1794
- ]
1795
- },
1796
- col.id
1797
- )),
1798
- onAddColumnClick && /* @__PURE__ */ jsx8(
1799
- "th",
1800
- {
1801
- className: "w-10 min-w-[2.5rem] px-1 text-center bg-muted/40 hover:bg-muted cursor-pointer transition-colors",
1802
- onClick: onAddColumnClick,
1803
- title: "Add Column",
1804
- children: /* @__PURE__ */ jsx8("span", { className: "text-xs font-bold text-muted-foreground", children: "+" })
1805
- }
1806
- )
1807
- ] }) }),
1808
- /* @__PURE__ */ jsx8("tbody", { children: view.rows.map((viewRow) => {
1809
- if (viewRow.kind === "group") {
1810
- return /* @__PURE__ */ jsx8(
1811
- SpreadsheetGroupRow,
1812
- {
1813
- group: viewRow.group,
1814
- columns: view.columns,
1815
- groupColId: state.document.table?.grouping?.colId ?? "",
1816
- onToggle: state.toggleGroupCollapsed,
1817
- trailingCell: !!onAddColumnClick
1818
- },
1819
- `group:${viewRow.group.key}`
1820
- );
1821
- }
1822
- const displayRowIndex = viewRow.index;
1823
- const selection = state.selectedRange ? normalizeRange2(state.selectedRange) : null;
1824
- return /* @__PURE__ */ jsxs7(
1825
- "tr",
1826
- {
1827
- "data-row": displayRowIndex,
1828
- className: `border-b border-border hover:bg-muted/10 transition-colors ${viewRow.section !== "body" ? "bg-muted/30 font-semibold" : ""}`,
1829
- style: { height: viewRow.row.height || 32 },
1830
- children: [
1831
- /* @__PURE__ */ jsx8(
1832
- "td",
1833
- {
1834
- 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",
1835
- style: viewRow.frozen ? { top: COLUMN_HEADER_HEIGHT + viewRow.stickyTop, zIndex: 15 } : void 0,
1836
- children: displayRowIndex + 1
1837
- }
1838
- ),
1839
- view.columns.map((viewCol) => {
1840
- const span = view.getCellSpan(displayRowIndex, viewCol.index);
1841
- if (!span) return null;
1842
- const rIdx = span.source.row;
1843
- const cIdx = span.source.col;
1844
- const row = state.document.rows[rIdx];
1845
- const col = state.document.columns[cIdx];
1846
- const cell = state.getCell(row.id, col.id);
1847
- const selected = isCellSelected(rIdx, cIdx);
1848
- const active = isActiveCell(rIdx, cIdx);
1849
- const isEditing = state.editingCell?.row === rIdx && state.editingCell?.col === cIdx;
1850
- const inFillPreview = !!fillTarget && !selected && rangeContains(fillTarget, displayRowIndex, viewCol.index);
1851
- const showFillHandle = !readOnly && !isEditing && !!selection && (span.merge ? rangeContains(span.merge, selection.endRow, selection.endCol) : selection.endRow === rIdx && selection.endCol === cIdx);
1852
- return /* @__PURE__ */ jsxs7(
1853
- "td",
1854
- {
1855
- "data-row": rIdx,
1856
- "data-col": cIdx,
1857
- rowSpan: span.rowSpan,
1858
- colSpan: span.colSpan,
1859
- className: `relative border-r border-border px-2 py-1 truncate max-w-0 text-foreground transition-colors ${selected ? "bg-primary/10" : ""} ${active ? "ring-2 ring-primary ring-inset z-10" : ""} ${viewCol.header ? "font-semibold" : ""}`,
1860
- style: {
1861
- maxWidth: 0,
1862
- overflow: "hidden",
1863
- ...frozenCellStyle(viewRow, viewCol),
1864
- ...inFillPreview ? { outline: "1px dashed var(--primary, #6366f1)", outlineOffset: -1 } : {}
1865
- },
1866
- onClick: (e) => handleCellClick(rIdx, cIdx, e),
1867
- onDoubleClick: () => handleCellDblClick(rIdx, cIdx),
1868
- onContextMenu: (e) => handleCellContextMenu(rIdx, cIdx, e),
1869
- onMouseOver: () => extendFill(displayRowIndex, viewCol.index),
1870
- children: [
1871
- isEditing ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
1872
- /* @__PURE__ */ jsx8(
1873
- "input",
1874
- {
1875
- ref: cellInputRef,
1876
- type: "text",
1877
- className: "absolute inset-0 w-full h-full px-2 py-1 bg-background text-foreground outline-none font-sans text-xs border-2 border-primary z-20",
1878
- value: state.draftValue,
1879
- onChange: (e) => state.setDraftValue(e.target.value),
1880
- onBlur: () => state.commitEditing(),
1881
- autoFocus: true
1882
- }
1883
- ),
1884
- /* @__PURE__ */ jsx8(FormulaHints, { inputRef: cellInputRef, value: state.draftValue, onAccept: state.setDraftValue })
1885
- ] }) : customCellRenderer ? customCellRenderer({ row, col, cell }) : /* @__PURE__ */ jsx8(
1886
- "span",
1887
- {
1888
- className: `truncate block max-w-full ${cell.error ? "text-destructive font-semibold" : ""}`,
1889
- title: cell.error ? cell.errorMessage ?? cell.error : void 0,
1890
- children: cell.error || (cell.value ?? "")
1891
- }
1892
- ),
1893
- showFillHandle && /* @__PURE__ */ jsx8(
1894
- "div",
1895
- {
1896
- className: "spreadsheet-fill-handle",
1897
- "aria-label": "Fill handle",
1898
- onMouseDown: startFill,
1899
- onClick: (e) => e.stopPropagation(),
1900
- style: { position: "absolute", right: 0, bottom: 0, width: 7, height: 7, background: "var(--primary, #6366f1)", cursor: "crosshair", zIndex: 25 }
1901
- }
1902
- )
1903
- ]
1904
- },
1905
- viewCol.column.id
1906
- );
1907
- }),
1908
- onAddColumnClick && /* @__PURE__ */ jsx8("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
1909
- ]
1910
- },
1911
- viewRow.row.id
1912
- );
1913
- }) })
1914
- ] }) }),
1915
- menu && /* @__PURE__ */ jsx8(
1916
- SpreadsheetContextMenu,
1917
- {
1918
- items: menu.items,
1919
- x: menu.x,
1920
- y: menu.y,
1921
- onClose: () => {
1922
- setMenu(null);
1923
- gridRef.current?.focus();
1924
- }
1925
- }
1926
- ),
1927
- /* @__PURE__ */ jsx8("style", { children: `
1928
- .col-resize-handle:hover .col-resize-handle-line {
1929
- background-color: var(--primary, #6366f1);
1930
- opacity: 0.75;
1931
- }
1932
- .col-resize-handle.active .col-resize-handle-line {
1933
- background-color: var(--primary, #6366f1);
1934
- opacity: 1;
1935
- }
1936
- table.table-fixed th {
1937
- overflow: hidden;
1938
- }
1939
- table.table-fixed td {
1940
- max-width: 0;
1941
- overflow: hidden;
1942
- }
1943
- ` })
1944
- ]
1945
- }
1946
- );
1947
- };
1948
-
1949
865
  // src/slots.tsx
1950
- import { createContext, useContext, useMemo as useMemo9 } from "react";
1951
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
866
+ import { createContext, useContext, useMemo as useMemo6 } from "react";
867
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1952
868
  var defaultSlots = {
1953
- Button: (props) => /* @__PURE__ */ jsx9(
869
+ Button: (props) => /* @__PURE__ */ jsx5(
1954
870
  "button",
1955
871
  {
1956
872
  type: "button",
@@ -1960,26 +876,26 @@ var defaultSlots = {
1960
876
  children: props.children
1961
877
  }
1962
878
  ),
1963
- Tooltip: (props) => /* @__PURE__ */ jsx9("div", { className: `pixerate-tooltip-container ${props.className || ""}`, title: String(props.content), children: props.children }),
1964
- NodeHeader: (props) => /* @__PURE__ */ jsxs8("div", { className: `pixerate-node-header flex items-center justify-between ${props.className || ""}`, children: [
1965
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
879
+ Tooltip: (props) => /* @__PURE__ */ jsx5("div", { className: `pixerate-tooltip-container ${props.className || ""}`, title: String(props.content), children: props.children }),
880
+ NodeHeader: (props) => /* @__PURE__ */ jsxs4("div", { className: `pixerate-node-header flex items-center justify-between ${props.className || ""}`, children: [
881
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
1966
882
  props.icon,
1967
- /* @__PURE__ */ jsx9("span", { className: "font-semibold", children: props.title })
883
+ /* @__PURE__ */ jsx5("span", { className: "font-semibold", children: props.title })
1968
884
  ] }),
1969
- props.onClose && /* @__PURE__ */ jsx9("button", { type: "button", onClick: props.onClose, className: "opacity-70 hover:opacity-100", children: "\xD7" })
885
+ props.onClose && /* @__PURE__ */ jsx5("button", { type: "button", onClick: props.onClose, className: "opacity-70 hover:opacity-100", children: "\xD7" })
1970
886
  ] })
1971
887
  };
1972
888
  var EditorSlotsContext = createContext(defaultSlots);
1973
889
  function EditorSlotsProvider({ slots, children }) {
1974
- const mergedSlots = useMemo9(() => ({ ...defaultSlots, ...slots }), [slots]);
1975
- return /* @__PURE__ */ jsx9(EditorSlotsContext.Provider, { value: mergedSlots, children });
890
+ const mergedSlots = useMemo6(() => ({ ...defaultSlots, ...slots }), [slots]);
891
+ return /* @__PURE__ */ jsx5(EditorSlotsContext.Provider, { value: mergedSlots, children });
1976
892
  }
1977
893
  function useEditorSlots() {
1978
894
  return useContext(EditorSlotsContext);
1979
895
  }
1980
896
 
1981
897
  // src/review/useReview.ts
1982
- import { useCallback as useCallback7, useMemo as useMemo10, useSyncExternalStore as useSyncExternalStore4 } from "react";
898
+ import { useCallback as useCallback4, useMemo as useMemo7, useSyncExternalStore as useSyncExternalStore4 } from "react";
1983
899
  import {
1984
900
  getCommentStore,
1985
901
  getTrackChangesStore
@@ -2001,23 +917,23 @@ function isCommentStore(value) {
2001
917
  }
2002
918
  function useComments(source) {
2003
919
  const store = isCommentStore(source) ? source : getCommentStore(source);
2004
- const subscribe = useCallback7(
920
+ const subscribe = useCallback4(
2005
921
  (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
2006
922
  [store]
2007
923
  );
2008
924
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_COMMENTS;
2009
925
  const snapshot = useSyncExternalStore4(subscribe, getSnapshot, getSnapshot);
2010
- return useMemo10(() => ({ ...snapshot, store }), [snapshot, store]);
926
+ return useMemo7(() => ({ ...snapshot, store }), [snapshot, store]);
2011
927
  }
2012
928
  function useTrackChanges(editor) {
2013
929
  const store = getTrackChangesStore(editor ?? null);
2014
- const subscribe = useCallback7(
930
+ const subscribe = useCallback4(
2015
931
  (onChange) => store ? store.subscribe(() => onChange()) : noopUnsubscribe,
2016
932
  [store]
2017
933
  );
2018
934
  const getSnapshot = () => store?.getSnapshot() ?? EMPTY_TRACK_CHANGES;
2019
935
  const snapshot = useSyncExternalStore4(subscribe, getSnapshot, getSnapshot);
2020
- return useMemo10(() => {
936
+ return useMemo7(() => {
2021
937
  const run = (fn) => () => {
2022
938
  if (editor && !editor.isDestroyed) fn(editor);
2023
939
  };
@@ -2037,12 +953,12 @@ function useTrackChanges(editor) {
2037
953
  }
2038
954
 
2039
955
  // src/review/CommentsPanel.tsx
2040
- import { useState as useState9 } from "react";
956
+ import { useState as useState5 } from "react";
2041
957
  import {
2042
958
  formatReviewTime,
2043
959
  getCommentAuthor
2044
960
  } from "@pixerate/editor";
2045
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
961
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2046
962
  var buttonClass = "rounded px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50";
2047
963
  var primaryButtonClass = "rounded px-2 py-1 text-xs bg-primary/10 text-primary hover:bg-primary/20 disabled:opacity-50";
2048
964
  var textareaClass = "w-full resize-y rounded-md border border-input bg-background p-2 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary";
@@ -2056,18 +972,18 @@ var CommentsPanel = ({
2056
972
  emptyMessage = "No comments yet. Select text and add a comment."
2057
973
  }) => {
2058
974
  const { threads, activeThreadId, store } = useComments(storeProp ?? editor);
2059
- const [showResolved, setShowResolved] = useState9(showResolvedInitial);
975
+ const [showResolved, setShowResolved] = useState5(showResolvedInitial);
2060
976
  const author = authorProp ?? getCommentAuthor(editor) ?? { id: "anonymous", name: "Anonymous" };
2061
977
  const visible = threads.filter((t) => showResolved || !t.resolved);
2062
978
  const resolvedCount = threads.filter((t) => t.resolved).length;
2063
- return /* @__PURE__ */ jsxs9("aside", { className, "aria-label": "Comments", children: [
2064
- /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between gap-2", children: [
2065
- /* @__PURE__ */ jsxs9("span", { className: "text-xs font-medium text-muted-foreground", children: [
979
+ return /* @__PURE__ */ jsxs5("aside", { className, "aria-label": "Comments", children: [
980
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between gap-2", children: [
981
+ /* @__PURE__ */ jsxs5("span", { className: "text-xs font-medium text-muted-foreground", children: [
2066
982
  threads.length - resolvedCount,
2067
983
  " open",
2068
984
  resolvedCount > 0 ? ` \xB7 ${resolvedCount} resolved` : ""
2069
985
  ] }),
2070
- resolvedCount > 0 && /* @__PURE__ */ jsx10(
986
+ resolvedCount > 0 && /* @__PURE__ */ jsx6(
2071
987
  "button",
2072
988
  {
2073
989
  type: "button",
@@ -2078,7 +994,7 @@ var CommentsPanel = ({
2078
994
  }
2079
995
  )
2080
996
  ] }),
2081
- visible.length === 0 ? /* @__PURE__ */ jsx10("p", { className: "text-xs text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ jsx10("ul", { className: "flex flex-col gap-2", role: "list", children: visible.map((thread) => /* @__PURE__ */ jsx10(
997
+ visible.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "text-xs text-muted-foreground", children: emptyMessage }) : /* @__PURE__ */ jsx6("ul", { className: "flex flex-col gap-2", role: "list", children: visible.map((thread) => /* @__PURE__ */ jsx6(
2082
998
  CommentThreadCard,
2083
999
  {
2084
1000
  thread,
@@ -2100,7 +1016,7 @@ var CommentThreadCard = ({
2100
1016
  author,
2101
1017
  formatTime
2102
1018
  }) => {
2103
- const [draft, setDraft] = useState9("");
1019
+ const [draft, setDraft] = useState5("");
2104
1020
  const isDraft = thread.body === "";
2105
1021
  const quote = thread.anchor?.quote ?? thread.lastQuote ?? "";
2106
1022
  const live = editor && !editor.isDestroyed ? editor : null;
@@ -2126,7 +1042,7 @@ var CommentThreadCard = ({
2126
1042
  setDraft("");
2127
1043
  };
2128
1044
  const stop = (e) => e.stopPropagation();
2129
- return /* @__PURE__ */ jsxs9(
1045
+ return /* @__PURE__ */ jsxs5(
2130
1046
  "li",
2131
1047
  {
2132
1048
  className: `flex flex-col gap-2 rounded-md border bg-background p-3 cursor-pointer ${active ? "border-primary ring-1 ring-primary" : "border-border"} ${thread.resolved ? "opacity-70" : ""}`,
@@ -2142,24 +1058,24 @@ var CommentThreadCard = ({
2142
1058
  }
2143
1059
  },
2144
1060
  children: [
2145
- /* @__PURE__ */ jsxs9("div", { className: "flex items-baseline justify-between gap-2", children: [
2146
- /* @__PURE__ */ jsx10("span", { className: "font-medium", children: thread.author.name }),
2147
- /* @__PURE__ */ jsx10("span", { className: "text-xs text-muted-foreground", children: formatTime(thread.createdAt) })
1061
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-baseline justify-between gap-2", children: [
1062
+ /* @__PURE__ */ jsx6("span", { className: "font-medium", children: thread.author.name }),
1063
+ /* @__PURE__ */ jsx6("span", { className: "text-xs text-muted-foreground", children: formatTime(thread.createdAt) })
2148
1064
  ] }),
2149
- quote && /* @__PURE__ */ jsx10("blockquote", { className: "border-l-2 border-border pl-2 text-xs text-muted-foreground line-clamp-2", children: quote }),
2150
- thread.orphaned && /* @__PURE__ */ jsx10("span", { className: "text-xs text-destructive", children: "The commented text was deleted." }),
2151
- thread.resolved && /* @__PURE__ */ jsx10("span", { className: "text-xs text-muted-foreground", children: "Resolved" }),
2152
- !isDraft && /* @__PURE__ */ jsx10("p", { className: "whitespace-pre-wrap break-words", children: thread.body }),
2153
- thread.replies.length > 0 && /* @__PURE__ */ jsx10("ul", { className: "flex flex-col gap-2 border-l border-border pl-3", role: "list", children: thread.replies.map((reply) => /* @__PURE__ */ jsxs9("li", { className: "flex flex-col gap-0.5", "data-reply-id": reply.id, children: [
2154
- /* @__PURE__ */ jsxs9("span", { className: "text-xs", children: [
2155
- /* @__PURE__ */ jsx10("span", { className: "font-medium", children: reply.author.name }),
1065
+ quote && /* @__PURE__ */ jsx6("blockquote", { className: "border-l-2 border-border pl-2 text-xs text-muted-foreground line-clamp-2", children: quote }),
1066
+ thread.orphaned && /* @__PURE__ */ jsx6("span", { className: "text-xs text-destructive", children: "The commented text was deleted." }),
1067
+ thread.resolved && /* @__PURE__ */ jsx6("span", { className: "text-xs text-muted-foreground", children: "Resolved" }),
1068
+ !isDraft && /* @__PURE__ */ jsx6("p", { className: "whitespace-pre-wrap break-words", children: thread.body }),
1069
+ thread.replies.length > 0 && /* @__PURE__ */ jsx6("ul", { className: "flex flex-col gap-2 border-l border-border pl-3", role: "list", children: thread.replies.map((reply) => /* @__PURE__ */ jsxs5("li", { className: "flex flex-col gap-0.5", "data-reply-id": reply.id, children: [
1070
+ /* @__PURE__ */ jsxs5("span", { className: "text-xs", children: [
1071
+ /* @__PURE__ */ jsx6("span", { className: "font-medium", children: reply.author.name }),
2156
1072
  " ",
2157
- /* @__PURE__ */ jsx10("span", { className: "text-muted-foreground", children: formatTime(reply.createdAt) })
1073
+ /* @__PURE__ */ jsx6("span", { className: "text-muted-foreground", children: formatTime(reply.createdAt) })
2158
1074
  ] }),
2159
- /* @__PURE__ */ jsx10("p", { className: "whitespace-pre-wrap break-words", children: reply.body })
1075
+ /* @__PURE__ */ jsx6("p", { className: "whitespace-pre-wrap break-words", children: reply.body })
2160
1076
  ] }, reply.id)) }),
2161
- (active || isDraft) && !thread.resolved && /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-1", onClick: stop, children: [
2162
- /* @__PURE__ */ jsx10(
1077
+ (active || isDraft) && !thread.resolved && /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", onClick: stop, children: [
1078
+ /* @__PURE__ */ jsx6(
2163
1079
  "textarea",
2164
1080
  {
2165
1081
  className: textareaClass,
@@ -2177,9 +1093,9 @@ var CommentThreadCard = ({
2177
1093
  }
2178
1094
  }
2179
1095
  ),
2180
- /* @__PURE__ */ jsxs9("div", { className: "flex justify-end gap-1", children: [
2181
- isDraft && /* @__PURE__ */ jsx10("button", { type: "button", className: buttonClass, onClick: remove, children: "Cancel" }),
2182
- /* @__PURE__ */ jsx10(
1096
+ /* @__PURE__ */ jsxs5("div", { className: "flex justify-end gap-1", children: [
1097
+ isDraft && /* @__PURE__ */ jsx6("button", { type: "button", className: buttonClass, onClick: remove, children: "Cancel" }),
1098
+ /* @__PURE__ */ jsx6(
2183
1099
  "button",
2184
1100
  {
2185
1101
  type: "button",
@@ -2191,9 +1107,9 @@ var CommentThreadCard = ({
2191
1107
  )
2192
1108
  ] })
2193
1109
  ] }),
2194
- !isDraft && /* @__PURE__ */ jsxs9("div", { className: "flex justify-end gap-1", onClick: stop, children: [
2195
- /* @__PURE__ */ jsx10("button", { type: "button", className: buttonClass, onClick: () => setResolved(!thread.resolved), children: thread.resolved ? "Reopen" : "Resolve" }),
2196
- /* @__PURE__ */ jsx10(
1110
+ !isDraft && /* @__PURE__ */ jsxs5("div", { className: "flex justify-end gap-1", onClick: stop, children: [
1111
+ /* @__PURE__ */ jsx6("button", { type: "button", className: buttonClass, onClick: () => setResolved(!thread.resolved), children: thread.resolved ? "Reopen" : "Resolve" }),
1112
+ /* @__PURE__ */ jsx6(
2197
1113
  "button",
2198
1114
  {
2199
1115
  type: "button",
@@ -2209,18 +1125,21 @@ var CommentThreadCard = ({
2209
1125
  };
2210
1126
 
2211
1127
  // src/review/ReviewToolbar.tsx
2212
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1128
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1129
+ var defaultDescriptionClass = "max-w-[16rem] truncate px-1 text-xs text-muted-foreground";
2213
1130
  var buttonClass2 = "rounded px-2 py-1 text-xs text-foreground hover:bg-muted disabled:opacity-40 disabled:hover:bg-transparent";
2214
1131
  var ReviewToolbar = ({
2215
1132
  editor,
2216
- className = "flex flex-wrap items-center gap-1 rounded-md border border-border bg-background p-1 text-sm"
1133
+ className = "flex flex-wrap items-center gap-1 rounded-md border border-border bg-background p-1 text-sm",
1134
+ showDescription = true,
1135
+ descriptionClassName = defaultDescriptionClass
2217
1136
  }) => {
2218
1137
  const review = useTrackChanges(editor);
2219
1138
  const count = review.changes.length;
2220
1139
  const current = review.currentChange;
2221
1140
  const index = current ? review.changes.findIndex((c) => c.id === current.id) + 1 : 0;
2222
- return /* @__PURE__ */ jsxs10("div", { className, role: "toolbar", "aria-label": "Review", children: [
2223
- /* @__PURE__ */ jsx11(
1141
+ return /* @__PURE__ */ jsxs6("div", { className, role: "toolbar", "aria-label": "Review", children: [
1142
+ /* @__PURE__ */ jsx7(
2224
1143
  "button",
2225
1144
  {
2226
1145
  type: "button",
@@ -2231,8 +1150,8 @@ var ReviewToolbar = ({
2231
1150
  children: review.enabled ? "Tracking on" : "Track changes"
2232
1151
  }
2233
1152
  ),
2234
- /* @__PURE__ */ jsx11("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
2235
- /* @__PURE__ */ jsx11(
1153
+ /* @__PURE__ */ jsx7("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
1154
+ /* @__PURE__ */ jsx7(
2236
1155
  "button",
2237
1156
  {
2238
1157
  type: "button",
@@ -2243,8 +1162,8 @@ var ReviewToolbar = ({
2243
1162
  children: "\u2039"
2244
1163
  }
2245
1164
  ),
2246
- /* @__PURE__ */ jsx11("span", { className: "min-w-[4rem] text-center text-xs text-muted-foreground", "aria-live": "polite", children: count === 0 ? "No changes" : current ? `${index} of ${count}` : `${count} changes` }),
2247
- /* @__PURE__ */ jsx11(
1165
+ /* @__PURE__ */ jsx7("span", { className: "min-w-[4rem] text-center text-xs text-muted-foreground", "aria-live": "polite", children: count === 0 ? "No changes" : current ? `${index} of ${count}` : `${count} changes` }),
1166
+ /* @__PURE__ */ jsx7(
2248
1167
  "button",
2249
1168
  {
2250
1169
  type: "button",
@@ -2255,8 +1174,20 @@ var ReviewToolbar = ({
2255
1174
  children: "\u203A"
2256
1175
  }
2257
1176
  ),
2258
- /* @__PURE__ */ jsx11("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
2259
- /* @__PURE__ */ jsx11(
1177
+ showDescription && current && /* @__PURE__ */ jsxs6(
1178
+ "span",
1179
+ {
1180
+ className: descriptionClassName,
1181
+ "data-change-type": current.type,
1182
+ title: `${current.author.name}: ${current.description}`,
1183
+ children: [
1184
+ current.author.name ? `${current.author.name}: ` : "",
1185
+ current.description
1186
+ ]
1187
+ }
1188
+ ),
1189
+ /* @__PURE__ */ jsx7("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": "true" }),
1190
+ /* @__PURE__ */ jsx7(
2260
1191
  "button",
2261
1192
  {
2262
1193
  type: "button",
@@ -2266,7 +1197,7 @@ var ReviewToolbar = ({
2266
1197
  children: "Accept"
2267
1198
  }
2268
1199
  ),
2269
- /* @__PURE__ */ jsx11(
1200
+ /* @__PURE__ */ jsx7(
2270
1201
  "button",
2271
1202
  {
2272
1203
  type: "button",
@@ -2276,18 +1207,18 @@ var ReviewToolbar = ({
2276
1207
  children: "Reject"
2277
1208
  }
2278
1209
  ),
2279
- /* @__PURE__ */ jsx11("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.acceptAll, children: "Accept all" }),
2280
- /* @__PURE__ */ jsx11("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.rejectAll, children: "Reject all" })
1210
+ /* @__PURE__ */ jsx7("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.acceptAll, children: "Accept all" }),
1211
+ /* @__PURE__ */ jsx7("button", { type: "button", className: buttonClass2, disabled: count === 0, onClick: review.rejectAll, children: "Reject all" })
2281
1212
  ] });
2282
1213
  };
2283
1214
 
2284
1215
  // src/review/CommentButton.tsx
2285
- import { jsx as jsx12 } from "react/jsx-runtime";
1216
+ import { jsx as jsx8 } from "react/jsx-runtime";
2286
1217
  var CommentButton = ({
2287
1218
  editor,
2288
1219
  className = "rounded px-2 py-1 text-xs text-popover-foreground hover:bg-muted",
2289
1220
  children = "Comment"
2290
- }) => /* @__PURE__ */ jsx12(
1221
+ }) => /* @__PURE__ */ jsx8(
2291
1222
  "button",
2292
1223
  {
2293
1224
  type: "button",