@pixerate/editor-react 0.12.0 → 0.13.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/README.md +4 -1
- package/dist/chunk-MHTAR3RS.js +1103 -0
- package/dist/chunk-MHTAR3RS.js.map +1 -0
- package/dist/index.cjs +28 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -159
- package/dist/index.d.ts +5 -159
- package/dist/index.js +68 -1152
- package/dist/index.js.map +1 -1
- package/dist/spreadsheet/index.cjs +1134 -0
- package/dist/spreadsheet/index.cjs.map +1 -0
- package/dist/spreadsheet/index.d.cts +160 -0
- package/dist/spreadsheet/index.d.ts +160 -0
- package/dist/spreadsheet/index.js +19 -0
- package/dist/spreadsheet/index.js.map +1 -0
- package/package.json +15 -2
|
@@ -0,0 +1,1134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/spreadsheet/index.ts
|
|
31
|
+
var spreadsheet_exports = {};
|
|
32
|
+
__export(spreadsheet_exports, {
|
|
33
|
+
FormulaHints: () => FormulaHints,
|
|
34
|
+
SpreadsheetContextMenu: () => SpreadsheetContextMenu,
|
|
35
|
+
SpreadsheetEditor: () => SpreadsheetEditor,
|
|
36
|
+
SpreadsheetGroupRow: () => SpreadsheetGroupRow,
|
|
37
|
+
useFillHandle: () => useFillHandle,
|
|
38
|
+
useSpreadsheetClipboard: () => useSpreadsheetClipboard,
|
|
39
|
+
useSpreadsheetEditor: () => useSpreadsheetEditor
|
|
40
|
+
});
|
|
41
|
+
module.exports = __toCommonJS(spreadsheet_exports);
|
|
42
|
+
|
|
43
|
+
// src/spreadsheet/useSpreadsheetEditor.ts
|
|
44
|
+
var import_react = require("react");
|
|
45
|
+
var import_spreadsheet = require("@pixerate/editor/spreadsheet");
|
|
46
|
+
function useSpreadsheetEditor(options = {}) {
|
|
47
|
+
const optionsRef = (0, import_react.useRef)(options);
|
|
48
|
+
optionsRef.current = options;
|
|
49
|
+
const settersRef = (0, import_react.useRef)({});
|
|
50
|
+
const [controller] = (0, import_react.useState)(
|
|
51
|
+
() => new import_spreadsheet.SpreadsheetController({
|
|
52
|
+
...options,
|
|
53
|
+
onDocumentChange: (updated) => {
|
|
54
|
+
settersRef.current.setDoc?.({ ...updated });
|
|
55
|
+
optionsRef.current.onDocumentChange?.(updated);
|
|
56
|
+
},
|
|
57
|
+
onSelectionChange: (active, range) => {
|
|
58
|
+
settersRef.current.setActiveCell?.(active);
|
|
59
|
+
settersRef.current.setSelectedRange?.(range);
|
|
60
|
+
optionsRef.current.onSelectionChange?.(active, range);
|
|
61
|
+
},
|
|
62
|
+
onCellCommit: (rowId, colId, raw, value) => {
|
|
63
|
+
optionsRef.current.onCellCommit?.(rowId, colId, raw, value);
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
);
|
|
67
|
+
const [doc, setDoc] = (0, import_react.useState)(() => ({ ...controller.document }));
|
|
68
|
+
const [activeCell, setActiveCell] = (0, import_react.useState)(controller.activeCell);
|
|
69
|
+
const [selectedRange, setSelectedRange] = (0, import_react.useState)(controller.selectedRange);
|
|
70
|
+
const [editingCell, setEditingCell] = (0, import_react.useState)(null);
|
|
71
|
+
const [draftValue, setDraftValue] = (0, import_react.useState)("");
|
|
72
|
+
settersRef.current = { setDoc, setActiveCell, setSelectedRange };
|
|
73
|
+
const setCellValue = (0, import_react.useCallback)((rowId, colId, raw) => {
|
|
74
|
+
controller.setCellValue(rowId, colId, raw);
|
|
75
|
+
setDoc({ ...controller.document });
|
|
76
|
+
}, [controller]);
|
|
77
|
+
const selectCell = (0, import_react.useCallback)((row, col, extendSelection = false) => {
|
|
78
|
+
controller.selectCell(row, col, extendSelection);
|
|
79
|
+
setActiveCell(controller.activeCell);
|
|
80
|
+
setSelectedRange(controller.selectedRange);
|
|
81
|
+
}, [controller]);
|
|
82
|
+
const selectRange = (0, import_react.useCallback)((range) => {
|
|
83
|
+
controller.selectRange(range);
|
|
84
|
+
setSelectedRange(controller.selectedRange);
|
|
85
|
+
}, [controller]);
|
|
86
|
+
const startEditing = (0, import_react.useCallback)((row, col, initialVal) => {
|
|
87
|
+
controller.startEditing(row, col, initialVal);
|
|
88
|
+
setActiveCell(controller.activeCell);
|
|
89
|
+
setEditingCell(controller.editingCell);
|
|
90
|
+
setDraftValue(controller.draftValue);
|
|
91
|
+
}, [controller]);
|
|
92
|
+
const commitEditing = (0, import_react.useCallback)((val) => {
|
|
93
|
+
controller.commitEditing(val);
|
|
94
|
+
setEditingCell(null);
|
|
95
|
+
setDraftValue("");
|
|
96
|
+
setDoc({ ...controller.document });
|
|
97
|
+
}, [controller]);
|
|
98
|
+
const cancelEditing = (0, import_react.useCallback)(() => {
|
|
99
|
+
controller.cancelEditing();
|
|
100
|
+
setEditingCell(null);
|
|
101
|
+
setDraftValue("");
|
|
102
|
+
}, [controller]);
|
|
103
|
+
const moveCursor = (0, import_react.useCallback)((dir, extend = false) => {
|
|
104
|
+
controller.moveCursor(dir, extend);
|
|
105
|
+
setActiveCell(controller.activeCell);
|
|
106
|
+
setSelectedRange(controller.selectedRange);
|
|
107
|
+
}, [controller]);
|
|
108
|
+
const insertColumn = (0, import_react.useCallback)((index, config) => {
|
|
109
|
+
controller.insertColumn(index, config);
|
|
110
|
+
setDoc({ ...controller.document });
|
|
111
|
+
}, [controller]);
|
|
112
|
+
const deleteColumn = (0, import_react.useCallback)((colId) => {
|
|
113
|
+
controller.deleteColumn(colId);
|
|
114
|
+
setDoc({ ...controller.document });
|
|
115
|
+
}, [controller]);
|
|
116
|
+
const insertRow = (0, import_react.useCallback)((index, config) => {
|
|
117
|
+
controller.insertRow(index, config);
|
|
118
|
+
setDoc({ ...controller.document });
|
|
119
|
+
}, [controller]);
|
|
120
|
+
const deleteRow = (0, import_react.useCallback)((rowId) => {
|
|
121
|
+
controller.deleteRow(rowId);
|
|
122
|
+
setDoc({ ...controller.document });
|
|
123
|
+
}, [controller]);
|
|
124
|
+
const setColumnWidth = (0, import_react.useCallback)((colId, width) => {
|
|
125
|
+
controller.setColumnWidth(colId, width);
|
|
126
|
+
setDoc({ ...controller.document });
|
|
127
|
+
}, [controller]);
|
|
128
|
+
const autoFitColumnWidth = (0, import_react.useCallback)((colId, customWidth) => {
|
|
129
|
+
controller.autoFitColumnWidth(colId, customWidth);
|
|
130
|
+
setDoc({ ...controller.document });
|
|
131
|
+
}, [controller]);
|
|
132
|
+
const registerDataSource = (0, import_react.useCallback)((ds) => {
|
|
133
|
+
controller.registerDataSource(ds);
|
|
134
|
+
setDoc({ ...controller.document });
|
|
135
|
+
}, [controller]);
|
|
136
|
+
const setPrimaryDataSource = (0, import_react.useCallback)((id) => {
|
|
137
|
+
controller.setPrimaryDataSource(id);
|
|
138
|
+
setDoc({ ...controller.document });
|
|
139
|
+
}, [controller]);
|
|
140
|
+
const recalculateAll = (0, import_react.useCallback)(() => {
|
|
141
|
+
controller.recalculateAll();
|
|
142
|
+
setDoc({ ...controller.document });
|
|
143
|
+
}, [controller]);
|
|
144
|
+
const undo = (0, import_react.useCallback)(() => {
|
|
145
|
+
controller.undo();
|
|
146
|
+
setDoc({ ...controller.document });
|
|
147
|
+
}, [controller]);
|
|
148
|
+
const redo = (0, import_react.useCallback)(() => {
|
|
149
|
+
controller.redo();
|
|
150
|
+
setDoc({ ...controller.document });
|
|
151
|
+
}, [controller]);
|
|
152
|
+
const tableActions = (0, import_react.useMemo)(() => (0, import_spreadsheet.bindSpreadsheetTableActions)(controller), [controller]);
|
|
153
|
+
const viewModel = (0, import_react.useMemo)(() => controller.getViewModel(), [controller, doc]);
|
|
154
|
+
return {
|
|
155
|
+
document: doc,
|
|
156
|
+
activeCell,
|
|
157
|
+
selectedRange,
|
|
158
|
+
editingCell,
|
|
159
|
+
draftValue,
|
|
160
|
+
setDraftValue,
|
|
161
|
+
controller,
|
|
162
|
+
getCell: (rowId, colId) => controller.getCell(rowId, colId),
|
|
163
|
+
setCellValue,
|
|
164
|
+
selectCell,
|
|
165
|
+
selectRange,
|
|
166
|
+
startEditing,
|
|
167
|
+
commitEditing,
|
|
168
|
+
cancelEditing,
|
|
169
|
+
moveCursor,
|
|
170
|
+
insertColumn,
|
|
171
|
+
deleteColumn,
|
|
172
|
+
insertRow,
|
|
173
|
+
deleteRow,
|
|
174
|
+
setColumnWidth,
|
|
175
|
+
autoFitColumnWidth,
|
|
176
|
+
loadDocument: (document2) => {
|
|
177
|
+
controller.loadDocument(document2);
|
|
178
|
+
setDoc({ ...controller.document });
|
|
179
|
+
setActiveCell(null);
|
|
180
|
+
setSelectedRange(null);
|
|
181
|
+
setEditingCell(null);
|
|
182
|
+
setDraftValue("");
|
|
183
|
+
},
|
|
184
|
+
syncWithDataSource: () => {
|
|
185
|
+
controller.syncWithDataSource();
|
|
186
|
+
controller.recalculateAll();
|
|
187
|
+
setDoc({ ...controller.document });
|
|
188
|
+
},
|
|
189
|
+
registerDataSource,
|
|
190
|
+
setPrimaryDataSource,
|
|
191
|
+
recalculateAll,
|
|
192
|
+
exportToTsv: (range) => controller.exportToTsv(range),
|
|
193
|
+
importFromTsv: (data) => {
|
|
194
|
+
controller.importFromTsv(data);
|
|
195
|
+
setDoc({ ...controller.document });
|
|
196
|
+
},
|
|
197
|
+
...tableActions,
|
|
198
|
+
viewModel,
|
|
199
|
+
undo,
|
|
200
|
+
redo
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/spreadsheet/SpreadsheetEditor.tsx
|
|
205
|
+
var import_react5 = require("react");
|
|
206
|
+
var import_spreadsheet4 = require("@pixerate/editor/spreadsheet");
|
|
207
|
+
|
|
208
|
+
// src/spreadsheet/SpreadsheetContextMenu.tsx
|
|
209
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
210
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
211
|
+
var SpreadsheetContextMenu = ({ items, x, y, onClose }) => {
|
|
212
|
+
const menuRef = (0, import_react2.useRef)(null);
|
|
213
|
+
const onCloseRef = (0, import_react2.useRef)(onClose);
|
|
214
|
+
onCloseRef.current = onClose;
|
|
215
|
+
(0, import_react2.useEffect)(() => {
|
|
216
|
+
const handlePointer = (e) => {
|
|
217
|
+
if (!menuRef.current?.contains(e.target)) onCloseRef.current();
|
|
218
|
+
};
|
|
219
|
+
const handleKey = (e) => {
|
|
220
|
+
if (e.key === "Escape") onCloseRef.current();
|
|
221
|
+
};
|
|
222
|
+
window.addEventListener("mousedown", handlePointer);
|
|
223
|
+
window.addEventListener("keydown", handleKey);
|
|
224
|
+
return () => {
|
|
225
|
+
window.removeEventListener("mousedown", handlePointer);
|
|
226
|
+
window.removeEventListener("keydown", handleKey);
|
|
227
|
+
};
|
|
228
|
+
}, []);
|
|
229
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
230
|
+
"div",
|
|
231
|
+
{
|
|
232
|
+
ref: menuRef,
|
|
233
|
+
role: "menu",
|
|
234
|
+
"aria-label": "Table actions",
|
|
235
|
+
className: "fixed z-50 min-w-[14rem] rounded-md border border-border bg-popover py-1 text-xs text-popover-foreground shadow-lg",
|
|
236
|
+
style: { left: x, top: y, background: "var(--popover, var(--background, #fff))" },
|
|
237
|
+
onContextMenu: (e) => e.preventDefault(),
|
|
238
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
239
|
+
children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.default.Fragment, { children: [
|
|
240
|
+
item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { role: "separator", className: "my-1 h-px bg-border" }),
|
|
241
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
242
|
+
"button",
|
|
243
|
+
{
|
|
244
|
+
type: "button",
|
|
245
|
+
role: "menuitem",
|
|
246
|
+
"data-action": item.id,
|
|
247
|
+
disabled: item.disabled,
|
|
248
|
+
className: "block w-full px-3 py-1 text-left hover:bg-muted disabled:cursor-default disabled:opacity-40 disabled:hover:bg-transparent",
|
|
249
|
+
onClick: () => {
|
|
250
|
+
onClose();
|
|
251
|
+
item.run();
|
|
252
|
+
},
|
|
253
|
+
children: item.label
|
|
254
|
+
}
|
|
255
|
+
)
|
|
256
|
+
] }, item.id))
|
|
257
|
+
}
|
|
258
|
+
);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// src/spreadsheet/SpreadsheetGroupRow.tsx
|
|
262
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
263
|
+
var formatSummary = (value) => value === null ? "\u2013" : String(value);
|
|
264
|
+
var SpreadsheetGroupRow = ({
|
|
265
|
+
group,
|
|
266
|
+
columns,
|
|
267
|
+
groupColId,
|
|
268
|
+
onToggle,
|
|
269
|
+
trailingCell
|
|
270
|
+
}) => {
|
|
271
|
+
const labelColId = columns.some((c) => c.column.id === groupColId) ? groupColId : columns[0]?.column.id;
|
|
272
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("tr", { className: "border-b border-border bg-muted/40 font-semibold", "data-group-key": group.key, style: { height: 28 }, children: [
|
|
273
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted/60 border-r border-border text-center", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
274
|
+
"button",
|
|
275
|
+
{
|
|
276
|
+
type: "button",
|
|
277
|
+
"aria-label": `${group.collapsed ? "Expand" : "Collapse"} group ${group.label}`,
|
|
278
|
+
"aria-expanded": !group.collapsed,
|
|
279
|
+
className: "h-5 w-5 rounded text-[10px] text-muted-foreground hover:bg-muted",
|
|
280
|
+
onClick: (e) => {
|
|
281
|
+
e.stopPropagation();
|
|
282
|
+
onToggle(group.key);
|
|
283
|
+
},
|
|
284
|
+
children: group.collapsed ? "\u25B8" : "\u25BE"
|
|
285
|
+
}
|
|
286
|
+
) }),
|
|
287
|
+
columns.map(({ column }) => {
|
|
288
|
+
const summary = group.summaries[column.id];
|
|
289
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "truncate", children: [
|
|
290
|
+
group.label,
|
|
291
|
+
" ",
|
|
292
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-normal text-muted-foreground", children: [
|
|
293
|
+
"(",
|
|
294
|
+
group.rowIndices.length,
|
|
295
|
+
")"
|
|
296
|
+
] })
|
|
297
|
+
] }) : summary !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "truncate", "data-summary": column.id, children: formatSummary(summary) }) : null }, column.id);
|
|
298
|
+
}),
|
|
299
|
+
trailingCell && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
|
|
300
|
+
] });
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// src/spreadsheet/useSpreadsheetTableInteractions.ts
|
|
304
|
+
var import_react3 = require("react");
|
|
305
|
+
var import_spreadsheet2 = require("@pixerate/editor/spreadsheet");
|
|
306
|
+
function gridHasFocus(grid) {
|
|
307
|
+
if (!grid || typeof document === "undefined") return false;
|
|
308
|
+
const active = document.activeElement;
|
|
309
|
+
if (!active || !grid.contains(active)) return false;
|
|
310
|
+
return !(active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement);
|
|
311
|
+
}
|
|
312
|
+
function useSpreadsheetClipboard(gridRef, state, readOnly) {
|
|
313
|
+
const stateRef = (0, import_react3.useRef)(state);
|
|
314
|
+
stateRef.current = state;
|
|
315
|
+
const readOnlyRef = (0, import_react3.useRef)(readOnly);
|
|
316
|
+
readOnlyRef.current = readOnly;
|
|
317
|
+
const copiedRef = (0, import_react3.useRef)(false);
|
|
318
|
+
(0, import_react3.useEffect)(() => {
|
|
319
|
+
if (typeof document === "undefined") return;
|
|
320
|
+
const handleCopy = (e) => {
|
|
321
|
+
const s = stateRef.current;
|
|
322
|
+
if (!gridHasFocus(gridRef.current) || s.editingCell || !e.clipboardData || !s.selectedRange) return;
|
|
323
|
+
for (const [type, data] of Object.entries(s.copyToClipboardData(s.selectedRange))) {
|
|
324
|
+
e.clipboardData.setData(type, data);
|
|
325
|
+
}
|
|
326
|
+
copiedRef.current = true;
|
|
327
|
+
e.preventDefault();
|
|
328
|
+
};
|
|
329
|
+
const handlePaste = (e) => {
|
|
330
|
+
const s = stateRef.current;
|
|
331
|
+
if (readOnlyRef.current || !gridHasFocus(gridRef.current) || s.editingCell || !e.clipboardData) return;
|
|
332
|
+
if (s.pasteClipboardData(e.clipboardData)) e.preventDefault();
|
|
333
|
+
};
|
|
334
|
+
document.addEventListener("copy", handleCopy);
|
|
335
|
+
document.addEventListener("paste", handlePaste);
|
|
336
|
+
return () => {
|
|
337
|
+
document.removeEventListener("copy", handleCopy);
|
|
338
|
+
document.removeEventListener("paste", handlePaste);
|
|
339
|
+
};
|
|
340
|
+
}, [gridRef]);
|
|
341
|
+
const copyShortcutFallback = (0, import_react3.useCallback)(() => {
|
|
342
|
+
copiedRef.current = false;
|
|
343
|
+
setTimeout(() => {
|
|
344
|
+
if (copiedRef.current) return;
|
|
345
|
+
const s = stateRef.current;
|
|
346
|
+
navigator.clipboard?.writeText(s.exportToTsv(s.selectedRange));
|
|
347
|
+
}, 0);
|
|
348
|
+
}, []);
|
|
349
|
+
return { copyShortcutFallback };
|
|
350
|
+
}
|
|
351
|
+
function useFillHandle(state) {
|
|
352
|
+
const [drag, setDrag] = (0, import_react3.useState)(null);
|
|
353
|
+
const dragRef = (0, import_react3.useRef)(drag);
|
|
354
|
+
dragRef.current = drag;
|
|
355
|
+
const stateRef = (0, import_react3.useRef)(state);
|
|
356
|
+
stateRef.current = state;
|
|
357
|
+
const dragging = drag !== null;
|
|
358
|
+
(0, import_react3.useEffect)(() => {
|
|
359
|
+
if (!dragging) return;
|
|
360
|
+
const handleMouseUp = () => {
|
|
361
|
+
const current = dragRef.current;
|
|
362
|
+
setDrag(null);
|
|
363
|
+
if (current?.target) stateRef.current.autofill(current.source, current.target);
|
|
364
|
+
};
|
|
365
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
366
|
+
return () => window.removeEventListener("mouseup", handleMouseUp);
|
|
367
|
+
}, [dragging]);
|
|
368
|
+
const startFill = (0, import_react3.useCallback)((e) => {
|
|
369
|
+
e.preventDefault();
|
|
370
|
+
e.stopPropagation();
|
|
371
|
+
const range = stateRef.current.selectedRange;
|
|
372
|
+
if (range) setDrag({ source: (0, import_spreadsheet2.normalizeRange)(range), target: null });
|
|
373
|
+
}, []);
|
|
374
|
+
const extendFill = (0, import_react3.useCallback)((row, col) => {
|
|
375
|
+
const current = dragRef.current;
|
|
376
|
+
if (!current) return;
|
|
377
|
+
setDrag({ source: current.source, target: (0, import_spreadsheet2.computeFillTarget)(current.source, { row, col }) });
|
|
378
|
+
}, []);
|
|
379
|
+
return { fillTarget: drag?.target ?? null, isFilling: dragging, startFill, extendFill };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// src/spreadsheet/FormulaHints.tsx
|
|
383
|
+
var import_react4 = require("react");
|
|
384
|
+
var import_spreadsheet3 = require("@pixerate/editor/spreadsheet");
|
|
385
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
386
|
+
var FormulaHints = ({ inputRef, value, onAccept, limit = 8 }) => {
|
|
387
|
+
const [caret, setCaret] = (0, import_react4.useState)(null);
|
|
388
|
+
const [focused, setFocused] = (0, import_react4.useState)(false);
|
|
389
|
+
const [selected, setSelected] = (0, import_react4.useState)(0);
|
|
390
|
+
const [dismissed, setDismissed] = (0, import_react4.useState)(false);
|
|
391
|
+
const [position, setPosition] = (0, import_react4.useState)(null);
|
|
392
|
+
const pendingCaret = (0, import_react4.useRef)(null);
|
|
393
|
+
const hints = (0, import_react4.useMemo)(
|
|
394
|
+
() => (0, import_spreadsheet3.getFormulaHints)(value, caret ?? value.length, { limit }),
|
|
395
|
+
[value, caret, limit]
|
|
396
|
+
);
|
|
397
|
+
const suggestions = dismissed ? [] : hints.suggestions;
|
|
398
|
+
const selectedIndex = Math.max(0, Math.min(selected, suggestions.length - 1));
|
|
399
|
+
const visible = focused && value.trimStart().startsWith("=") && (suggestions.length > 0 || !!hints.activeCall);
|
|
400
|
+
const latest = (0, import_react4.useRef)({ suggestions, selected: selectedIndex, value, caret, onAccept });
|
|
401
|
+
latest.current = { suggestions, selected: selectedIndex, value, caret, onAccept };
|
|
402
|
+
const accept = (index) => {
|
|
403
|
+
const { suggestions: list, value: current, caret: at, onAccept: commit } = latest.current;
|
|
404
|
+
const suggestion = list[index];
|
|
405
|
+
if (!suggestion) return;
|
|
406
|
+
const next = (0, import_spreadsheet3.applyFormulaSuggestion)(current, at ?? current.length, suggestion.name);
|
|
407
|
+
pendingCaret.current = next.caret;
|
|
408
|
+
commit(next.value);
|
|
409
|
+
};
|
|
410
|
+
(0, import_react4.useEffect)(() => {
|
|
411
|
+
const input = inputRef.current;
|
|
412
|
+
if (!input) return;
|
|
413
|
+
const syncCaret = () => setCaret(input.selectionStart ?? input.value.length);
|
|
414
|
+
const onInput = () => {
|
|
415
|
+
setDismissed(false);
|
|
416
|
+
setSelected(0);
|
|
417
|
+
syncCaret();
|
|
418
|
+
};
|
|
419
|
+
const onFocus = () => {
|
|
420
|
+
setFocused(true);
|
|
421
|
+
syncCaret();
|
|
422
|
+
};
|
|
423
|
+
const onBlur = () => setFocused(false);
|
|
424
|
+
const onKeyDown = (e) => {
|
|
425
|
+
const { suggestions: list } = latest.current;
|
|
426
|
+
if (list.length === 0) return;
|
|
427
|
+
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
428
|
+
e.preventDefault();
|
|
429
|
+
e.stopPropagation();
|
|
430
|
+
const step = e.key === "ArrowDown" ? 1 : -1;
|
|
431
|
+
setSelected((i) => (i + step + list.length) % list.length);
|
|
432
|
+
} else if (e.key === "Tab" && !e.shiftKey) {
|
|
433
|
+
e.preventDefault();
|
|
434
|
+
e.stopPropagation();
|
|
435
|
+
accept(latest.current.selected);
|
|
436
|
+
} else if (e.key === "Escape") {
|
|
437
|
+
e.preventDefault();
|
|
438
|
+
e.stopPropagation();
|
|
439
|
+
setDismissed(true);
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
input.addEventListener("input", onInput);
|
|
443
|
+
input.addEventListener("keyup", syncCaret);
|
|
444
|
+
input.addEventListener("click", syncCaret);
|
|
445
|
+
input.addEventListener("select", syncCaret);
|
|
446
|
+
input.addEventListener("focus", onFocus);
|
|
447
|
+
input.addEventListener("blur", onBlur);
|
|
448
|
+
input.addEventListener("keydown", onKeyDown);
|
|
449
|
+
if (typeof document !== "undefined" && document.activeElement === input) onFocus();
|
|
450
|
+
return () => {
|
|
451
|
+
input.removeEventListener("input", onInput);
|
|
452
|
+
input.removeEventListener("keyup", syncCaret);
|
|
453
|
+
input.removeEventListener("click", syncCaret);
|
|
454
|
+
input.removeEventListener("select", syncCaret);
|
|
455
|
+
input.removeEventListener("focus", onFocus);
|
|
456
|
+
input.removeEventListener("blur", onBlur);
|
|
457
|
+
input.removeEventListener("keydown", onKeyDown);
|
|
458
|
+
};
|
|
459
|
+
}, [inputRef]);
|
|
460
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
461
|
+
const input = inputRef.current;
|
|
462
|
+
const at = pendingCaret.current;
|
|
463
|
+
if (!input || at === null || input.value !== value) return;
|
|
464
|
+
pendingCaret.current = null;
|
|
465
|
+
input.setSelectionRange(at, at);
|
|
466
|
+
setCaret(at);
|
|
467
|
+
setSelected(0);
|
|
468
|
+
}, [value, inputRef]);
|
|
469
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
470
|
+
const input = inputRef.current;
|
|
471
|
+
if (!visible || !input) return;
|
|
472
|
+
const rect = input.getBoundingClientRect();
|
|
473
|
+
setPosition({ top: rect.bottom + 2, left: rect.left, width: rect.width });
|
|
474
|
+
}, [visible, value, inputRef]);
|
|
475
|
+
if (!visible) return null;
|
|
476
|
+
const { activeCall } = hints;
|
|
477
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
478
|
+
"div",
|
|
479
|
+
{
|
|
480
|
+
"data-formula-hints": "",
|
|
481
|
+
className: "rounded-md border border-border bg-popover text-popover-foreground shadow-md text-xs font-sans overflow-hidden",
|
|
482
|
+
style: {
|
|
483
|
+
position: "fixed",
|
|
484
|
+
top: position?.top ?? 0,
|
|
485
|
+
left: position?.left ?? 0,
|
|
486
|
+
minWidth: Math.max(260, position?.width ?? 0),
|
|
487
|
+
maxWidth: 440,
|
|
488
|
+
zIndex: 50,
|
|
489
|
+
visibility: position ? "visible" : "hidden"
|
|
490
|
+
},
|
|
491
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
492
|
+
children: [
|
|
493
|
+
activeCall && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { "data-formula-signature": "", className: "px-2 py-1.5 border-b border-border bg-muted/30", children: [
|
|
494
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "font-mono", children: activeCall.parts.map((part, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
495
|
+
"span",
|
|
496
|
+
{
|
|
497
|
+
"data-active": part.active ? "" : void 0,
|
|
498
|
+
className: part.active ? "font-bold text-primary underline" : "",
|
|
499
|
+
children: part.text
|
|
500
|
+
},
|
|
501
|
+
i
|
|
502
|
+
)) }),
|
|
503
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mt-0.5 text-muted-foreground", children: activeCall.argument ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
504
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "font-semibold", children: activeCall.argument.name }),
|
|
505
|
+
": ",
|
|
506
|
+
activeCall.argument.description
|
|
507
|
+
] }) : activeCall.description })
|
|
508
|
+
] }),
|
|
509
|
+
suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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__ */ (0, import_jsx_runtime3.jsxs)(
|
|
510
|
+
"li",
|
|
511
|
+
{
|
|
512
|
+
role: "option",
|
|
513
|
+
"aria-selected": i === selectedIndex,
|
|
514
|
+
className: `cursor-pointer px-2 py-1 ${i === selectedIndex ? "bg-primary/10" : "hover:bg-muted/50"}`,
|
|
515
|
+
onMouseEnter: () => setSelected(i),
|
|
516
|
+
onMouseDown: (e) => {
|
|
517
|
+
e.preventDefault();
|
|
518
|
+
accept(i);
|
|
519
|
+
},
|
|
520
|
+
children: [
|
|
521
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "font-mono font-semibold text-foreground", children: s.signature }),
|
|
522
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-muted-foreground truncate", children: s.description })
|
|
523
|
+
]
|
|
524
|
+
},
|
|
525
|
+
s.name
|
|
526
|
+
)) })
|
|
527
|
+
]
|
|
528
|
+
}
|
|
529
|
+
);
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
// src/spreadsheet/SpreadsheetEditor.tsx
|
|
533
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
534
|
+
var COLUMN_HEADER_HEIGHT = 28;
|
|
535
|
+
var ROW_HEADER_WIDTH = 48;
|
|
536
|
+
function frozenCellStyle(viewRow, viewCol) {
|
|
537
|
+
if (!viewRow.frozen && !viewCol.frozen) return void 0;
|
|
538
|
+
return {
|
|
539
|
+
position: "sticky",
|
|
540
|
+
...viewRow.frozen ? { top: COLUMN_HEADER_HEIGHT + viewRow.stickyTop } : {},
|
|
541
|
+
...viewCol.frozen ? { left: ROW_HEADER_WIDTH + viewCol.stickyLeft } : {},
|
|
542
|
+
zIndex: viewRow.frozen && viewCol.frozen ? 14 : viewRow.frozen ? 12 : 11,
|
|
543
|
+
background: "var(--background, #fff)"
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
var SpreadsheetEditor = ({
|
|
547
|
+
state: externalState,
|
|
548
|
+
readOnly = false,
|
|
549
|
+
customCellRenderer,
|
|
550
|
+
onAddColumnClick,
|
|
551
|
+
onColumnHeaderClick,
|
|
552
|
+
onColumnResize
|
|
553
|
+
}) => {
|
|
554
|
+
const defaultState = useSpreadsheetEditor();
|
|
555
|
+
const state = externalState || defaultState;
|
|
556
|
+
const gridRef = (0, import_react5.useRef)(null);
|
|
557
|
+
const formulaBarRef = (0, import_react5.useRef)(null);
|
|
558
|
+
const cellInputRef = (0, import_react5.useRef)(null);
|
|
559
|
+
const [resizingColId, setResizingColId] = (0, import_react5.useState)(null);
|
|
560
|
+
const [hoveredColId, setHoveredColId] = (0, import_react5.useState)(null);
|
|
561
|
+
const [resizeStartX, setResizeStartX] = (0, import_react5.useState)(0);
|
|
562
|
+
const [resizeStartWidth, setResizeStartWidth] = (0, import_react5.useState)(0);
|
|
563
|
+
const view = state.viewModel;
|
|
564
|
+
const { copyShortcutFallback } = useSpreadsheetClipboard(gridRef, state, readOnly);
|
|
565
|
+
const { fillTarget, startFill, extendFill } = useFillHandle(state);
|
|
566
|
+
const [menu, setMenu] = (0, import_react5.useState)(null);
|
|
567
|
+
const filter = state.document.table?.filter;
|
|
568
|
+
const filteredColIds = (0, import_react5.useMemo)(
|
|
569
|
+
() => new Set(filter?.enabled ? filter.rules.map((r) => r.colId) : []),
|
|
570
|
+
[filter]
|
|
571
|
+
);
|
|
572
|
+
const handleCellContextMenu = (0, import_react5.useCallback)((rIdx, cIdx, e) => {
|
|
573
|
+
if (readOnly) return;
|
|
574
|
+
e.preventDefault();
|
|
575
|
+
if (state.editingCell) state.commitEditing();
|
|
576
|
+
if (!state.selectedRange || !(0, import_spreadsheet4.rangeContains)(state.selectedRange, rIdx, cIdx)) {
|
|
577
|
+
state.selectCell(rIdx, cIdx);
|
|
578
|
+
}
|
|
579
|
+
setMenu({ x: e.clientX, y: e.clientY, items: (0, import_spreadsheet4.getSpreadsheetMenuItems)(state.controller, { row: rIdx, col: cIdx }) });
|
|
580
|
+
}, [readOnly, state]);
|
|
581
|
+
const activeRawValue = (0, import_react5.useMemo)(() => {
|
|
582
|
+
if (!state.activeCell) return "";
|
|
583
|
+
const row = state.document.rows[state.activeCell.row];
|
|
584
|
+
const col = state.document.columns[state.activeCell.col];
|
|
585
|
+
if (!row || !col) return "";
|
|
586
|
+
return state.getCell(row.id, col.id).raw;
|
|
587
|
+
}, [state.activeCell, state.document]);
|
|
588
|
+
const coordLabel = (0, import_react5.useMemo)(() => {
|
|
589
|
+
if (!state.activeCell) return "--";
|
|
590
|
+
const colName = (0, import_spreadsheet4.indexToColName)(state.activeCell.col);
|
|
591
|
+
return `${colName}${state.activeCell.row + 1}`;
|
|
592
|
+
}, [state.activeCell]);
|
|
593
|
+
const [formulaDraft, setFormulaDraft] = (0, import_react5.useState)(null);
|
|
594
|
+
const activeColumn = state.activeCell ? state.document.columns[state.activeCell.col] : void 0;
|
|
595
|
+
const formulaBarReadOnly = readOnly || !state.activeCell || !!activeColumn?.readOnly;
|
|
596
|
+
(0, import_react5.useEffect)(() => {
|
|
597
|
+
setFormulaDraft(null);
|
|
598
|
+
}, [state.activeCell?.row, state.activeCell?.col]);
|
|
599
|
+
const commitFormulaBar = () => {
|
|
600
|
+
if (state.editingCell) {
|
|
601
|
+
state.commitEditing();
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
if (formulaDraft === null || !state.activeCell) return;
|
|
605
|
+
const row = state.document.rows[state.activeCell.row];
|
|
606
|
+
const col = state.document.columns[state.activeCell.col];
|
|
607
|
+
if (row && col && !formulaBarReadOnly && formulaDraft !== activeRawValue) {
|
|
608
|
+
state.setCellValue(row.id, col.id, formulaDraft);
|
|
609
|
+
}
|
|
610
|
+
setFormulaDraft(null);
|
|
611
|
+
};
|
|
612
|
+
const handleCellClick = (0, import_react5.useCallback)((rIdx, cIdx, e) => {
|
|
613
|
+
if (state.editingCell && (state.editingCell.row !== rIdx || state.editingCell.col !== cIdx)) {
|
|
614
|
+
state.commitEditing();
|
|
615
|
+
}
|
|
616
|
+
state.selectCell(rIdx, cIdx, e.shiftKey);
|
|
617
|
+
}, [state]);
|
|
618
|
+
const handleCellDblClick = (0, import_react5.useCallback)((rIdx, cIdx) => {
|
|
619
|
+
if (readOnly) return;
|
|
620
|
+
const col = state.document.columns[cIdx];
|
|
621
|
+
if (col?.readOnly) return;
|
|
622
|
+
state.startEditing(rIdx, cIdx);
|
|
623
|
+
}, [readOnly, state]);
|
|
624
|
+
const handleKeyDown = (0, import_react5.useCallback)((e) => {
|
|
625
|
+
if (state.editingCell) {
|
|
626
|
+
if (e.key === "Enter") {
|
|
627
|
+
e.preventDefault();
|
|
628
|
+
state.commitEditing();
|
|
629
|
+
state.moveCursor("down");
|
|
630
|
+
} else if (e.key === "Escape") {
|
|
631
|
+
e.preventDefault();
|
|
632
|
+
state.cancelEditing();
|
|
633
|
+
} else if (e.key === "Tab") {
|
|
634
|
+
e.preventDefault();
|
|
635
|
+
state.commitEditing();
|
|
636
|
+
state.moveCursor(e.shiftKey ? "left" : "right");
|
|
637
|
+
}
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
if (!state.activeCell) return;
|
|
641
|
+
if (e.key === "ArrowUp") {
|
|
642
|
+
e.preventDefault();
|
|
643
|
+
state.moveCursor("up", e.shiftKey);
|
|
644
|
+
} else if (e.key === "ArrowDown") {
|
|
645
|
+
e.preventDefault();
|
|
646
|
+
state.moveCursor("down", e.shiftKey);
|
|
647
|
+
} else if (e.key === "ArrowLeft") {
|
|
648
|
+
e.preventDefault();
|
|
649
|
+
state.moveCursor("left", e.shiftKey);
|
|
650
|
+
} else if (e.key === "ArrowRight") {
|
|
651
|
+
e.preventDefault();
|
|
652
|
+
state.moveCursor("right", e.shiftKey);
|
|
653
|
+
} else if (e.key === "Tab") {
|
|
654
|
+
e.preventDefault();
|
|
655
|
+
state.moveCursor(e.shiftKey ? "left" : "right");
|
|
656
|
+
} else if (e.key === "Enter") {
|
|
657
|
+
e.preventDefault();
|
|
658
|
+
const col = state.document.columns[state.activeCell.col];
|
|
659
|
+
if (!readOnly && !col?.readOnly) {
|
|
660
|
+
state.startEditing();
|
|
661
|
+
}
|
|
662
|
+
} else if (e.key === "Backspace" || e.key === "Delete") {
|
|
663
|
+
if (readOnly) return;
|
|
664
|
+
const row = state.document.rows[state.activeCell.row];
|
|
665
|
+
const col = state.document.columns[state.activeCell.col];
|
|
666
|
+
if (row && col && !col.readOnly) {
|
|
667
|
+
e.preventDefault();
|
|
668
|
+
state.setCellValue(row.id, col.id, "");
|
|
669
|
+
}
|
|
670
|
+
} else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "z") {
|
|
671
|
+
e.preventDefault();
|
|
672
|
+
if (e.shiftKey) {
|
|
673
|
+
state.redo();
|
|
674
|
+
} else {
|
|
675
|
+
state.undo();
|
|
676
|
+
}
|
|
677
|
+
} else if (e.ctrlKey && !e.metaKey && e.key.toLowerCase() === "y") {
|
|
678
|
+
e.preventDefault();
|
|
679
|
+
state.redo();
|
|
680
|
+
} else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "c") {
|
|
681
|
+
copyShortcutFallback();
|
|
682
|
+
} else if (!readOnly && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
683
|
+
const col = state.document.columns[state.activeCell.col];
|
|
684
|
+
if (!col?.readOnly) {
|
|
685
|
+
state.startEditing(state.activeCell.row, state.activeCell.col, e.key);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}, [readOnly, state, copyShortcutFallback]);
|
|
689
|
+
const stateRef = (0, import_react5.useRef)(state);
|
|
690
|
+
stateRef.current = state;
|
|
691
|
+
const onColumnResizeRef = (0, import_react5.useRef)(onColumnResize);
|
|
692
|
+
onColumnResizeRef.current = onColumnResize;
|
|
693
|
+
(0, import_react5.useEffect)(() => {
|
|
694
|
+
if (!resizingColId) return;
|
|
695
|
+
if (typeof document !== "undefined") {
|
|
696
|
+
document.body.style.cursor = "col-resize";
|
|
697
|
+
document.body.style.userSelect = "none";
|
|
698
|
+
}
|
|
699
|
+
let rafId = null;
|
|
700
|
+
let pendingWidth = resizeStartWidth;
|
|
701
|
+
let hasDragged = false;
|
|
702
|
+
const handleMouseMove = (e) => {
|
|
703
|
+
const delta = e.clientX - resizeStartX;
|
|
704
|
+
if (Math.abs(delta) > 2) {
|
|
705
|
+
hasDragged = true;
|
|
706
|
+
}
|
|
707
|
+
pendingWidth = Math.max(30, resizeStartWidth + delta);
|
|
708
|
+
if (rafId === null) {
|
|
709
|
+
rafId = requestAnimationFrame(() => {
|
|
710
|
+
rafId = null;
|
|
711
|
+
stateRef.current.setColumnWidth(resizingColId, pendingWidth);
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
const handleMouseUp = () => {
|
|
716
|
+
if (rafId !== null) {
|
|
717
|
+
cancelAnimationFrame(rafId);
|
|
718
|
+
rafId = null;
|
|
719
|
+
}
|
|
720
|
+
if (hasDragged) {
|
|
721
|
+
stateRef.current.setColumnWidth(resizingColId, pendingWidth);
|
|
722
|
+
onColumnResizeRef.current?.(resizingColId, pendingWidth);
|
|
723
|
+
}
|
|
724
|
+
setResizingColId(null);
|
|
725
|
+
if (typeof document !== "undefined") {
|
|
726
|
+
document.body.style.cursor = "";
|
|
727
|
+
document.body.style.userSelect = "";
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
731
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
732
|
+
return () => {
|
|
733
|
+
if (rafId !== null) {
|
|
734
|
+
cancelAnimationFrame(rafId);
|
|
735
|
+
}
|
|
736
|
+
if (typeof document !== "undefined") {
|
|
737
|
+
document.body.style.cursor = "";
|
|
738
|
+
document.body.style.userSelect = "";
|
|
739
|
+
}
|
|
740
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
741
|
+
window.removeEventListener("mouseup", handleMouseUp);
|
|
742
|
+
};
|
|
743
|
+
}, [resizingColId, resizeStartX, resizeStartWidth]);
|
|
744
|
+
const measureElementContentWidth = (0, import_react5.useCallback)((el, container) => {
|
|
745
|
+
if (typeof document === "undefined") return 0;
|
|
746
|
+
let maxW = el.scrollWidth || 0;
|
|
747
|
+
const firstChild = el.firstElementChild;
|
|
748
|
+
if (firstChild && firstChild.scrollWidth > maxW) {
|
|
749
|
+
maxW = firstChild.scrollWidth;
|
|
750
|
+
}
|
|
751
|
+
const truncated = el.querySelectorAll(".truncate, [title], span, div");
|
|
752
|
+
truncated.forEach((node) => {
|
|
753
|
+
if (node.scrollWidth > maxW) {
|
|
754
|
+
maxW = node.scrollWidth;
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
try {
|
|
758
|
+
const clone = firstChild ? firstChild.cloneNode(true) : el.cloneNode(true);
|
|
759
|
+
clone.style.cssText = `
|
|
760
|
+
position: absolute !important;
|
|
761
|
+
visibility: hidden !important;
|
|
762
|
+
width: max-content !important;
|
|
763
|
+
min-width: max-content !important;
|
|
764
|
+
max-width: none !important;
|
|
765
|
+
overflow: visible !important;
|
|
766
|
+
white-space: nowrap !important;
|
|
767
|
+
pointer-events: none !important;
|
|
768
|
+
top: -9999px !important;
|
|
769
|
+
left: -9999px !important;
|
|
770
|
+
`;
|
|
771
|
+
const allNodes = clone.querySelectorAll("*");
|
|
772
|
+
allNodes.forEach((node) => {
|
|
773
|
+
node.style.maxWidth = "none";
|
|
774
|
+
node.style.overflow = "visible";
|
|
775
|
+
node.style.textOverflow = "clip";
|
|
776
|
+
node.style.whiteSpace = "nowrap";
|
|
777
|
+
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")) {
|
|
778
|
+
node.style.width = "max-content";
|
|
779
|
+
node.style.minWidth = "max-content";
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
container.appendChild(clone);
|
|
783
|
+
const rect = clone.getBoundingClientRect();
|
|
784
|
+
container.removeChild(clone);
|
|
785
|
+
if (rect.width > maxW) {
|
|
786
|
+
maxW = rect.width;
|
|
787
|
+
}
|
|
788
|
+
} catch {
|
|
789
|
+
}
|
|
790
|
+
return maxW;
|
|
791
|
+
}, []);
|
|
792
|
+
const autoFitColumn = (0, import_react5.useCallback)((colId, colIndex) => {
|
|
793
|
+
const col = state.document.columns.find((c) => c.id === colId);
|
|
794
|
+
if (!col) return;
|
|
795
|
+
let calculatedWidth = 0;
|
|
796
|
+
if (gridRef.current && typeof document !== "undefined") {
|
|
797
|
+
const table = gridRef.current.querySelector("table");
|
|
798
|
+
if (table) {
|
|
799
|
+
const th = table.querySelector(`thead th[data-col="${colIndex}"]`);
|
|
800
|
+
if (th) {
|
|
801
|
+
const headerInner = th.firstElementChild || th;
|
|
802
|
+
const headerW = measureElementContentWidth(headerInner, gridRef.current);
|
|
803
|
+
calculatedWidth = Math.max(calculatedWidth, headerW + 28);
|
|
804
|
+
}
|
|
805
|
+
const rows = table.querySelectorAll("tbody tr");
|
|
806
|
+
const sampleLimit = Math.min(rows.length, 150);
|
|
807
|
+
for (let r = 0; r < sampleLimit; r++) {
|
|
808
|
+
const tr = rows[r];
|
|
809
|
+
const td = tr.querySelector(`td[data-col="${colIndex}"][colspan="1"]`);
|
|
810
|
+
if (td) {
|
|
811
|
+
const cellW = measureElementContentWidth(td, gridRef.current);
|
|
812
|
+
calculatedWidth = Math.max(calculatedWidth, cellW + 20);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
if (calculatedWidth > 0) {
|
|
818
|
+
const finalWidth = Math.min(600, Math.max(50, Math.ceil(calculatedWidth)));
|
|
819
|
+
state.setColumnWidth(colId, finalWidth);
|
|
820
|
+
} else {
|
|
821
|
+
state.autoFitColumnWidth?.(colId);
|
|
822
|
+
}
|
|
823
|
+
const updatedCol = state.document.columns.find((c) => c.id === colId);
|
|
824
|
+
if (updatedCol && onColumnResize) {
|
|
825
|
+
onColumnResize(updatedCol.id, updatedCol.width || 130);
|
|
826
|
+
}
|
|
827
|
+
}, [state, measureElementContentWidth, onColumnResize]);
|
|
828
|
+
const handleResizeDblClick = (0, import_react5.useCallback)((colId, colIndex, e) => {
|
|
829
|
+
e.preventDefault();
|
|
830
|
+
e.stopPropagation();
|
|
831
|
+
autoFitColumn(colId, colIndex);
|
|
832
|
+
}, [autoFitColumn]);
|
|
833
|
+
const handleHeaderDblClick = (0, import_react5.useCallback)((colId, colIndex, e) => {
|
|
834
|
+
const target = e.currentTarget;
|
|
835
|
+
if (target) {
|
|
836
|
+
const rect = target.getBoundingClientRect();
|
|
837
|
+
if (rect.right - e.clientX <= 12) {
|
|
838
|
+
e.preventDefault();
|
|
839
|
+
e.stopPropagation();
|
|
840
|
+
autoFitColumn(colId, colIndex);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}, [autoFitColumn]);
|
|
844
|
+
const isCellSelected = (rIdx, cIdx) => {
|
|
845
|
+
if (!state.selectedRange) return false;
|
|
846
|
+
const minR = Math.min(state.selectedRange.startRow, state.selectedRange.endRow);
|
|
847
|
+
const maxR = Math.max(state.selectedRange.startRow, state.selectedRange.endRow);
|
|
848
|
+
const minC = Math.min(state.selectedRange.startCol, state.selectedRange.endCol);
|
|
849
|
+
const maxC = Math.max(state.selectedRange.startCol, state.selectedRange.endCol);
|
|
850
|
+
return rIdx >= minR && rIdx <= maxR && cIdx >= minC && cIdx <= maxC;
|
|
851
|
+
};
|
|
852
|
+
const isActiveCell = (rIdx, cIdx) => {
|
|
853
|
+
return state.activeCell?.row === rIdx && state.activeCell?.col === cIdx;
|
|
854
|
+
};
|
|
855
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
856
|
+
"div",
|
|
857
|
+
{
|
|
858
|
+
ref: gridRef,
|
|
859
|
+
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",
|
|
860
|
+
tabIndex: 0,
|
|
861
|
+
onKeyDown: handleKeyDown,
|
|
862
|
+
role: "grid",
|
|
863
|
+
"aria-label": "Spreadsheet",
|
|
864
|
+
children: [
|
|
865
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2 border-b border-border bg-muted/20 px-3 py-1.5 text-xs font-mono", children: [
|
|
866
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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 }),
|
|
867
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex items-center text-muted-foreground select-none", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "italic font-serif font-bold text-sm text-primary", children: "fx" }) }),
|
|
868
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
869
|
+
"input",
|
|
870
|
+
{
|
|
871
|
+
ref: formulaBarRef,
|
|
872
|
+
type: "text",
|
|
873
|
+
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",
|
|
874
|
+
placeholder: "Enter a value or formula (e.g. =SUM(A1:A5))",
|
|
875
|
+
"aria-label": "Formula bar",
|
|
876
|
+
readOnly: formulaBarReadOnly,
|
|
877
|
+
value: state.editingCell ? state.draftValue : formulaDraft ?? activeRawValue,
|
|
878
|
+
onFocus: () => {
|
|
879
|
+
if (!state.editingCell) setFormulaDraft(activeRawValue);
|
|
880
|
+
},
|
|
881
|
+
onChange: (e) => {
|
|
882
|
+
if (state.editingCell) {
|
|
883
|
+
state.setDraftValue(e.target.value);
|
|
884
|
+
} else if (state.activeCell) {
|
|
885
|
+
setFormulaDraft(e.target.value);
|
|
886
|
+
}
|
|
887
|
+
},
|
|
888
|
+
onBlur: () => commitFormulaBar(),
|
|
889
|
+
onKeyDown: (e) => {
|
|
890
|
+
e.stopPropagation();
|
|
891
|
+
if (e.key === "Enter") {
|
|
892
|
+
e.preventDefault();
|
|
893
|
+
commitFormulaBar();
|
|
894
|
+
} else if (e.key === "Escape") {
|
|
895
|
+
e.preventDefault();
|
|
896
|
+
setFormulaDraft(null);
|
|
897
|
+
if (state.editingCell) state.cancelEditing();
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
),
|
|
902
|
+
!formulaBarReadOnly && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
903
|
+
FormulaHints,
|
|
904
|
+
{
|
|
905
|
+
inputRef: formulaBarRef,
|
|
906
|
+
value: state.editingCell ? state.draftValue : formulaDraft ?? activeRawValue,
|
|
907
|
+
onAccept: (next) => state.editingCell ? state.setDraftValue(next) : setFormulaDraft(next)
|
|
908
|
+
}
|
|
909
|
+
)
|
|
910
|
+
] }),
|
|
911
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "relative flex-1 overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("table", { className: "border-collapse table-fixed w-max min-w-full", children: [
|
|
912
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("tr", { className: "h-7 bg-muted/60 sticky top-0 z-20 border-b border-border shadow-xs", children: [
|
|
913
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("th", { className: "w-12 min-w-[3rem] sticky left-0 z-30 bg-muted/90 border-r border-border text-center text-[10px] text-muted-foreground font-medium", children: "#" }),
|
|
914
|
+
view.columns.map(({ column: col, index: cIdx, frozen, stickyLeft }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
915
|
+
"th",
|
|
916
|
+
{
|
|
917
|
+
"data-col": cIdx,
|
|
918
|
+
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"}`,
|
|
919
|
+
style: {
|
|
920
|
+
width: col.width || 130,
|
|
921
|
+
minWidth: col.width || 130,
|
|
922
|
+
maxWidth: col.width || 130,
|
|
923
|
+
overflow: "hidden",
|
|
924
|
+
...frozen ? { position: "sticky", left: ROW_HEADER_WIDTH + stickyLeft, zIndex: 25, background: "var(--muted, #f4f4f5)" } : {}
|
|
925
|
+
},
|
|
926
|
+
onClick: () => onColumnHeaderClick?.(col),
|
|
927
|
+
onDoubleClick: (e) => handleHeaderDblClick(col.id, cIdx, e),
|
|
928
|
+
children: [
|
|
929
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center justify-between gap-1 min-w-0 overflow-hidden truncate", children: [
|
|
930
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "truncate font-semibold text-foreground text-xs min-w-0", children: col.title }),
|
|
931
|
+
filteredColIds.has(col.id) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[10px] text-primary shrink-0", title: "Filtered", "data-filtered": "true", children: "\u23F7" }),
|
|
932
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "text-[10px] text-muted-foreground/70 font-mono font-normal shrink-0", children: [
|
|
933
|
+
"(",
|
|
934
|
+
col.key,
|
|
935
|
+
")"
|
|
936
|
+
] })
|
|
937
|
+
] }),
|
|
938
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
939
|
+
"div",
|
|
940
|
+
{
|
|
941
|
+
className: "col-resize-handle",
|
|
942
|
+
style: { position: "absolute", top: 0, bottom: 0, width: 12, right: -6, cursor: "col-resize", zIndex: 20, display: "flex", justifyContent: "center", touchAction: "none" },
|
|
943
|
+
onMouseEnter: () => setHoveredColId(col.id),
|
|
944
|
+
onMouseLeave: () => setHoveredColId((cur) => cur === col.id ? null : cur),
|
|
945
|
+
onMouseDown: (e) => {
|
|
946
|
+
e.preventDefault();
|
|
947
|
+
e.stopPropagation();
|
|
948
|
+
setResizingColId(col.id);
|
|
949
|
+
setResizeStartX(e.clientX);
|
|
950
|
+
setResizeStartWidth(col.width || 130);
|
|
951
|
+
},
|
|
952
|
+
onDoubleClick: (e) => handleResizeDblClick(col.id, cIdx, e),
|
|
953
|
+
role: "separator",
|
|
954
|
+
"aria-orientation": "vertical",
|
|
955
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
956
|
+
"div",
|
|
957
|
+
{
|
|
958
|
+
className: "col-resize-handle-line",
|
|
959
|
+
style: {
|
|
960
|
+
width: 2,
|
|
961
|
+
height: "100%",
|
|
962
|
+
pointerEvents: "none",
|
|
963
|
+
transition: "background-color 150ms ease",
|
|
964
|
+
backgroundColor: resizingColId === col.id ? "var(--primary, #6366f1)" : hoveredColId === col.id ? "color-mix(in srgb, var(--primary, #6366f1) 75%, transparent)" : "transparent"
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
)
|
|
968
|
+
}
|
|
969
|
+
)
|
|
970
|
+
]
|
|
971
|
+
},
|
|
972
|
+
col.id
|
|
973
|
+
)),
|
|
974
|
+
onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
975
|
+
"th",
|
|
976
|
+
{
|
|
977
|
+
className: "w-10 min-w-[2.5rem] px-1 text-center bg-muted/40 hover:bg-muted cursor-pointer transition-colors",
|
|
978
|
+
onClick: onAddColumnClick,
|
|
979
|
+
title: "Add Column",
|
|
980
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-xs font-bold text-muted-foreground", children: "+" })
|
|
981
|
+
}
|
|
982
|
+
)
|
|
983
|
+
] }) }),
|
|
984
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { children: view.rows.map((viewRow) => {
|
|
985
|
+
if (viewRow.kind === "group") {
|
|
986
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
987
|
+
SpreadsheetGroupRow,
|
|
988
|
+
{
|
|
989
|
+
group: viewRow.group,
|
|
990
|
+
columns: view.columns,
|
|
991
|
+
groupColId: state.document.table?.grouping?.colId ?? "",
|
|
992
|
+
onToggle: state.toggleGroupCollapsed,
|
|
993
|
+
trailingCell: !!onAddColumnClick
|
|
994
|
+
},
|
|
995
|
+
`group:${viewRow.group.key}`
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
const displayRowIndex = viewRow.index;
|
|
999
|
+
const selection = state.selectedRange ? (0, import_spreadsheet4.normalizeRange)(state.selectedRange) : null;
|
|
1000
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1001
|
+
"tr",
|
|
1002
|
+
{
|
|
1003
|
+
"data-row": displayRowIndex,
|
|
1004
|
+
className: `border-b border-border hover:bg-muted/10 transition-colors ${viewRow.section !== "body" ? "bg-muted/30 font-semibold" : ""}`,
|
|
1005
|
+
style: { height: viewRow.row.height || 32 },
|
|
1006
|
+
children: [
|
|
1007
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1008
|
+
"td",
|
|
1009
|
+
{
|
|
1010
|
+
className: "w-12 min-w-[3rem] sticky left-0 z-10 bg-muted/50 border-r border-border text-center text-[10px] text-muted-foreground font-mono select-none",
|
|
1011
|
+
style: viewRow.frozen ? { top: COLUMN_HEADER_HEIGHT + viewRow.stickyTop, zIndex: 15 } : void 0,
|
|
1012
|
+
children: displayRowIndex + 1
|
|
1013
|
+
}
|
|
1014
|
+
),
|
|
1015
|
+
view.columns.map((viewCol) => {
|
|
1016
|
+
const span = view.getCellSpan(displayRowIndex, viewCol.index);
|
|
1017
|
+
if (!span) return null;
|
|
1018
|
+
const rIdx = span.source.row;
|
|
1019
|
+
const cIdx = span.source.col;
|
|
1020
|
+
const row = state.document.rows[rIdx];
|
|
1021
|
+
const col = state.document.columns[cIdx];
|
|
1022
|
+
const cell = state.getCell(row.id, col.id);
|
|
1023
|
+
const selected = isCellSelected(rIdx, cIdx);
|
|
1024
|
+
const active = isActiveCell(rIdx, cIdx);
|
|
1025
|
+
const isEditing = state.editingCell?.row === rIdx && state.editingCell?.col === cIdx;
|
|
1026
|
+
const inFillPreview = !!fillTarget && !selected && (0, import_spreadsheet4.rangeContains)(fillTarget, displayRowIndex, viewCol.index);
|
|
1027
|
+
const showFillHandle = !readOnly && !isEditing && !!selection && (span.merge ? (0, import_spreadsheet4.rangeContains)(span.merge, selection.endRow, selection.endCol) : selection.endRow === rIdx && selection.endCol === cIdx);
|
|
1028
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1029
|
+
"td",
|
|
1030
|
+
{
|
|
1031
|
+
"data-row": rIdx,
|
|
1032
|
+
"data-col": cIdx,
|
|
1033
|
+
rowSpan: span.rowSpan,
|
|
1034
|
+
colSpan: span.colSpan,
|
|
1035
|
+
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" : ""}`,
|
|
1036
|
+
style: {
|
|
1037
|
+
maxWidth: 0,
|
|
1038
|
+
overflow: "hidden",
|
|
1039
|
+
...frozenCellStyle(viewRow, viewCol),
|
|
1040
|
+
...inFillPreview ? { outline: "1px dashed var(--primary, #6366f1)", outlineOffset: -1 } : {}
|
|
1041
|
+
},
|
|
1042
|
+
onClick: (e) => handleCellClick(rIdx, cIdx, e),
|
|
1043
|
+
onDoubleClick: () => handleCellDblClick(rIdx, cIdx),
|
|
1044
|
+
onContextMenu: (e) => handleCellContextMenu(rIdx, cIdx, e),
|
|
1045
|
+
onMouseOver: () => extendFill(displayRowIndex, viewCol.index),
|
|
1046
|
+
children: [
|
|
1047
|
+
isEditing ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1048
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1049
|
+
"input",
|
|
1050
|
+
{
|
|
1051
|
+
ref: cellInputRef,
|
|
1052
|
+
type: "text",
|
|
1053
|
+
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",
|
|
1054
|
+
value: state.draftValue,
|
|
1055
|
+
onChange: (e) => state.setDraftValue(e.target.value),
|
|
1056
|
+
onBlur: () => state.commitEditing(),
|
|
1057
|
+
autoFocus: true
|
|
1058
|
+
}
|
|
1059
|
+
),
|
|
1060
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FormulaHints, { inputRef: cellInputRef, value: state.draftValue, onAccept: state.setDraftValue })
|
|
1061
|
+
] }) : customCellRenderer ? customCellRenderer({ row, col, cell }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1062
|
+
"span",
|
|
1063
|
+
{
|
|
1064
|
+
className: `truncate block max-w-full ${cell.error ? "text-destructive font-semibold" : ""}`,
|
|
1065
|
+
title: cell.error ? cell.errorMessage ?? cell.error : void 0,
|
|
1066
|
+
children: cell.error || (cell.value ?? "")
|
|
1067
|
+
}
|
|
1068
|
+
),
|
|
1069
|
+
showFillHandle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1070
|
+
"div",
|
|
1071
|
+
{
|
|
1072
|
+
className: "spreadsheet-fill-handle",
|
|
1073
|
+
"aria-label": "Fill handle",
|
|
1074
|
+
onMouseDown: startFill,
|
|
1075
|
+
onClick: (e) => e.stopPropagation(),
|
|
1076
|
+
style: { position: "absolute", right: 0, bottom: 0, width: 7, height: 7, background: "var(--primary, #6366f1)", cursor: "crosshair", zIndex: 25 }
|
|
1077
|
+
}
|
|
1078
|
+
)
|
|
1079
|
+
]
|
|
1080
|
+
},
|
|
1081
|
+
viewCol.column.id
|
|
1082
|
+
);
|
|
1083
|
+
}),
|
|
1084
|
+
onAddColumnClick && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("td", { className: "w-10 min-w-[2.5rem] bg-muted/10 border-r border-border" })
|
|
1085
|
+
]
|
|
1086
|
+
},
|
|
1087
|
+
viewRow.row.id
|
|
1088
|
+
);
|
|
1089
|
+
}) })
|
|
1090
|
+
] }) }),
|
|
1091
|
+
menu && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1092
|
+
SpreadsheetContextMenu,
|
|
1093
|
+
{
|
|
1094
|
+
items: menu.items,
|
|
1095
|
+
x: menu.x,
|
|
1096
|
+
y: menu.y,
|
|
1097
|
+
onClose: () => {
|
|
1098
|
+
setMenu(null);
|
|
1099
|
+
gridRef.current?.focus();
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
),
|
|
1103
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `
|
|
1104
|
+
.col-resize-handle:hover .col-resize-handle-line {
|
|
1105
|
+
background-color: var(--primary, #6366f1);
|
|
1106
|
+
opacity: 0.75;
|
|
1107
|
+
}
|
|
1108
|
+
.col-resize-handle.active .col-resize-handle-line {
|
|
1109
|
+
background-color: var(--primary, #6366f1);
|
|
1110
|
+
opacity: 1;
|
|
1111
|
+
}
|
|
1112
|
+
table.table-fixed th {
|
|
1113
|
+
overflow: hidden;
|
|
1114
|
+
}
|
|
1115
|
+
table.table-fixed td {
|
|
1116
|
+
max-width: 0;
|
|
1117
|
+
overflow: hidden;
|
|
1118
|
+
}
|
|
1119
|
+
` })
|
|
1120
|
+
]
|
|
1121
|
+
}
|
|
1122
|
+
);
|
|
1123
|
+
};
|
|
1124
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1125
|
+
0 && (module.exports = {
|
|
1126
|
+
FormulaHints,
|
|
1127
|
+
SpreadsheetContextMenu,
|
|
1128
|
+
SpreadsheetEditor,
|
|
1129
|
+
SpreadsheetGroupRow,
|
|
1130
|
+
useFillHandle,
|
|
1131
|
+
useSpreadsheetClipboard,
|
|
1132
|
+
useSpreadsheetEditor
|
|
1133
|
+
});
|
|
1134
|
+
//# sourceMappingURL=index.cjs.map
|