@particle-academy/fancy-sheets 0.4.1 → 0.4.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 +97 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +97 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -16,6 +16,8 @@ interface CellFormat {
|
|
|
16
16
|
textAlign?: TextAlign;
|
|
17
17
|
/** Display format — controls how the value is rendered */
|
|
18
18
|
displayFormat?: CellDisplayFormat;
|
|
19
|
+
/** Number of decimal places to display (for number/currency/percentage) */
|
|
20
|
+
decimals?: number;
|
|
19
21
|
}
|
|
20
22
|
/** A single cell's complete data */
|
|
21
23
|
interface CellData {
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ interface CellFormat {
|
|
|
16
16
|
textAlign?: TextAlign;
|
|
17
17
|
/** Display format — controls how the value is rendered */
|
|
18
18
|
displayFormat?: CellDisplayFormat;
|
|
19
|
+
/** Number of decimal places to display (for number/currency/percentage) */
|
|
20
|
+
decimals?: number;
|
|
19
21
|
}
|
|
20
22
|
/** A single cell's complete data */
|
|
21
23
|
interface CellData {
|
package/dist/index.js
CHANGED
|
@@ -1235,10 +1235,15 @@ function getRecalculationOrder(graph) {
|
|
|
1235
1235
|
|
|
1236
1236
|
// src/hooks/use-spreadsheet-store.ts
|
|
1237
1237
|
function recalculateWorkbook(workbook) {
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
}
|
|
1238
|
+
const recalculated = [];
|
|
1239
|
+
for (const sheet of workbook.sheets) {
|
|
1240
|
+
recalculated.push(recalculateSheet(sheet, recalculated));
|
|
1241
|
+
}
|
|
1242
|
+
const finalSheets = [];
|
|
1243
|
+
for (const sheet of recalculated) {
|
|
1244
|
+
finalSheets.push(recalculateSheet(sheet, recalculated));
|
|
1245
|
+
}
|
|
1246
|
+
return { ...workbook, sheets: finalSheets };
|
|
1242
1247
|
}
|
|
1243
1248
|
function createInitialState(data) {
|
|
1244
1249
|
const workbook = data ?? createEmptyWorkbook();
|
|
@@ -1338,10 +1343,11 @@ function reducer(state, action) {
|
|
|
1338
1343
|
const sheet = getActiveSheet(state);
|
|
1339
1344
|
const existing = sheet.cells[action.address];
|
|
1340
1345
|
if (existing?.format) cellData.format = existing.format;
|
|
1341
|
-
const
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
});
|
|
1346
|
+
const updatedWorkbook = updateActiveSheet(state, (s) => ({
|
|
1347
|
+
...s,
|
|
1348
|
+
cells: { ...s.cells, [action.address]: cellData }
|
|
1349
|
+
}));
|
|
1350
|
+
const workbook = recalculateWorkbook(updatedWorkbook);
|
|
1345
1351
|
return { ...state, workbook, ...history };
|
|
1346
1352
|
}
|
|
1347
1353
|
case "SET_CELL_FORMAT": {
|
|
@@ -1599,7 +1605,7 @@ function ColumnResizeHandle({ colIndex }) {
|
|
|
1599
1605
|
}
|
|
1600
1606
|
ColumnResizeHandle.displayName = "ColumnResizeHandle";
|
|
1601
1607
|
function ColumnHeaders() {
|
|
1602
|
-
const { columnCount, rowCount, rowHeight, getColumnWidth, selection, selectRange, _isDragging } = useSpreadsheet();
|
|
1608
|
+
const { columnCount, rowCount, rowHeight, getColumnWidth, selection, selectRange, _isDragging, isCellSelected } = useSpreadsheet();
|
|
1603
1609
|
const handleColumnMouseDown = useCallback(
|
|
1604
1610
|
(colIdx, e) => {
|
|
1605
1611
|
if (e.button !== 0) return;
|
|
@@ -1643,28 +1649,35 @@ function ColumnHeaders() {
|
|
|
1643
1649
|
style: { width: 48, minWidth: 48 }
|
|
1644
1650
|
}
|
|
1645
1651
|
),
|
|
1646
|
-
Array.from({ length: columnCount }, (_, i) =>
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1652
|
+
Array.from({ length: columnCount }, (_, i) => {
|
|
1653
|
+
const isColSelected = isCellSelected(toAddress(0, i));
|
|
1654
|
+
return /* @__PURE__ */ jsxs(
|
|
1655
|
+
"div",
|
|
1656
|
+
{
|
|
1657
|
+
className: cn(
|
|
1658
|
+
"relative flex shrink-0 cursor-pointer items-center justify-center border-r border-zinc-300 text-[11px] font-medium select-none hover:bg-zinc-200 dark:border-zinc-600 dark:hover:bg-zinc-700",
|
|
1659
|
+
isColSelected ? "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300" : "text-zinc-500 dark:text-zinc-400"
|
|
1660
|
+
),
|
|
1661
|
+
style: { width: getColumnWidth(i), minWidth: getColumnWidth(i) },
|
|
1662
|
+
onMouseDown: (e) => handleColumnMouseDown(i, e),
|
|
1663
|
+
onMouseEnter: () => handleColumnMouseEnter(i),
|
|
1664
|
+
onMouseUp: handleMouseUp,
|
|
1665
|
+
children: [
|
|
1666
|
+
columnToLetter(i),
|
|
1667
|
+
/* @__PURE__ */ jsx(ColumnResizeHandle, { colIndex: i })
|
|
1668
|
+
]
|
|
1669
|
+
},
|
|
1670
|
+
i
|
|
1671
|
+
);
|
|
1672
|
+
})
|
|
1661
1673
|
]
|
|
1662
1674
|
}
|
|
1663
1675
|
);
|
|
1664
1676
|
}
|
|
1665
1677
|
ColumnHeaders.displayName = "ColumnHeaders";
|
|
1666
1678
|
function RowHeader({ rowIndex }) {
|
|
1667
|
-
const { rowHeight, columnCount, selection, selectRange, _isDragging } = useSpreadsheet();
|
|
1679
|
+
const { rowHeight, columnCount, selection, selectRange, _isDragging, isCellSelected } = useSpreadsheet();
|
|
1680
|
+
const isRowSelected = isCellSelected(toAddress(rowIndex, 0));
|
|
1668
1681
|
const handleMouseDown = useCallback(
|
|
1669
1682
|
(e) => {
|
|
1670
1683
|
if (e.button !== 0) return;
|
|
@@ -1695,7 +1708,10 @@ function RowHeader({ rowIndex }) {
|
|
|
1695
1708
|
"div",
|
|
1696
1709
|
{
|
|
1697
1710
|
"data-fancy-sheets-row-header": "",
|
|
1698
|
-
className:
|
|
1711
|
+
className: cn(
|
|
1712
|
+
"flex shrink-0 cursor-pointer items-center justify-center border-r border-b border-zinc-300 text-[11px] font-medium select-none hover:bg-zinc-200 dark:border-zinc-600 dark:hover:bg-zinc-700",
|
|
1713
|
+
isRowSelected ? "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300" : "bg-zinc-100 text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400"
|
|
1714
|
+
),
|
|
1699
1715
|
style: { width: 48, minWidth: 48, height: rowHeight },
|
|
1700
1716
|
onMouseDown: handleMouseDown,
|
|
1701
1717
|
onMouseEnter: handleMouseEnter,
|
|
@@ -1724,21 +1740,24 @@ function serialToDateTimeStr(serial) {
|
|
|
1724
1740
|
}
|
|
1725
1741
|
function isDateFormula(formula) {
|
|
1726
1742
|
if (!formula) return false;
|
|
1727
|
-
const f = formula.toUpperCase();
|
|
1728
|
-
return /^(TODAY|NOW|DATE|EDATE)\
|
|
1743
|
+
const f = formula.trim().toUpperCase();
|
|
1744
|
+
return /^(TODAY|NOW|DATE|EDATE)\s*\(/.test(f);
|
|
1729
1745
|
}
|
|
1730
1746
|
function formatCellValue(val, cell) {
|
|
1731
1747
|
if (val === null || val === void 0) return "";
|
|
1732
1748
|
const fmt = cell?.format?.displayFormat;
|
|
1733
1749
|
if (typeof val === "number") {
|
|
1750
|
+
const dec = cell?.format?.decimals;
|
|
1734
1751
|
if (fmt === "date") return serialToDateStr(val);
|
|
1735
1752
|
if (fmt === "datetime") return serialToDateTimeStr(val);
|
|
1736
|
-
if (fmt === "percentage") return (val * 100).toFixed(1) + "%";
|
|
1737
|
-
if (fmt === "currency") return "$" + val.toFixed(2);
|
|
1753
|
+
if (fmt === "percentage") return (val * 100).toFixed(dec ?? 1) + "%";
|
|
1754
|
+
if (fmt === "currency") return "$" + val.toFixed(dec ?? 2);
|
|
1755
|
+
if (fmt === "number" && dec !== void 0) return val.toFixed(dec);
|
|
1738
1756
|
if (fmt === "auto" || !fmt) {
|
|
1739
1757
|
if (cell?.formula && isDateFormula(cell.formula)) {
|
|
1740
1758
|
return val % 1 === 0 ? serialToDateStr(val) : serialToDateTimeStr(val);
|
|
1741
1759
|
}
|
|
1760
|
+
if (dec !== void 0) return val.toFixed(dec);
|
|
1742
1761
|
}
|
|
1743
1762
|
}
|
|
1744
1763
|
if (typeof val === "boolean") return val ? "TRUE" : "FALSE";
|
|
@@ -2182,6 +2201,8 @@ function DefaultToolbar() {
|
|
|
2182
2201
|
const isBold = cell?.format?.bold ?? false;
|
|
2183
2202
|
const isItalic = cell?.format?.italic ?? false;
|
|
2184
2203
|
const textAlign = cell?.format?.textAlign ?? "left";
|
|
2204
|
+
const displayFormat = cell?.format?.displayFormat ?? "auto";
|
|
2205
|
+
const decimals = cell?.format?.decimals;
|
|
2185
2206
|
const selectedAddresses = [selection.activeCell];
|
|
2186
2207
|
const handleFormulaBarChange = (e) => {
|
|
2187
2208
|
if (editingCell) {
|
|
@@ -2293,6 +2314,52 @@ function DefaultToolbar() {
|
|
|
2293
2314
|
/* @__PURE__ */ jsx("line", { x1: "19", y1: "3", x2: "19", y2: "21", strokeDasharray: "3 3" })
|
|
2294
2315
|
] })
|
|
2295
2316
|
}
|
|
2317
|
+
),
|
|
2318
|
+
/* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-zinc-200 dark:bg-zinc-700" }),
|
|
2319
|
+
/* @__PURE__ */ jsxs(
|
|
2320
|
+
"select",
|
|
2321
|
+
{
|
|
2322
|
+
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",
|
|
2323
|
+
value: displayFormat,
|
|
2324
|
+
onChange: (e) => setCellFormat(selectedAddresses, { displayFormat: e.target.value }),
|
|
2325
|
+
disabled: readOnly,
|
|
2326
|
+
title: "Cell format",
|
|
2327
|
+
children: [
|
|
2328
|
+
/* @__PURE__ */ jsx("option", { value: "auto", children: "Auto" }),
|
|
2329
|
+
/* @__PURE__ */ jsx("option", { value: "text", children: "Text" }),
|
|
2330
|
+
/* @__PURE__ */ jsx("option", { value: "number", children: "Number" }),
|
|
2331
|
+
/* @__PURE__ */ jsx("option", { value: "currency", children: "Currency ($)" }),
|
|
2332
|
+
/* @__PURE__ */ jsx("option", { value: "percentage", children: "Percentage (%)" }),
|
|
2333
|
+
/* @__PURE__ */ jsx("option", { value: "date", children: "Date" }),
|
|
2334
|
+
/* @__PURE__ */ jsx("option", { value: "datetime", children: "Date & Time" })
|
|
2335
|
+
]
|
|
2336
|
+
}
|
|
2337
|
+
),
|
|
2338
|
+
/* @__PURE__ */ jsxs(
|
|
2339
|
+
"button",
|
|
2340
|
+
{
|
|
2341
|
+
className: btnClass,
|
|
2342
|
+
onClick: () => setCellFormat(selectedAddresses, { decimals: Math.max(0, (decimals ?? 0) - 1) }),
|
|
2343
|
+
disabled: readOnly || (decimals ?? 0) <= 0,
|
|
2344
|
+
title: "Decrease decimal places",
|
|
2345
|
+
children: [
|
|
2346
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px]", children: ".0" }),
|
|
2347
|
+
/* @__PURE__ */ jsx("span", { className: "text-[8px]", children: "\u2190" })
|
|
2348
|
+
]
|
|
2349
|
+
}
|
|
2350
|
+
),
|
|
2351
|
+
/* @__PURE__ */ jsxs(
|
|
2352
|
+
"button",
|
|
2353
|
+
{
|
|
2354
|
+
className: btnClass,
|
|
2355
|
+
onClick: () => setCellFormat(selectedAddresses, { decimals: (decimals ?? 0) + 1 }),
|
|
2356
|
+
disabled: readOnly,
|
|
2357
|
+
title: "Increase decimal places",
|
|
2358
|
+
children: [
|
|
2359
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px]", children: ".00" }),
|
|
2360
|
+
/* @__PURE__ */ jsx("span", { className: "text-[8px]", children: "\u2192" })
|
|
2361
|
+
]
|
|
2362
|
+
}
|
|
2296
2363
|
)
|
|
2297
2364
|
] }),
|
|
2298
2365
|
/* @__PURE__ */ 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: [
|