@particle-academy/fancy-sheets 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +194 -191
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -8
- package/dist/index.d.ts +16 -8
- package/dist/index.js +144 -145
- package/dist/index.js.map +1 -1
- 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);
|
|
@@ -2035,6 +2039,26 @@ function tsvToCells(tsv) {
|
|
|
2035
2039
|
const cols = Math.max(...values.map((v) => v.length));
|
|
2036
2040
|
return { values, rows, cols };
|
|
2037
2041
|
}
|
|
2042
|
+
function renderMenuItems(items, activeCell) {
|
|
2043
|
+
return items.map((item, i) => {
|
|
2044
|
+
if (item.items && item.items.length > 0) {
|
|
2045
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactFancy.ContextMenu.Sub, { children: [
|
|
2046
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.SubTrigger, { children: item.label }),
|
|
2047
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.SubContent, { children: renderMenuItems(item.items, activeCell) })
|
|
2048
|
+
] }, i);
|
|
2049
|
+
}
|
|
2050
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2051
|
+
reactFancy.ContextMenu.Item,
|
|
2052
|
+
{
|
|
2053
|
+
onClick: () => item.onClick?.(activeCell),
|
|
2054
|
+
disabled: typeof item.disabled === "function" ? item.disabled(activeCell) : item.disabled,
|
|
2055
|
+
danger: item.danger,
|
|
2056
|
+
children: item.label
|
|
2057
|
+
},
|
|
2058
|
+
i
|
|
2059
|
+
);
|
|
2060
|
+
});
|
|
2061
|
+
}
|
|
2038
2062
|
function SpreadsheetGrid({ className }) {
|
|
2039
2063
|
const {
|
|
2040
2064
|
columnCount,
|
|
@@ -2057,8 +2081,8 @@ function SpreadsheetGrid({ className }) {
|
|
|
2057
2081
|
redo,
|
|
2058
2082
|
contextMenuItems
|
|
2059
2083
|
} = useSpreadsheet();
|
|
2060
|
-
const containerRef =
|
|
2061
|
-
const handleKeyDown =
|
|
2084
|
+
const containerRef = React.useRef(null);
|
|
2085
|
+
const handleKeyDown = React.useCallback(
|
|
2062
2086
|
(e) => {
|
|
2063
2087
|
if (editingCell) return;
|
|
2064
2088
|
if (e.key === "ArrowUp") {
|
|
@@ -2143,14 +2167,14 @@ function SpreadsheetGrid({ className }) {
|
|
|
2143
2167
|
const top = row * rowHeight;
|
|
2144
2168
|
return { left, top };
|
|
2145
2169
|
})() : null;
|
|
2146
|
-
const handleCopy =
|
|
2170
|
+
const handleCopy = React.useCallback(() => {
|
|
2147
2171
|
const range = selection.ranges[0];
|
|
2148
2172
|
if (range) {
|
|
2149
2173
|
const tsv = cellsToTSV(activeSheet.cells, range);
|
|
2150
2174
|
navigator.clipboard.writeText(tsv);
|
|
2151
2175
|
}
|
|
2152
2176
|
}, [selection, activeSheet]);
|
|
2153
|
-
const handlePaste =
|
|
2177
|
+
const handlePaste = React.useCallback(() => {
|
|
2154
2178
|
navigator.clipboard.readText().then((text) => {
|
|
2155
2179
|
if (!text) return;
|
|
2156
2180
|
const { values } = tsvToCells(text);
|
|
@@ -2162,7 +2186,7 @@ function SpreadsheetGrid({ className }) {
|
|
|
2162
2186
|
}
|
|
2163
2187
|
});
|
|
2164
2188
|
}, [selection, setCellValue]);
|
|
2165
|
-
const handleClearSelection =
|
|
2189
|
+
const handleClearSelection = React.useCallback(() => {
|
|
2166
2190
|
const range = selection.ranges[0];
|
|
2167
2191
|
if (!range) return;
|
|
2168
2192
|
const { start, end } = range;
|
|
@@ -2254,25 +2278,17 @@ function SpreadsheetGrid({ className }) {
|
|
|
2254
2278
|
if (!items || items.length === 0) return null;
|
|
2255
2279
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2256
2280
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Separator, {}),
|
|
2257
|
-
items
|
|
2258
|
-
reactFancy.ContextMenu.Item,
|
|
2259
|
-
{
|
|
2260
|
-
onClick: () => item.onClick(selection.activeCell),
|
|
2261
|
-
disabled: typeof item.disabled === "function" ? item.disabled(selection.activeCell) : item.disabled,
|
|
2262
|
-
danger: item.danger,
|
|
2263
|
-
children: item.label
|
|
2264
|
-
},
|
|
2265
|
-
i
|
|
2266
|
-
))
|
|
2281
|
+
renderMenuItems(items, selection.activeCell)
|
|
2267
2282
|
] });
|
|
2268
2283
|
})()
|
|
2269
2284
|
] })
|
|
2270
2285
|
] });
|
|
2271
2286
|
}
|
|
2272
2287
|
SpreadsheetGrid.displayName = "SpreadsheetGrid";
|
|
2288
|
+
var ALL_BUTTONS = ["undo", "bold", "align", "freeze", "format", "decimals", "formulaBar"];
|
|
2273
2289
|
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
2290
|
var activeBtnClass = "bg-zinc-200 dark:bg-zinc-700";
|
|
2275
|
-
function DefaultToolbar({ extra }) {
|
|
2291
|
+
function DefaultToolbar({ extra, buttons }) {
|
|
2276
2292
|
const {
|
|
2277
2293
|
selection,
|
|
2278
2294
|
activeSheet,
|
|
@@ -2322,105 +2338,97 @@ function DefaultToolbar({ extra }) {
|
|
|
2322
2338
|
}
|
|
2323
2339
|
};
|
|
2324
2340
|
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);
|
|
2341
|
+
const divider = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-1 h-4 w-px bg-zinc-200 dark:bg-zinc-700" });
|
|
2342
|
+
const groups = [];
|
|
2343
|
+
if (buttons.has("undo")) {
|
|
2344
|
+
groups.push(
|
|
2345
|
+
/* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
|
|
2346
|
+
/* @__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: [
|
|
2347
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "1 4 1 10 7 10" }),
|
|
2348
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3.51 15a9 9 0 1 0 2.13-9.36L1 10" })
|
|
2349
|
+
] }) }),
|
|
2350
|
+
/* @__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: [
|
|
2351
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "23 4 23 10 17 10" }),
|
|
2352
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })
|
|
2353
|
+
] }) })
|
|
2354
|
+
] }, "undo")
|
|
2355
|
+
);
|
|
2356
|
+
}
|
|
2357
|
+
if (buttons.has("bold")) {
|
|
2358
|
+
groups.push(
|
|
2359
|
+
/* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
|
|
2360
|
+
/* @__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" }) }),
|
|
2361
|
+
/* @__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" }) })
|
|
2362
|
+
] }, "bold")
|
|
2363
|
+
);
|
|
2364
|
+
}
|
|
2365
|
+
if (buttons.has("align")) {
|
|
2366
|
+
groups.push(
|
|
2367
|
+
/* @__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: [
|
|
2368
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }),
|
|
2369
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: align === "left" ? "3" : align === "center" ? "6" : "9", y1: "12", x2: align === "left" ? "15" : align === "center" ? "18" : "21", y2: "12" }),
|
|
2370
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "18", x2: "21", y2: "18" })
|
|
2371
|
+
] }) }, align)) }, "align")
|
|
2372
|
+
);
|
|
2373
|
+
}
|
|
2374
|
+
if (buttons.has("freeze")) {
|
|
2375
|
+
groups.push(
|
|
2376
|
+
/* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
|
|
2377
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2378
|
+
"button",
|
|
2379
|
+
{
|
|
2380
|
+
className: reactFancy.cn(btnClass, activeSheet.frozenRows > 0 && activeBtnClass),
|
|
2381
|
+
onClick: () => {
|
|
2382
|
+
if (activeSheet.frozenRows > 0) {
|
|
2383
|
+
setFrozenRows(0);
|
|
2407
2384
|
} else {
|
|
2408
|
-
|
|
2385
|
+
const row = selection.activeCell.match(/\d+/);
|
|
2386
|
+
setFrozenRows(row ? parseInt(row[0], 10) - 1 || 1 : 1);
|
|
2409
2387
|
}
|
|
2410
|
-
}
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2388
|
+
},
|
|
2389
|
+
disabled: readOnly,
|
|
2390
|
+
title: activeSheet.frozenRows > 0 ? `Unfreeze rows (${activeSheet.frozenRows} frozen)` : "Freeze rows above current cell",
|
|
2391
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
2392
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }),
|
|
2393
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "4", x2: "21", y2: "4" }),
|
|
2394
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "14", x2: "21", y2: "14", strokeDasharray: "3 3" }),
|
|
2395
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "19", x2: "21", y2: "19", strokeDasharray: "3 3" })
|
|
2396
|
+
] })
|
|
2397
|
+
}
|
|
2398
|
+
),
|
|
2399
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2400
|
+
"button",
|
|
2401
|
+
{
|
|
2402
|
+
className: reactFancy.cn(btnClass, activeSheet.frozenCols > 0 && activeBtnClass),
|
|
2403
|
+
onClick: () => {
|
|
2404
|
+
if (activeSheet.frozenCols > 0) {
|
|
2405
|
+
setFrozenCols(0);
|
|
2406
|
+
} else {
|
|
2407
|
+
const colMatch = selection.activeCell.match(/^([A-Z]+)/);
|
|
2408
|
+
if (colMatch) {
|
|
2409
|
+
const col = colMatch[1].split("").reduce((acc, ch) => acc * 26 + ch.charCodeAt(0) - 64, 0) - 1;
|
|
2410
|
+
setFrozenCols(col || 1);
|
|
2411
|
+
} else {
|
|
2412
|
+
setFrozenCols(1);
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
},
|
|
2416
|
+
disabled: readOnly,
|
|
2417
|
+
title: activeSheet.frozenCols > 0 ? `Unfreeze columns (${activeSheet.frozenCols} frozen)` : "Freeze columns left of current cell",
|
|
2418
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
2419
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "3", x2: "9", y2: "21" }),
|
|
2420
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4", y1: "3", x2: "4", y2: "21" }),
|
|
2421
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "14", y1: "3", x2: "14", y2: "21", strokeDasharray: "3 3" }),
|
|
2422
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "19", y1: "3", x2: "19", y2: "21", strokeDasharray: "3 3" })
|
|
2423
|
+
] })
|
|
2424
|
+
}
|
|
2425
|
+
)
|
|
2426
|
+
] }, "freeze")
|
|
2427
|
+
);
|
|
2428
|
+
}
|
|
2429
|
+
if (buttons.has("format")) {
|
|
2430
|
+
groups.push(
|
|
2431
|
+
/* @__PURE__ */ jsxRuntime.jsx(React__default.default.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2424
2432
|
"select",
|
|
2425
2433
|
{
|
|
2426
2434
|
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 +2446,32 @@ function DefaultToolbar({ extra }) {
|
|
|
2438
2446
|
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "datetime", children: "Date & Time" })
|
|
2439
2447
|
]
|
|
2440
2448
|
}
|
|
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: [
|
|
2449
|
+
) }, "format")
|
|
2450
|
+
);
|
|
2451
|
+
}
|
|
2452
|
+
if (buttons.has("decimals")) {
|
|
2453
|
+
groups.push(
|
|
2454
|
+
/* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
|
|
2455
|
+
/* @__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: [
|
|
2456
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px]", children: ".0" }),
|
|
2457
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[8px]", children: "\u2190" })
|
|
2458
|
+
] }),
|
|
2459
|
+
/* @__PURE__ */ jsxRuntime.jsxs("button", { className: btnClass, onClick: () => setCellFormat(selectedAddresses, { decimals: currentDecimals + 1 }), disabled: readOnly, title: `Increase decimal places (currently ${currentDecimals})`, children: [
|
|
2460
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px]", children: ".00" }),
|
|
2461
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[8px]", children: "\u2192" })
|
|
2462
|
+
] })
|
|
2463
|
+
] }, "decimals")
|
|
2464
|
+
);
|
|
2465
|
+
}
|
|
2466
|
+
if (extra) {
|
|
2467
|
+
groups.push(/* @__PURE__ */ jsxRuntime.jsx(React__default.default.Fragment, { children: extra }, "extra"));
|
|
2468
|
+
}
|
|
2469
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2470
|
+
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: [
|
|
2471
|
+
i > 0 && divider,
|
|
2472
|
+
group
|
|
2473
|
+
] }, i)) }),
|
|
2474
|
+
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
2475
|
/* @__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
2476
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-px bg-zinc-200 dark:bg-zinc-700" }),
|
|
2476
2477
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2487,15 +2488,16 @@ function DefaultToolbar({ extra }) {
|
|
|
2487
2488
|
] })
|
|
2488
2489
|
] });
|
|
2489
2490
|
}
|
|
2490
|
-
function SpreadsheetToolbar({ children, className, extra }) {
|
|
2491
|
-
|
|
2491
|
+
function SpreadsheetToolbar({ children, className, extra, buttons: buttonsProp }) {
|
|
2492
|
+
const buttonsSet = new Set(buttonsProp ?? ALL_BUTTONS);
|
|
2493
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-fancy-sheets-toolbar": "", className: reactFancy.cn("", className), children: children ?? /* @__PURE__ */ jsxRuntime.jsx(DefaultToolbar, { extra, buttons: buttonsSet }) });
|
|
2492
2494
|
}
|
|
2493
2495
|
SpreadsheetToolbar.displayName = "SpreadsheetToolbar";
|
|
2494
2496
|
function SpreadsheetSheetTabs({ className }) {
|
|
2495
2497
|
const { workbook, setActiveSheet, addSheet, renameSheet, deleteSheet, readOnly } = useSpreadsheet();
|
|
2496
|
-
const [renamingId, setRenamingId] =
|
|
2497
|
-
const [renameValue, setRenameValue] =
|
|
2498
|
-
const handleDoubleClick =
|
|
2498
|
+
const [renamingId, setRenamingId] = React.useState(null);
|
|
2499
|
+
const [renameValue, setRenameValue] = React.useState("");
|
|
2500
|
+
const handleDoubleClick = React.useCallback(
|
|
2499
2501
|
(sheetId, name) => {
|
|
2500
2502
|
if (readOnly) return;
|
|
2501
2503
|
setRenamingId(sheetId);
|
|
@@ -2503,7 +2505,7 @@ function SpreadsheetSheetTabs({ className }) {
|
|
|
2503
2505
|
},
|
|
2504
2506
|
[readOnly]
|
|
2505
2507
|
);
|
|
2506
|
-
const handleRenameConfirm =
|
|
2508
|
+
const handleRenameConfirm = React.useCallback(() => {
|
|
2507
2509
|
if (renamingId && renameValue.trim()) {
|
|
2508
2510
|
renameSheet(renamingId, renameValue.trim());
|
|
2509
2511
|
}
|
|
@@ -2589,18 +2591,18 @@ function SpreadsheetRoot({
|
|
|
2589
2591
|
contextMenuItems
|
|
2590
2592
|
}) {
|
|
2591
2593
|
const { state, actions } = useSpreadsheetStore(data ?? defaultData);
|
|
2592
|
-
const onChangeRef =
|
|
2593
|
-
const isExternalSync =
|
|
2594
|
-
const prevDataRef =
|
|
2594
|
+
const onChangeRef = React.useRef(onChange);
|
|
2595
|
+
const isExternalSync = React.useRef(false);
|
|
2596
|
+
const prevDataRef = React.useRef(data);
|
|
2595
2597
|
onChangeRef.current = onChange;
|
|
2596
|
-
|
|
2598
|
+
React.useEffect(() => {
|
|
2597
2599
|
if (data && data !== prevDataRef.current && data !== state.workbook) {
|
|
2598
2600
|
isExternalSync.current = true;
|
|
2599
2601
|
actions.setWorkbook(data);
|
|
2600
2602
|
prevDataRef.current = data;
|
|
2601
2603
|
}
|
|
2602
2604
|
}, [data]);
|
|
2603
|
-
|
|
2605
|
+
React.useEffect(() => {
|
|
2604
2606
|
if (isExternalSync.current) {
|
|
2605
2607
|
isExternalSync.current = false;
|
|
2606
2608
|
return;
|
|
@@ -2608,15 +2610,15 @@ function SpreadsheetRoot({
|
|
|
2608
2610
|
prevDataRef.current = state.workbook;
|
|
2609
2611
|
onChangeRef.current?.(state.workbook);
|
|
2610
2612
|
}, [state.workbook]);
|
|
2611
|
-
const activeSheet =
|
|
2613
|
+
const activeSheet = React.useMemo(
|
|
2612
2614
|
() => state.workbook.sheets.find((s) => s.id === state.workbook.activeSheetId),
|
|
2613
2615
|
[state.workbook]
|
|
2614
2616
|
);
|
|
2615
|
-
const getColumnWidth =
|
|
2617
|
+
const getColumnWidth = React.useCallback(
|
|
2616
2618
|
(col) => activeSheet.columnWidths[col] ?? defaultColumnWidth,
|
|
2617
2619
|
[activeSheet.columnWidths, defaultColumnWidth]
|
|
2618
2620
|
);
|
|
2619
|
-
const isCellSelected =
|
|
2621
|
+
const isCellSelected = React.useCallback(
|
|
2620
2622
|
(address) => {
|
|
2621
2623
|
const target = parseAddress(address);
|
|
2622
2624
|
return state.selection.ranges.some((range) => {
|
|
@@ -2628,12 +2630,12 @@ function SpreadsheetRoot({
|
|
|
2628
2630
|
},
|
|
2629
2631
|
[state.selection.ranges]
|
|
2630
2632
|
);
|
|
2631
|
-
const isCellActive =
|
|
2633
|
+
const isCellActive = React.useCallback(
|
|
2632
2634
|
(address) => state.selection.activeCell === address,
|
|
2633
2635
|
[state.selection.activeCell]
|
|
2634
2636
|
);
|
|
2635
|
-
const isDraggingRef =
|
|
2636
|
-
const ctx =
|
|
2637
|
+
const isDraggingRef = React.useRef(false);
|
|
2638
|
+
const ctx = React.useMemo(
|
|
2637
2639
|
() => ({
|
|
2638
2640
|
workbook: state.workbook,
|
|
2639
2641
|
activeSheet,
|
|
@@ -2672,11 +2674,11 @@ var Spreadsheet = Object.assign(SpreadsheetRoot, {
|
|
|
2672
2674
|
SheetTabs: SpreadsheetSheetTabs
|
|
2673
2675
|
});
|
|
2674
2676
|
function Sheet({ data, onChange, contextMenuItems, ...props }) {
|
|
2675
|
-
const workbook =
|
|
2677
|
+
const workbook = React.useMemo(
|
|
2676
2678
|
() => data ? { sheets: [data], activeSheetId: data.id } : void 0,
|
|
2677
2679
|
[data]
|
|
2678
2680
|
);
|
|
2679
|
-
const handleChange =
|
|
2681
|
+
const handleChange = React.useMemo(() => {
|
|
2680
2682
|
if (!onChange) return void 0;
|
|
2681
2683
|
return (wb) => onChange(wb.sheets[0]);
|
|
2682
2684
|
}, [onChange]);
|
|
@@ -2687,11 +2689,12 @@ function SheetWorkbook({
|
|
|
2687
2689
|
hideToolbar = false,
|
|
2688
2690
|
hideTabs = false,
|
|
2689
2691
|
toolbarExtra,
|
|
2692
|
+
toolbarButtons,
|
|
2690
2693
|
contextMenuItems,
|
|
2691
2694
|
...props
|
|
2692
2695
|
}) {
|
|
2693
2696
|
return /* @__PURE__ */ jsxRuntime.jsxs(Spreadsheet, { ...props, contextMenuItems, children: [
|
|
2694
|
-
!hideToolbar && /* @__PURE__ */ jsxRuntime.jsx(Spreadsheet.Toolbar, { extra: toolbarExtra }),
|
|
2697
|
+
!hideToolbar && /* @__PURE__ */ jsxRuntime.jsx(Spreadsheet.Toolbar, { extra: toolbarExtra, buttons: toolbarButtons }),
|
|
2695
2698
|
/* @__PURE__ */ jsxRuntime.jsx(Spreadsheet.Grid, {}),
|
|
2696
2699
|
!hideTabs && /* @__PURE__ */ jsxRuntime.jsx(Spreadsheet.SheetTabs, {})
|
|
2697
2700
|
] });
|