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