@particle-academy/fancy-sheets 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +136 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +137 -74
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createContext, memo, useCallback, useContext, useRef, useEffect, useMemo, useState, useReducer } from 'react';
|
|
2
|
-
import { cn } from '@particle-academy/react-fancy';
|
|
2
|
+
import { cn, ContextMenu } from '@particle-academy/react-fancy';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
|
|
5
5
|
// src/components/Spreadsheet/Spreadsheet.tsx
|
|
@@ -1599,14 +1599,19 @@ function ColumnResizeHandle({ colIndex }) {
|
|
|
1599
1599
|
}
|
|
1600
1600
|
ColumnResizeHandle.displayName = "ColumnResizeHandle";
|
|
1601
1601
|
function ColumnHeaders() {
|
|
1602
|
-
const { columnCount, rowCount, rowHeight, getColumnWidth, selectRange } = useSpreadsheet();
|
|
1602
|
+
const { columnCount, rowCount, rowHeight, getColumnWidth, selection, selectRange } = useSpreadsheet();
|
|
1603
1603
|
const handleColumnClick = useCallback(
|
|
1604
|
-
(colIdx) => {
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1604
|
+
(colIdx, e) => {
|
|
1605
|
+
if (e.shiftKey) {
|
|
1606
|
+
const activeCol = parseAddress(selection.activeCell).col;
|
|
1607
|
+
const minCol = Math.min(activeCol, colIdx);
|
|
1608
|
+
const maxCol = Math.max(activeCol, colIdx);
|
|
1609
|
+
selectRange(toAddress(0, minCol), toAddress(rowCount - 1, maxCol));
|
|
1610
|
+
} else {
|
|
1611
|
+
selectRange(toAddress(0, colIdx), toAddress(rowCount - 1, colIdx));
|
|
1612
|
+
}
|
|
1608
1613
|
},
|
|
1609
|
-
[rowCount, selectRange]
|
|
1614
|
+
[rowCount, selectRange, selection.activeCell]
|
|
1610
1615
|
);
|
|
1611
1616
|
return /* @__PURE__ */ jsxs(
|
|
1612
1617
|
"div",
|
|
@@ -1627,7 +1632,7 @@ function ColumnHeaders() {
|
|
|
1627
1632
|
{
|
|
1628
1633
|
className: "relative flex shrink-0 cursor-pointer items-center justify-center border-r border-zinc-300 text-[11px] font-medium text-zinc-500 select-none hover:bg-zinc-200 dark:border-zinc-600 dark:text-zinc-400 dark:hover:bg-zinc-700",
|
|
1629
1634
|
style: { width: getColumnWidth(i), minWidth: getColumnWidth(i) },
|
|
1630
|
-
onClick: () => handleColumnClick(i),
|
|
1635
|
+
onClick: (e) => handleColumnClick(i, e),
|
|
1631
1636
|
children: [
|
|
1632
1637
|
columnToLetter(i),
|
|
1633
1638
|
/* @__PURE__ */ jsx(ColumnResizeHandle, { colIndex: i })
|
|
@@ -1641,12 +1646,20 @@ function ColumnHeaders() {
|
|
|
1641
1646
|
}
|
|
1642
1647
|
ColumnHeaders.displayName = "ColumnHeaders";
|
|
1643
1648
|
function RowHeader({ rowIndex }) {
|
|
1644
|
-
const { rowHeight, columnCount, selectRange } = useSpreadsheet();
|
|
1645
|
-
const handleClick = useCallback(
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1649
|
+
const { rowHeight, columnCount, selection, selectRange, extendSelection } = useSpreadsheet();
|
|
1650
|
+
const handleClick = useCallback(
|
|
1651
|
+
(e) => {
|
|
1652
|
+
if (e.shiftKey) {
|
|
1653
|
+
const activeRow = parseAddress(selection.activeCell).row;
|
|
1654
|
+
const minRow = Math.min(activeRow, rowIndex);
|
|
1655
|
+
const maxRow = Math.max(activeRow, rowIndex);
|
|
1656
|
+
selectRange(toAddress(minRow, 0), toAddress(maxRow, columnCount - 1));
|
|
1657
|
+
} else {
|
|
1658
|
+
selectRange(toAddress(rowIndex, 0), toAddress(rowIndex, columnCount - 1));
|
|
1659
|
+
}
|
|
1660
|
+
},
|
|
1661
|
+
[rowIndex, columnCount, selectRange, selection.activeCell]
|
|
1662
|
+
);
|
|
1650
1663
|
return /* @__PURE__ */ jsx(
|
|
1651
1664
|
"div",
|
|
1652
1665
|
{
|
|
@@ -1752,9 +1765,9 @@ var Cell = memo(function Cell2({ address, row, col }) {
|
|
|
1752
1765
|
"data-active": isActive || void 0,
|
|
1753
1766
|
role: "gridcell",
|
|
1754
1767
|
className: cn(
|
|
1755
|
-
"relative flex items-center truncate border-r border-b border-zinc-200 px-1.5 text-[13px] dark:border-zinc-700",
|
|
1768
|
+
"relative flex items-center truncate border-r border-b border-zinc-200 bg-white px-1.5 text-[13px] dark:border-zinc-700 dark:bg-zinc-900",
|
|
1756
1769
|
isActive && "ring-2 ring-inset ring-blue-500",
|
|
1757
|
-
isSelected && !isActive && "bg-blue-
|
|
1770
|
+
isSelected && !isActive && "bg-blue-50 dark:bg-blue-950/40"
|
|
1758
1771
|
),
|
|
1759
1772
|
style: { width, minWidth: width, height: rowHeight, ...formatStyle },
|
|
1760
1773
|
onMouseDown: handleMouseDown,
|
|
@@ -1897,6 +1910,8 @@ function SpreadsheetGrid({ className }) {
|
|
|
1897
1910
|
confirmEdit,
|
|
1898
1911
|
cancelEdit,
|
|
1899
1912
|
setCellValue,
|
|
1913
|
+
setFrozenRows,
|
|
1914
|
+
setFrozenCols,
|
|
1900
1915
|
undo,
|
|
1901
1916
|
redo
|
|
1902
1917
|
} = useSpreadsheet();
|
|
@@ -1986,66 +2001,114 @@ function SpreadsheetGrid({ className }) {
|
|
|
1986
2001
|
const top = row * rowHeight;
|
|
1987
2002
|
return { left, top };
|
|
1988
2003
|
})() : null;
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
{
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2004
|
+
const handleCopy = useCallback(() => {
|
|
2005
|
+
const range = selection.ranges[0];
|
|
2006
|
+
if (range) {
|
|
2007
|
+
const tsv = cellsToTSV(activeSheet.cells, range);
|
|
2008
|
+
navigator.clipboard.writeText(tsv);
|
|
2009
|
+
}
|
|
2010
|
+
}, [selection, activeSheet]);
|
|
2011
|
+
const handlePaste = useCallback(() => {
|
|
2012
|
+
navigator.clipboard.readText().then((text) => {
|
|
2013
|
+
if (!text) return;
|
|
2014
|
+
const { values } = tsvToCells(text);
|
|
2015
|
+
const { row: startRow, col: startCol } = parseAddress(selection.activeCell);
|
|
2016
|
+
for (let r = 0; r < values.length; r++) {
|
|
2017
|
+
for (let c = 0; c < values[r].length; c++) {
|
|
2018
|
+
setCellValue(toAddress(startRow + r, startCol + c), values[r][c]);
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
});
|
|
2022
|
+
}, [selection, setCellValue]);
|
|
2023
|
+
const handleClearSelection = useCallback(() => {
|
|
2024
|
+
const range = selection.ranges[0];
|
|
2025
|
+
if (!range) return;
|
|
2026
|
+
const { start, end } = range;
|
|
2027
|
+
const s = parseAddress(start);
|
|
2028
|
+
const e = parseAddress(end);
|
|
2029
|
+
const minR = Math.min(s.row, e.row), maxR = Math.max(s.row, e.row);
|
|
2030
|
+
const minC = Math.min(s.col, e.col), maxC = Math.max(s.col, e.col);
|
|
2031
|
+
for (let r = minR; r <= maxR; r++) {
|
|
2032
|
+
for (let c = minC; c <= maxC; c++) {
|
|
2033
|
+
setCellValue(toAddress(r, c), "");
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
}, [selection, setCellValue]);
|
|
2037
|
+
return /* @__PURE__ */ jsxs(ContextMenu, { children: [
|
|
2038
|
+
/* @__PURE__ */ jsx(ContextMenu.Trigger, { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxs(
|
|
2039
|
+
"div",
|
|
2040
|
+
{
|
|
2041
|
+
ref: containerRef,
|
|
2042
|
+
"data-fancy-sheets-grid": "",
|
|
2043
|
+
className: cn("relative h-full overflow-auto bg-white focus:outline-none dark:bg-zinc-900", className),
|
|
2044
|
+
tabIndex: 0,
|
|
2045
|
+
onKeyDown: handleKeyDown,
|
|
2046
|
+
children: [
|
|
2047
|
+
/* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10", children: /* @__PURE__ */ jsx(ColumnHeaders, {}) }),
|
|
2048
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
2049
|
+
Array.from({ length: rowCount }, (_, rowIdx) => {
|
|
2050
|
+
const isFrozenRow = rowIdx < activeSheet.frozenRows;
|
|
2051
|
+
return /* @__PURE__ */ jsxs(
|
|
2052
|
+
"div",
|
|
2053
|
+
{
|
|
2054
|
+
className: "flex",
|
|
2055
|
+
style: isFrozenRow ? {
|
|
2056
|
+
position: "sticky",
|
|
2057
|
+
top: rowHeight + rowIdx * rowHeight,
|
|
2058
|
+
zIndex: 8
|
|
2059
|
+
} : void 0,
|
|
2060
|
+
children: [
|
|
2061
|
+
/* @__PURE__ */ jsx("div", { className: "sticky left-0 z-[5]", children: /* @__PURE__ */ jsx(RowHeader, { rowIndex: rowIdx }) }),
|
|
2062
|
+
Array.from({ length: columnCount }, (_2, colIdx) => {
|
|
2063
|
+
const addr = toAddress(rowIdx, colIdx);
|
|
2064
|
+
const isFrozenCol = colIdx < activeSheet.frozenCols;
|
|
2065
|
+
return /* @__PURE__ */ jsx(
|
|
2066
|
+
"div",
|
|
2067
|
+
{
|
|
2068
|
+
style: isFrozenCol ? {
|
|
2069
|
+
position: "sticky",
|
|
2070
|
+
left: 48 + Array.from({ length: colIdx }, (_3, c) => getColumnWidth(c)).reduce((a, b) => a + b, 0),
|
|
2071
|
+
zIndex: isFrozenRow ? 9 : 6
|
|
2072
|
+
} : void 0,
|
|
2073
|
+
children: /* @__PURE__ */ jsx(Cell, { address: addr, row: rowIdx, col: colIdx })
|
|
2074
|
+
},
|
|
2075
|
+
addr
|
|
2076
|
+
);
|
|
2077
|
+
})
|
|
2078
|
+
]
|
|
2079
|
+
},
|
|
2080
|
+
rowIdx
|
|
2081
|
+
);
|
|
2082
|
+
}),
|
|
2083
|
+
/* @__PURE__ */ jsx(SelectionOverlay, {}),
|
|
2084
|
+
editorPosition && /* @__PURE__ */ jsx(
|
|
2003
2085
|
"div",
|
|
2004
2086
|
{
|
|
2005
|
-
className: "
|
|
2006
|
-
style:
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
})
|
|
2031
|
-
]
|
|
2032
|
-
},
|
|
2033
|
-
rowIdx
|
|
2034
|
-
);
|
|
2035
|
-
}),
|
|
2036
|
-
/* @__PURE__ */ jsx(SelectionOverlay, {}),
|
|
2037
|
-
editorPosition && /* @__PURE__ */ jsx(
|
|
2038
|
-
"div",
|
|
2039
|
-
{
|
|
2040
|
-
className: "absolute z-20",
|
|
2041
|
-
style: { left: editorPosition.left, top: editorPosition.top },
|
|
2042
|
-
children: /* @__PURE__ */ jsx(CellEditor, {})
|
|
2043
|
-
}
|
|
2044
|
-
)
|
|
2045
|
-
] })
|
|
2046
|
-
]
|
|
2047
|
-
}
|
|
2048
|
-
);
|
|
2087
|
+
className: "absolute z-20",
|
|
2088
|
+
style: { left: editorPosition.left, top: editorPosition.top },
|
|
2089
|
+
children: /* @__PURE__ */ jsx(CellEditor, {})
|
|
2090
|
+
}
|
|
2091
|
+
)
|
|
2092
|
+
] })
|
|
2093
|
+
]
|
|
2094
|
+
}
|
|
2095
|
+
) }),
|
|
2096
|
+
/* @__PURE__ */ jsxs(ContextMenu.Content, { children: [
|
|
2097
|
+
/* @__PURE__ */ jsx(ContextMenu.Item, { onClick: handleCopy, children: "Copy" }),
|
|
2098
|
+
/* @__PURE__ */ jsx(ContextMenu.Item, { onClick: handlePaste, disabled: readOnly, children: "Paste" }),
|
|
2099
|
+
/* @__PURE__ */ jsx(ContextMenu.Separator, {}),
|
|
2100
|
+
/* @__PURE__ */ jsx(ContextMenu.Item, { onClick: handleClearSelection, disabled: readOnly, children: "Clear cells" }),
|
|
2101
|
+
/* @__PURE__ */ jsx(ContextMenu.Separator, {}),
|
|
2102
|
+
/* @__PURE__ */ jsx(ContextMenu.Item, { onClick: () => {
|
|
2103
|
+
const row = parseAddress(selection.activeCell).row;
|
|
2104
|
+
setFrozenRows(activeSheet.frozenRows > 0 ? 0 : row);
|
|
2105
|
+
}, disabled: readOnly, children: activeSheet.frozenRows > 0 ? "Unfreeze rows" : "Freeze rows above" }),
|
|
2106
|
+
/* @__PURE__ */ jsx(ContextMenu.Item, { onClick: () => {
|
|
2107
|
+
const col = parseAddress(selection.activeCell).col;
|
|
2108
|
+
setFrozenCols(activeSheet.frozenCols > 0 ? 0 : col);
|
|
2109
|
+
}, disabled: readOnly, children: activeSheet.frozenCols > 0 ? "Unfreeze columns" : "Freeze columns left" })
|
|
2110
|
+
] })
|
|
2111
|
+
] });
|
|
2049
2112
|
}
|
|
2050
2113
|
SpreadsheetGrid.displayName = "SpreadsheetGrid";
|
|
2051
2114
|
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";
|