@particle-academy/fancy-sheets 0.4.1 → 0.4.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.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
@@ -1599,7 +1599,7 @@ function ColumnResizeHandle({ colIndex }) {
1599
1599
  }
1600
1600
  ColumnResizeHandle.displayName = "ColumnResizeHandle";
1601
1601
  function ColumnHeaders() {
1602
- const { columnCount, rowCount, rowHeight, getColumnWidth, selection, selectRange, _isDragging } = useSpreadsheet();
1602
+ const { columnCount, rowCount, rowHeight, getColumnWidth, selection, selectRange, _isDragging, isCellSelected } = useSpreadsheet();
1603
1603
  const handleColumnMouseDown = useCallback(
1604
1604
  (colIdx, e) => {
1605
1605
  if (e.button !== 0) return;
@@ -1643,28 +1643,35 @@ function ColumnHeaders() {
1643
1643
  style: { width: 48, minWidth: 48 }
1644
1644
  }
1645
1645
  ),
1646
- Array.from({ length: columnCount }, (_, i) => /* @__PURE__ */ jsxs(
1647
- "div",
1648
- {
1649
- 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",
1650
- style: { width: getColumnWidth(i), minWidth: getColumnWidth(i) },
1651
- onMouseDown: (e) => handleColumnMouseDown(i, e),
1652
- onMouseEnter: () => handleColumnMouseEnter(i),
1653
- onMouseUp: handleMouseUp,
1654
- children: [
1655
- columnToLetter(i),
1656
- /* @__PURE__ */ jsx(ColumnResizeHandle, { colIndex: i })
1657
- ]
1658
- },
1659
- i
1660
- ))
1646
+ Array.from({ length: columnCount }, (_, i) => {
1647
+ const isColSelected = isCellSelected(toAddress(0, i));
1648
+ return /* @__PURE__ */ jsxs(
1649
+ "div",
1650
+ {
1651
+ className: cn(
1652
+ "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",
1653
+ isColSelected ? "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300" : "text-zinc-500 dark:text-zinc-400"
1654
+ ),
1655
+ style: { width: getColumnWidth(i), minWidth: getColumnWidth(i) },
1656
+ onMouseDown: (e) => handleColumnMouseDown(i, e),
1657
+ onMouseEnter: () => handleColumnMouseEnter(i),
1658
+ onMouseUp: handleMouseUp,
1659
+ children: [
1660
+ columnToLetter(i),
1661
+ /* @__PURE__ */ jsx(ColumnResizeHandle, { colIndex: i })
1662
+ ]
1663
+ },
1664
+ i
1665
+ );
1666
+ })
1661
1667
  ]
1662
1668
  }
1663
1669
  );
1664
1670
  }
1665
1671
  ColumnHeaders.displayName = "ColumnHeaders";
