@particle-academy/fancy-sheets 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +173 -181
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -6
- package/dist/index.d.ts +12 -6
- package/dist/index.js +123 -135
- package/dist/index.js.map +1 -1
- package/docs/Spreadsheet.md +140 -77
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React = require('react');
|
|
4
4
|
var reactFancy = require('@particle-academy/react-fancy');
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
10
|
+
|
|
7
11
|
// src/components/Spreadsheet/Spreadsheet.tsx
|
|
8
|
-
var SpreadsheetContext =
|
|
12
|
+
var SpreadsheetContext = React.createContext(null);
|
|
9
13
|
function useSpreadsheet() {
|
|
10
|
-
const ctx =
|
|
14
|
+
const ctx = React.useContext(SpreadsheetContext);
|
|
11
15
|
if (!ctx) {
|
|
12
16
|
throw new Error("useSpreadsheet must be used within a <Spreadsheet> component");
|
|
13
17
|
}
|
|
@@ -1545,8 +1549,8 @@ function reducer(state, action) {
|
|
|
1545
1549
|
}
|
|
1546
1550
|
}
|
|
1547
1551
|
function useSpreadsheetStore(initialData) {
|
|
1548
|
-
const [state, dispatch] =
|
|
1549
|
-
const actions =
|
|
1552
|
+
const [state, dispatch] = React.useReducer(reducer, initialData, (data) => createInitialState(data));
|
|
1553
|
+
const actions = React.useMemo(() => ({
|
|
1550
1554
|
setCellValue: (address, value) => dispatch({ type: "SET_CELL_VALUE", address, value }),
|
|
1551
1555
|
setCellFormat: (addresses, format) => dispatch({ type: "SET_CELL_FORMAT", addresses, format }),
|
|
1552
1556
|
setSelection: (cell) => dispatch({ type: "SET_SELECTION", cell }),
|
|
@@ -1573,9 +1577,9 @@ function useSpreadsheetStore(initialData) {
|
|
|
1573
1577
|
}
|
|
1574
1578
|
function ColumnResizeHandle({ colIndex }) {
|
|
1575
1579
|
const { resizeColumn, getColumnWidth } = useSpreadsheet();
|
|
1576
|
-
const startX =
|
|
1577
|
-
const startWidth =
|
|
1578
|
-
const handlePointerDown =
|
|
1580
|
+
const startX = React.useRef(0);
|
|
1581
|
+
const startWidth = React.useRef(0);
|
|
1582
|
+
const handlePointerDown = React.useCallback(
|
|
1579
1583
|
(e) => {
|
|
1580
1584
|
e.preventDefault();
|
|
1581
1585
|
e.stopPropagation();
|
|
@@ -1608,7 +1612,7 @@ function ColumnResizeHandle({ colIndex }) {
|
|
|
1608
1612
|
ColumnResizeHandle.displayName = "ColumnResizeHandle";
|
|
1609
1613
|
function ColumnHeaders() {
|
|
1610
1614
|
const { columnCount, rowCount, rowHeight, getColumnWidth, selection, selectRange, _isDragging } = useSpreadsheet();
|
|
1611
|
-
const handleColumnMouseDown =
|
|
1615
|
+
const handleColumnMouseDown = React.useCallback(
|
|
1612
1616
|
(colIdx, e) => {
|
|
1613
1617
|
if (e.button !== 0) return;
|
|
1614
1618
|
if (e.shiftKey) {
|
|
@@ -1623,7 +1627,7 @@ function ColumnHeaders() {
|
|
|
1623
1627
|
},
|
|
1624
1628
|
[rowCount, selectRange, selection.activeCell, _isDragging]
|
|
1625
1629
|
);
|
|
1626
|
-
const handleColumnMouseEnter =
|
|
1630
|
+
const handleColumnMouseEnter = React.useCallback(
|
|
1627
1631
|
(colIdx) => {
|
|
1628
1632
|
if (_isDragging.current) {
|
|
1629
1633
|
const activeCol = parseAddress(selection.activeCell).col;
|
|
@@ -1634,7 +1638,7 @@ function ColumnHeaders() {
|
|
|
1634
1638
|
},
|
|
1635
1639
|
[rowCount, selection.activeCell, selectRange, _isDragging]
|
|
1636
1640
|
);
|
|
1637
|
-
const handleMouseUp =
|
|
1641
|
+
const handleMouseUp = React.useCallback(() => {
|
|
1638
1642
|
_isDragging.current = false;
|
|
1639
1643
|
}, [_isDragging]);
|
|
1640
1644
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -1696,7 +1700,7 @@ function RowHeader({ rowIndex }) {
|
|
|
1696
1700
|
const maxCol = Math.max(s.col, e.col);
|
|
1697
1701
|
return rowIndex >= minRow && rowIndex <= maxRow && minCol === 0 && maxCol >= columnCount - 1;
|
|
1698
1702
|
});
|
|
1699
|
-
const handleMouseDown =
|
|
1703
|
+
const handleMouseDown = React.useCallback(
|
|
1700
1704
|
(e) => {
|
|
1701
1705
|
if (e.button !== 0) return;
|
|
1702
1706
|
if (e.shiftKey) {
|
|
@@ -1711,7 +1715,7 @@ function RowHeader({ rowIndex }) {
|
|
|
1711
1715
|
},
|
|
1712
1716
|
[rowIndex, columnCount, selectRange, selection.activeCell, _isDragging]
|
|
1713
1717
|
);
|
|
1714
|
-
const handleMouseEnter =
|
|
1718
|
+
const handleMouseEnter = React.useCallback(() => {
|
|
1715
1719
|
if (_isDragging.current) {
|
|
1716
1720
|
const activeRow = parseAddress(selection.activeCell).row;
|
|
1717
1721
|
const minRow = Math.min(activeRow, rowIndex);
|
|
@@ -1719,7 +1723,7 @@ function RowHeader({ rowIndex }) {
|
|
|
1719
1723
|
selectRange(toAddress(minRow, 0), toAddress(maxRow, columnCount - 1));
|
|
1720
1724
|
}
|
|
1721
1725
|
}, [rowIndex, columnCount, selection.activeCell, selectRange, _isDragging]);
|
|
1722
|
-
const handleMouseUp =
|
|
1726
|
+
const handleMouseUp = React.useCallback(() => {
|
|
1723
1727
|
_isDragging.current = false;
|
|
1724
1728
|
}, [_isDragging]);
|
|
1725
1729
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1787,7 +1791,7 @@ function getCellDisplayValue2(cell) {
|
|
|
1787
1791
|
const val = cell.formula && cell.computedValue !== void 0 ? cell.computedValue : cell.value;
|
|
1788
1792
|
return formatCellValue(val, cell);
|
|
1789
1793
|
}
|
|
1790
|
-
var Cell =
|
|
1794
|
+
var Cell = React.memo(function Cell2({ address, row, col }) {
|
|
1791
1795
|
const {
|
|
1792
1796
|
activeSheet,
|
|
1793
1797
|
selection,
|
|
@@ -1809,7 +1813,7 @@ var Cell = react.memo(function Cell2({ address, row, col }) {
|
|
|
1809
1813
|
const isEditing = editingCell === address;
|
|
1810
1814
|
const displayValue = getCellDisplayValue2(cell);
|
|
1811
1815
|
const width = getColumnWidth(col);
|
|
1812
|
-
const handleMouseDown =
|
|
1816
|
+
const handleMouseDown = React.useCallback(
|
|
1813
1817
|
(e) => {
|
|
1814
1818
|
if (e.button !== 0) return;
|
|
1815
1819
|
if (e.shiftKey) {
|
|
@@ -1823,15 +1827,15 @@ var Cell = react.memo(function Cell2({ address, row, col }) {
|
|
|
1823
1827
|
},
|
|
1824
1828
|
[address, setSelection, extendSelection, addSelection, _isDragging]
|
|
1825
1829
|
);
|
|
1826
|
-
const handleMouseEnter =
|
|
1830
|
+
const handleMouseEnter = React.useCallback(() => {
|
|
1827
1831
|
if (_isDragging.current) {
|
|
1828
1832
|
extendSelection(address);
|
|
1829
1833
|
}
|
|
1830
1834
|
}, [address, extendSelection, _isDragging]);
|
|
1831
|
-
const handleMouseUp =
|
|
1835
|
+
const handleMouseUp = React.useCallback(() => {
|
|
1832
1836
|
_isDragging.current = false;
|
|
1833
1837
|
}, [_isDragging]);
|
|
1834
|
-
const handleDoubleClick =
|
|
1838
|
+
const handleDoubleClick = React.useCallback(() => {
|
|
1835
1839
|
if (readOnly) return;
|
|
1836
1840
|
startEdit();
|
|
1837
1841
|
}, [readOnly, startEdit]);
|
|
@@ -1864,7 +1868,7 @@ var Cell = react.memo(function Cell2({ address, row, col }) {
|
|
|
1864
1868
|
formatStyle.borderWidth = 1;
|
|
1865
1869
|
formatStyle.borderStyle = "solid";
|
|
1866
1870
|
}
|
|
1867
|
-
const [showComment, setShowComment] =
|
|
1871
|
+
const [showComment, setShowComment] = React.useState(false);
|
|
1868
1872
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1869
1873
|
"div",
|
|
1870
1874
|
{
|
|
@@ -1927,15 +1931,15 @@ function CellEditor() {
|
|
|
1927
1931
|
getColumnWidth,
|
|
1928
1932
|
rowHeight
|
|
1929
1933
|
} = useSpreadsheet();
|
|
1930
|
-
const inputRef =
|
|
1931
|
-
const mountedAt =
|
|
1932
|
-
|
|
1934
|
+
const inputRef = React.useRef(null);
|
|
1935
|
+
const mountedAt = React.useRef(0);
|
|
1936
|
+
React.useEffect(() => {
|
|
1933
1937
|
if (editingCell && inputRef.current) {
|
|
1934
1938
|
mountedAt.current = Date.now();
|
|
1935
1939
|
inputRef.current.focus();
|
|
1936
1940
|
}
|
|
1937
1941
|
}, [editingCell]);
|
|
1938
|
-
const handleBlur =
|
|
1942
|
+
const handleBlur = React.useCallback(() => {
|
|
1939
1943
|
if (Date.now() - mountedAt.current < 100) return;
|
|
1940
1944
|
confirmEdit();
|
|
1941
1945
|
}, [confirmEdit]);
|
|
@@ -1971,7 +1975,7 @@ function CellEditor() {
|
|
|
1971
1975
|
CellEditor.displayName = "CellEditor";
|
|
1972
1976
|
function SelectionOverlay() {
|
|
1973
1977
|
const { selection, getColumnWidth, rowHeight } = useSpreadsheet();
|
|
1974
|
-
const rects =
|
|
1978
|
+
const rects = React.useMemo(() => {
|
|
1975
1979
|
return selection.ranges.map((range, i) => {
|
|
1976
1980
|
const norm = normalizeRange(range.start, range.end);
|
|
1977
1981
|
const s = parseAddress(norm.start);
|
|
@@ -2057,8 +2061,8 @@ function SpreadsheetGrid({ className }) {
|
|
|
2057
2061
|
redo,
|
|
2058
2062
|
contextMenuItems
|
|
2059
2063
|
} = useSpreadsheet();
|
|
2060
|
-
const containerRef =
|
|
2061
|
-
const handleKeyDown =
|
|
2064
|
+
const containerRef = React.useRef(null);
|
|
2065
|
+
const handleKeyDown = React.useCallback(
|
|
2062
2066
|
(e) => {
|
|
2063
2067
|
if (editingCell) return;
|
|
2064
2068
|
if (e.key === "ArrowUp") {
|
|
@@ -2143,14 +2147,14 @@ function SpreadsheetGrid({ className }) {
|
|
|
2143
2147
|
const top = row * rowHeight;
|
|
2144
2148
|
return { left, top };
|
|
2145
2149
|
})() : null;
|
|
2146
|
-
const handleCopy =
|
|
2150
|
+
const handleCopy = React.useCallback(() => {
|
|
2147
2151
|
const range = selection.ranges[0];
|
|
2148
2152
|
if (range) {
|
|
2149
2153
|
const tsv = cellsToTSV(activeSheet.cells, range);
|
|
2150
2154
|
navigator.clipboard.writeText(tsv);
|
|
2151
2155
|
}
|
|
2152
2156
|
}, [selection, activeSheet]);
|
|
2153
|
-
const handlePaste =
|
|
2157
|
+
const handlePaste = React.useCallback(() => {
|
|
2154
2158
|
navigator.clipboard.readText().then((text) => {
|
|
2155
2159
|
if (!text) return;
|
|
2156
2160
|
const { values } = tsvToCells(text);
|
|
@@ -2162,7 +2166,7 @@ function SpreadsheetGrid({ className }) {
|
|
|
2162
2166
|
}
|
|
2163
2167
|
});
|
|
2164
2168
|
}, [selection, setCellValue]);
|
|
2165
|
-
const handleClearSelection =
|
|
2169
|
+
const handleClearSelection = React.useCallback(() => {
|
|
2166
2170
|
const range = selection.ranges[0];
|
|
2167
2171
|
if (!range) return;
|
|
2168
2172
|
const { start, end } = range;
|
|
@@ -2270,9 +2274,10 @@ function SpreadsheetGrid({ className }) {
|
|
|
2270
2274
|
] });
|
|
2271
2275
|
}
|
|
2272
2276
|
SpreadsheetGrid.displayName = "SpreadsheetGrid";
|
|
2277
|
+
var ALL_BUTTONS = ["undo", "bold", "align", "freeze", "format", "decimals", "formulaBar"];
|
|
2273
2278
|
var btnClass = "inline-flex items-center justify-center rounded px-2 py-1 text-[12px] font-medium text-zinc-600 transition-colors hover:bg-zinc-100 disabled:opacity-40 dark:text-zinc-300 dark:hover:bg-zinc-800";
|
|
2274
2279
|
var activeBtnClass = "bg-zinc-200 dark:bg-zinc-700";
|
|
2275
|
-
function DefaultToolbar({ extra }) {
|
|
2280
|
+
function DefaultToolbar({ extra, buttons }) {
|
|
2276
2281
|
const {
|
|
2277
2282
|
selection,
|
|
2278
2283
|
activeSheet,
|
|
@@ -2322,105 +2327,97 @@ function DefaultToolbar({ extra }) {
|
|
|
2322
2327
|
}
|
|
2323
2328
|
};
|
|
2324
2329
|
const formulaBarValue = editingCell ? editValue : cell?.formula ? "=" + cell.formula : cell?.value != null ? String(cell.value) : "";
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
}
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
}
|
|
2355
|
-
),
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
] })
|
|
2369
|
-
},
|
|
2370
|
-
align
|
|
2371
|
-
)),
|
|
2372
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-1 h-4 w-px bg-zinc-200 dark:bg-zinc-700" }),
|
|
2373
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2374
|
-
"button",
|
|
2375
|
-
{
|
|
2376
|
-
className: reactFancy.cn(btnClass, activeSheet.frozenRows > 0 && activeBtnClass),
|
|
2377
|
-
onClick: () => {
|
|
2378
|
-
if (activeSheet.frozenRows > 0) {
|
|
2379
|
-
setFrozenRows(0);
|
|
2380
|
-
} else {
|
|
2381
|
-
const row = selection.activeCell.match(/\d+/);
|
|
2382
|
-
setFrozenRows(row ? parseInt(row[0], 10) - 1 || 1 : 1);
|
|
2383
|
-
}
|
|
2384
|
-
},
|
|
2385
|
-
disabled: readOnly,
|
|
2386
|
-
title: activeSheet.frozenRows > 0 ? `Unfreeze rows (${activeSheet.frozenRows} frozen)` : "Freeze rows above current cell",
|
|
2387
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
2388
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }),
|
|
2389
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "4", x2: "21", y2: "4" }),
|
|
2390
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "14", x2: "21", y2: "14", strokeDasharray: "3 3" }),
|
|
2391
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "19", x2: "21", y2: "19", strokeDasharray: "3 3" })
|
|
2392
|
-
] })
|
|
2393
|
-
}
|
|
2394
|
-
),
|
|
2395
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2396
|
-
"button",
|
|
2397
|
-
{
|
|
2398
|
-
className: reactFancy.cn(btnClass, activeSheet.frozenCols > 0 && activeBtnClass),
|
|
2399
|
-
onClick: () => {
|
|
2400
|
-
if (activeSheet.frozenCols > 0) {
|
|
2401
|
-
setFrozenCols(0);
|
|
2402
|
-
} else {
|
|
2403
|
-
const colMatch = selection.activeCell.match(/^([A-Z]+)/);
|
|
2404
|
-
if (colMatch) {
|
|
2405
|
-
const col = colMatch[1].split("").reduce((acc, ch) => acc * 26 + ch.charCodeAt(0) - 64, 0) - 1;
|
|
2406
|
-
setFrozenCols(col || 1);
|
|
2330
|
+
const divider = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-1 h-4 w-px bg-zinc-200 dark:bg-zinc-700" });
|
|
2331
|
+
const groups = [];
|
|
2332
|
+
if (buttons.has("undo")) {
|
|
2333
|
+
groups.push(
|
|
2334
|
+
/* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
|
|
2335
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { className: btnClass, onClick: undo, disabled: !canUndo || readOnly, title: "Undo (Ctrl+Z)", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2336
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "1 4 1 10 7 10" }),
|
|
2337
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3.51 15a9 9 0 1 0 2.13-9.36L1 10" })
|
|
2338
|
+
] }) }),
|
|
2339
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { className: btnClass, onClick: redo, disabled: !canRedo || readOnly, title: "Redo (Ctrl+Y)", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2340
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "23 4 23 10 17 10" }),
|
|
2341
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })
|
|
2342
|
+
] }) })
|
|
2343
|
+
] }, "undo")
|
|
2344
|
+
);
|
|
2345
|
+
}
|
|
2346
|
+
if (buttons.has("bold")) {
|
|
2347
|
+
groups.push(
|
|
2348
|
+
/* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
|
|
2349
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { className: reactFancy.cn(btnClass, isBold && activeBtnClass), onClick: () => setCellFormat(selectedAddresses, { bold: !isBold }), disabled: readOnly, title: "Bold", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold", children: "B" }) }),
|
|
2350
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { className: reactFancy.cn(btnClass, isItalic && activeBtnClass), onClick: () => setCellFormat(selectedAddresses, { italic: !isItalic }), disabled: readOnly, title: "Italic", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic", children: "I" }) })
|
|
2351
|
+
] }, "bold")
|
|
2352
|
+
);
|
|
2353
|
+
}
|
|
2354
|
+
if (buttons.has("align")) {
|
|
2355
|
+
groups.push(
|
|
2356
|
+
/* @__PURE__ */ jsxRuntime.jsx(React__default.default.Fragment, { children: ["left", "center", "right"].map((align) => /* @__PURE__ */ jsxRuntime.jsx("button", { className: reactFancy.cn(btnClass, textAlign === align && activeBtnClass), onClick: () => setCellFormat(selectedAddresses, { textAlign: align }), disabled: readOnly, title: `Align ${align}`, children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
2357
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }),
|
|
2358
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: align === "left" ? "3" : align === "center" ? "6" : "9", y1: "12", x2: align === "left" ? "15" : align === "center" ? "18" : "21", y2: "12" }),
|
|
2359
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "18", x2: "21", y2: "18" })
|
|
2360
|
+
] }) }, align)) }, "align")
|
|
2361
|
+
);
|
|
2362
|
+
}
|
|
2363
|
+
if (buttons.has("freeze")) {
|
|
2364
|
+
groups.push(
|
|
2365
|
+
/* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
|
|
2366
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2367
|
+
"button",
|
|
2368
|
+
{
|
|
2369
|
+
className: reactFancy.cn(btnClass, activeSheet.frozenRows > 0 && activeBtnClass),
|
|
2370
|
+
onClick: () => {
|
|
2371
|
+
if (activeSheet.frozenRows > 0) {
|
|
2372
|
+
setFrozenRows(0);
|
|
2407
2373
|
} else {
|
|
2408
|
-
|
|
2374
|
+
const row = selection.activeCell.match(/\d+/);
|
|
2375
|
+
setFrozenRows(row ? parseInt(row[0], 10) - 1 || 1 : 1);
|
|
2409
2376
|
}
|
|
2410
|
-
}
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2377
|
+
},
|
|
2378
|
+
disabled: readOnly,
|
|
2379
|
+
title: activeSheet.frozenRows > 0 ? `Unfreeze rows (${activeSheet.frozenRows} frozen)` : "Freeze rows above current cell",
|
|
2380
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
2381
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }),
|
|
2382
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "4", x2: "21", y2: "4" }),
|
|
2383
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "14", x2: "21", y2: "14", strokeDasharray: "3 3" }),
|
|
2384
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "19", x2: "21", y2: "19", strokeDasharray: "3 3" })
|
|
2385
|
+
] })
|
|
2386
|
+
}
|
|
2387
|
+
),
|
|
2388
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2389
|
+
"button",
|
|
2390
|
+
{
|
|
2391
|
+
className: reactFancy.cn(btnClass, activeSheet.frozenCols > 0 && activeBtnClass),
|
|
2392
|
+
onClick: () => {
|
|
2393
|
+
if (activeSheet.frozenCols > 0) {
|
|
2394
|
+
setFrozenCols(0);
|
|
2395
|
+
} else {
|
|
2396
|
+
const colMatch = selection.activeCell.match(/^([A-Z]+)/);
|
|
2397
|
+
if (colMatch) {
|
|
2398
|
+
const col = colMatch[1].split("").reduce((acc, ch) => acc * 26 + ch.charCodeAt(0) - 64, 0) - 1;
|
|
2399
|
+
setFrozenCols(col || 1);
|
|
2400
|
+
} else {
|
|
2401
|
+
setFrozenCols(1);
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
},
|
|
2405
|
+
disabled: readOnly,
|
|
2406
|
+
title: activeSheet.frozenCols > 0 ? `Unfreeze columns (${activeSheet.frozenCols} frozen)` : "Freeze columns left of current cell",
|
|
2407
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
2408
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "3", x2: "9", y2: "21" }),
|
|
2409
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4", y1: "3", x2: "4", y2: "21" }),
|
|
2410
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "14", y1: "3", x2: "14", y2: "21", strokeDasharray: "3 3" }),
|
|
2411
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "19", y1: "3", x2: "19", y2: "21", strokeDasharray: "3 3" })
|
|
2412
|
+
] })
|
|
2413
|
+
}
|
|
2414
|
+
)
|
|
2415
|
+
] }, "freeze")
|
|
2416
|
+
);
|
|
2417
|
+
}
|
|
2418
|
+
if (buttons.has("format")) {
|
|
2419
|
+
groups.push(
|
|
2420
|
+
/* @__PURE__ */ jsxRuntime.jsx(React__default.default.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2424
2421
|
"select",
|
|
2425
2422
|
{
|
|
2426
2423
|
className: "h-6 rounded border border-zinc-200 bg-transparent px-1 text-[11px] text-zinc-600 outline-none hover:border-zinc-300 dark:border-zinc-700 dark:text-zinc-400 dark:hover:border-zinc-600",
|
|
@@ -2438,39 +2435,32 @@ function DefaultToolbar({ extra }) {
|
|
|
2438
2435
|
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "datetime", children: "Date & Time" })
|
|
2439
2436
|
]
|
|
2440
2437
|
}
|
|
2441
|
-
),
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
children:
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
]
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
),
|
|
2468
|
-
extra && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2469
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-1 h-4 w-px bg-zinc-200 dark:bg-zinc-700" }),
|
|
2470
|
-
extra
|
|
2471
|
-
] })
|
|
2472
|
-
] }),
|
|
2473
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-fancy-sheets-formula-bar": "", className: "flex items-center gap-2 border-b border-zinc-200 px-2 py-1 dark:border-zinc-700", children: [
|
|
2438
|
+
) }, "format")
|
|
2439
|
+
);
|
|
2440
|
+
}
|
|
2441
|
+
if (buttons.has("decimals")) {
|
|
2442
|
+
groups.push(
|
|
2443
|
+
/* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
|
|
2444
|
+
/* @__PURE__ */ jsxRuntime.jsxs("button", { className: btnClass, onClick: () => setCellFormat(selectedAddresses, { decimals: Math.max(0, currentDecimals - 1) }), disabled: readOnly || currentDecimals <= 0, title: `Decrease decimal places (currently ${currentDecimals})`, children: [
|
|
2445
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px]", children: ".0" }),
|
|
2446
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[8px]", children: "\u2190" })
|
|
2447
|
+
] }),
|
|
2448
|
+
/* @__PURE__ */ jsxRuntime.jsxs("button", { className: btnClass, onClick: () => setCellFormat(selectedAddresses, { decimals: currentDecimals + 1 }), disabled: readOnly, title: `Increase decimal places (currently ${currentDecimals})`, children: [
|
|
2449
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px]", children: ".00" }),
|
|
2450
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[8px]", children: "\u2192" })
|
|
2451
|
+
] })
|
|
2452
|
+
] }, "decimals")
|
|
2453
|
+
);
|
|
2454
|
+
}
|
|
2455
|
+
if (extra) {
|
|
2456
|
+
groups.push(/* @__PURE__ */ jsxRuntime.jsx(React__default.default.Fragment, { children: extra }, "extra"));
|
|
2457
|
+
}
|
|
2458
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2459
|
+
groups.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-0.5 border-b border-zinc-200 px-1.5 py-1 dark:border-zinc-700", children: groups.map((group, i) => /* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
|
|
2460
|
+
i > 0 && divider,
|
|
2461
|
+
group
|
|
2462
|
+
] }, i)) }),
|
|
2463
|
+
buttons.has("formulaBar") && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-fancy-sheets-formula-bar": "", className: "flex items-center gap-2 border-b border-zinc-200 px-2 py-1 dark:border-zinc-700", children: [
|
|
2474
2464
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-12 shrink-0 text-center text-[11px] font-medium text-zinc-500 dark:text-zinc-400", children: selection.activeCell }),
|
|
2475
2465
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-px bg-zinc-200 dark:bg-zinc-700" }),
|
|
2476
2466
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2487,15 +2477,16 @@ function DefaultToolbar({ extra }) {
|
|
|
2487
2477
|
] })
|
|
2488
2478
|
] });
|
|
2489
2479
|
}
|
|
2490
|
-
function SpreadsheetToolbar({ children, className, extra }) {
|
|
2491
|
-
|
|
2480
|
+
function SpreadsheetToolbar({ children, className, extra, buttons: buttonsProp }) {
|
|
2481
|
+
const buttonsSet = new Set(buttonsProp ?? ALL_BUTTONS);
|
|
2482
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-fancy-sheets-toolbar": "", className: reactFancy.cn("", className), children: children ?? /* @__PURE__ */ jsxRuntime.jsx(DefaultToolbar, { extra, buttons: buttonsSet }) });
|
|
2492
2483
|
}
|
|
2493
2484
|
SpreadsheetToolbar.displayName = "SpreadsheetToolbar";
|
|
2494
2485
|
function SpreadsheetSheetTabs({ className }) {
|
|
2495
2486
|
const { workbook, setActiveSheet, addSheet, renameSheet, deleteSheet, readOnly } = useSpreadsheet();
|
|
2496
|
-
const [renamingId, setRenamingId] =
|
|
2497
|
-
const [renameValue, setRenameValue] =
|
|
2498
|
-
const handleDoubleClick =
|
|
2487
|
+
const [renamingId, setRenamingId] = React.useState(null);
|
|
2488
|
+
const [renameValue, setRenameValue] = React.useState("");
|
|
2489
|
+
const handleDoubleClick = React.useCallback(
|
|
2499
2490
|
(sheetId, name) => {
|
|
2500
2491
|
if (readOnly) return;
|
|
2501
2492
|
setRenamingId(sheetId);
|
|
@@ -2503,7 +2494,7 @@ function SpreadsheetSheetTabs({ className }) {
|
|
|
2503
2494
|
},
|
|
2504
2495
|
[readOnly]
|
|
2505
2496
|
);
|
|
2506
|
-
const handleRenameConfirm =
|
|
2497
|
+
const handleRenameConfirm = React.useCallback(() => {
|
|
2507
2498
|
if (renamingId && renameValue.trim()) {
|
|
2508
2499
|
renameSheet(renamingId, renameValue.trim());
|
|
2509
2500
|
}
|
|
@@ -2589,18 +2580,18 @@ function SpreadsheetRoot({
|
|
|
2589
2580
|
contextMenuItems
|
|
2590
2581
|
}) {
|
|
2591
2582
|
const { state, actions } = useSpreadsheetStore(data ?? defaultData);
|
|
2592
|
-
const onChangeRef =
|
|
2593
|
-
const isExternalSync =
|
|
2594
|
-
const prevDataRef =
|
|
2583
|
+
const onChangeRef = React.useRef(onChange);
|
|
2584
|
+
const isExternalSync = React.useRef(false);
|
|
2585
|
+
const prevDataRef = React.useRef(data);
|
|
2595
2586
|
onChangeRef.current = onChange;
|
|
2596
|
-
|
|
2587
|
+
React.useEffect(() => {
|
|
2597
2588
|
if (data && data !== prevDataRef.current && data !== state.workbook) {
|
|
2598
2589
|
isExternalSync.current = true;
|
|
2599
2590
|
actions.setWorkbook(data);
|
|
2600
2591
|
prevDataRef.current = data;
|
|
2601
2592
|
}
|
|
2602
2593
|
}, [data]);
|
|
2603
|
-
|
|
2594
|
+
React.useEffect(() => {
|
|
2604
2595
|
if (isExternalSync.current) {
|
|
2605
2596
|
isExternalSync.current = false;
|
|
2606
2597
|
return;
|
|
@@ -2608,15 +2599,15 @@ function SpreadsheetRoot({
|
|
|
2608
2599
|
prevDataRef.current = state.workbook;
|
|
2609
2600
|
onChangeRef.current?.(state.workbook);
|
|
2610
2601
|
}, [state.workbook]);
|
|
2611
|
-
const activeSheet =
|
|
2602
|
+
const activeSheet = React.useMemo(
|
|
2612
2603
|
() => state.workbook.sheets.find((s) => s.id === state.workbook.activeSheetId),
|
|
2613
2604
|
[state.workbook]
|
|
2614
2605
|
);
|
|
2615
|
-
const getColumnWidth =
|
|
2606
|
+
const getColumnWidth = React.useCallback(
|
|
2616
2607
|
(col) => activeSheet.columnWidths[col] ?? defaultColumnWidth,
|
|
2617
2608
|
[activeSheet.columnWidths, defaultColumnWidth]
|
|
2618
2609
|
);
|
|
2619
|
-
const isCellSelected =
|
|
2610
|
+
const isCellSelected = React.useCallback(
|
|
2620
2611
|
(address) => {
|
|
2621
2612
|
const target = parseAddress(address);
|
|
2622
2613
|
return state.selection.ranges.some((range) => {
|
|
@@ -2628,12 +2619,12 @@ function SpreadsheetRoot({
|
|
|
2628
2619
|
},
|
|
2629
2620
|
[state.selection.ranges]
|
|
2630
2621
|
);
|
|
2631
|
-
const isCellActive =
|
|
2622
|
+
const isCellActive = React.useCallback(
|
|
2632
2623
|
(address) => state.selection.activeCell === address,
|
|
2633
2624
|
[state.selection.activeCell]
|
|
2634
2625
|
);
|
|
2635
|
-
const isDraggingRef =
|
|
2636
|
-
const ctx =
|
|
2626
|
+
const isDraggingRef = React.useRef(false);
|
|
2627
|
+
const ctx = React.useMemo(
|
|
2637
2628
|
() => ({
|
|
2638
2629
|
workbook: state.workbook,
|
|
2639
2630
|
activeSheet,
|
|
@@ -2672,11 +2663,11 @@ var Spreadsheet = Object.assign(SpreadsheetRoot, {
|
|
|
2672
2663
|
SheetTabs: SpreadsheetSheetTabs
|
|
2673
2664
|
});
|
|
2674
2665
|
function Sheet({ data, onChange, contextMenuItems, ...props }) {
|
|
2675
|
-
const workbook =
|
|
2666
|
+
const workbook = React.useMemo(
|
|
2676
2667
|
() => data ? { sheets: [data], activeSheetId: data.id } : void 0,
|
|
2677
2668
|
[data]
|
|
2678
2669
|
);
|
|
2679
|
-
const handleChange =
|
|
2670
|
+
const handleChange = React.useMemo(() => {
|
|
2680
2671
|
if (!onChange) return void 0;
|
|
2681
2672
|
return (wb) => onChange(wb.sheets[0]);
|
|
2682
2673
|
}, [onChange]);
|
|
@@ -2687,11 +2678,12 @@ function SheetWorkbook({
|
|
|
2687
2678
|
hideToolbar = false,
|
|
2688
2679
|
hideTabs = false,
|
|
2689
2680
|
toolbarExtra,
|
|
2681
|
+
toolbarButtons,
|
|
2690
2682
|
contextMenuItems,
|
|
2691
2683
|
...props
|
|
2692
2684
|
}) {
|
|
2693
2685
|
return /* @__PURE__ */ jsxRuntime.jsxs(Spreadsheet, { ...props, contextMenuItems, children: [
|
|
2694
|
-
!hideToolbar && /* @__PURE__ */ jsxRuntime.jsx(Spreadsheet.Toolbar, { extra: toolbarExtra }),
|
|
2686
|
+
!hideToolbar && /* @__PURE__ */ jsxRuntime.jsx(Spreadsheet.Toolbar, { extra: toolbarExtra, buttons: toolbarButtons }),
|
|
2695
2687
|
/* @__PURE__ */ jsxRuntime.jsx(Spreadsheet.Grid, {}),
|
|
2696
2688
|
!hideTabs && /* @__PURE__ */ jsxRuntime.jsx(Spreadsheet.SheetTabs, {})
|
|
2697
2689
|
] });
|