1666
1672
  function RowHeader({ rowIndex }) {
1667
- const { rowHeight, columnCount, selection, selectRange, _isDragging } = useSpreadsheet();
1673
+ const { rowHeight, columnCount, selection, selectRange, _isDragging, isCellSelected } = useSpreadsheet();
1674
+ const isRowSelected = isCellSelected(toAddress(rowIndex, 0));
1668
1675
  const handleMouseDown = useCallback(
1669
1676
  (e) => {
1670
1677
  if (e.button !== 0) return;
@@ -1695,7 +1702,10 @@ function RowHeader({ rowIndex }) {
1695
1702
  "div",
1696
1703
  {
1697
1704
  "data-fancy-sheets-row-header": "",
1698
- 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",
1705
+ className: cn(
1706
+ "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",
1707
+ 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"
1708
+ ),
1699
1709
  style: { width: 48, minWidth: 48, height: rowHeight },
1700
1710
  onMouseDown: handleMouseDown,
1701
1711
  onMouseEnter: handleMouseEnter,
@@ -1724,21 +1734,24 @@ function serialToDateTimeStr(serial) {
1724
1734
  }
1725
1735
  function isDateFormula(formula) {
1726
1736
  if (!formula) return false;
1727
- const f = formula.toUpperCase();
1728
- return /^(TODAY|NOW|DATE|EDATE)\b/.test(f) || /\b(TODAY|NOW|DATE|EDATE)\s*\(/.test(f);
1737
+ const f = formula.trim().toUpperCase();
1738
+ return /^(TODAY|NOW|DATE|EDATE)\s*\(/.test(f);
1729
1739
  }
1730
1740
  function formatCellValue(val, cell) {
1731
1741
  if (val === null || val === void 0) return "";
1732
1742
  const fmt = cell?.format?.displayFormat;
1733
1743
  if (typeof val === "number") {
1744
+ const dec = cell?.format?.decimals;
1734
1745
  if (fmt === "date") return serialToDateStr(val);
1735
1746
  if (fmt === "datetime") return serialToDateTimeStr(val);
1736
- if (fmt === "percentage") return (val * 100).toFixed(1) + "%";
1737
- if (fmt === "currency") return "$" + val.toFixed(2);
1747
+ if (fmt === "percentage") return (val * 100).toFixed(dec ?? 1) + "%";
1748
+ if (fmt === "currency") return "$" + val.toFixed(dec ?? 2);
1749
+ if (fmt === "number" && dec !== void 0) return val.toFixed(dec);
1738
1750
  if (fmt === "auto" || !fmt) {
1739
1751
  if (cell?.formula && isDateFormula(cell.formula)) {
1740
1752
  return val % 1 === 0 ? serialToDateStr(val) : serialToDateTimeStr(val);
1741
1753
  }
1754
+ if (dec !== void 0) return val.toFixed(dec);
1742
1755
  }
1743
1756
  }
1744
1757
  if (typeof val === "boolean") return val ? "TRUE" : "FALSE";
@@ -2182,6 +2195,8 @@ function DefaultToolbar() {
2182
2195
  const isBold = cell?.format?.bold ?? false;
2183
2196
  const isItalic = cell?.format?.italic ?? false;
2184
2197
  const textAlign = cell?.format?.textAlign ?? "left";
2198
+ const displayFormat = cell?.format?.displayFormat ?? "auto";
2199
+ const decimals = cell?.format?.decimals;
2185
2200
  const selectedAddresses = [selection.activeCell];
2186
2201
  const handleFormulaBarChange = (e) => {
2187
2202
  if (editingCell) {
@@ -2293,6 +2308,52 @@ function DefaultToolbar() {
2293
2308
  /* @__PURE__ */ jsx("line", { x1: "19", y1: "3", x2: "19", y2: "21", strokeDasharray: "3 3" })
2294
2309
  ] })
2295
2310
  }
2311
+ ),
2312
+ /* @__PURE__ */ jsx("div", { className: "mx-1 h-4 w-px bg-zinc-200 dark:bg-zinc-700" }),
2313
+ /* @__PURE__ */ jsxs(
2314
+ "select",
2315
+ {
2316
+ 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",
2317
+ value: displayFormat,
2318
+ onChange: (e) => setCellFormat(selectedAddresses, { displayFormat: e.target.value }),
2319
+ disabled: readOnly,
2320
+ title: "Cell format",
2321
+ children: [
2322
+ /* @__PURE__ */ jsx("option", { value: "auto", children: "Auto" }),
2323
+ /* @__PURE__ */ jsx("option", { value: "text", children: "Text" }),
2324
+ /* @__PURE__ */ jsx("option", { value: "number", children: "Number" }),
2325
+ /* @__PURE__ */ jsx("option", { value: "currency", children: "Currency ($)" }),
2326
+ /* @__PURE__ */ jsx("option", { value: "percentage", children: "Percentage (%)" }),
2327
+ /* @__PURE__ */ jsx("option", { value: "date", children: "Date" }),
2328
+ /* @__PURE__ */ jsx("option", { value: "datetime", children: "Date & Time" })
2329
+ ]
2330
+ }
2331
+ ),
2332
+ /* @__PURE__ */ jsxs(
2333
+ "button",
2334
+ {
2335
+ className: btnClass,
2336
+ onClick: () => setCellFormat(selectedAddresses, { decimals: Math.max(0, (decimals ?? 0) - 1) }),
2337
+ disabled: readOnly || (decimals ?? 0) <= 0,
2338
+ title: "Decrease decimal places",
2339
+ children: [
2340
+ /* @__PURE__ */ jsx("span", { className: "text-[10px]", children: ".0" }),
2341
+ /* @__PURE__ */ jsx("span", { className: "text-[8px]", children: "\u2190" })
2342
+ ]
2343
+ }
2344
+ ),
2345
+ /* @__PURE__ */ jsxs(
2346
+ "button",
2347
+ {
2348
+ className: btnClass,
2349
+ onClick: () => setCellFormat(selectedAddresses, { decimals: (decimals ?? 0) + 1 }),
2350
+ disabled: readOnly,
2351
+ title: "Increase decimal places",
2352
+ children: [
2353
+ /* @__PURE__ */ jsx("span", { className: "text-[10px]", children: ".00" }),
2354
+ /* @__PURE__ */ jsx("span", { className: "text-[8px]", children: "\u2192" })
2355
+ ]
2356
+ }
2296
2357
  )
2297
2358
  ] }),
2298
2359
  /* @__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: [