@particle-academy/fancy-sheets 0.2.0 → 0.3.1

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
@@ -123,12 +123,41 @@ function lexFormula(input) {
123
123
  if (ch >= "A" && ch <= "Z" || ch >= "a" && ch <= "z" || ch === "_") {
124
124
  const pos = i;
125
125
  i++;
126
- while (i < len && (input[i] >= "A" && input[i] <= "Z" || input[i] >= "a" && input[i] <= "z" || input[i] >= "0" && input[i] <= "9" || input[i] === "_")) i++;
127
- const word = input.slice(pos, i);
126
+ while (i < len && (input[i] >= "A" && input[i] <= "Z" || input[i] >= "a" && input[i] <= "z" || input[i] >= "0" && input[i] <= "9" || input[i] === "_" || input[i] === " ")) {
127
+ if (input[i] === " ") {
128
+ let lookAhead = i + 1;
129
+ while (lookAhead < len && input[lookAhead] === " ") lookAhead++;
130
+ if (lookAhead < len && (input[lookAhead] >= "A" && input[lookAhead] <= "Z" || input[lookAhead] >= "a" && input[lookAhead] <= "z" || input[lookAhead] >= "0" && input[lookAhead] <= "9" || input[lookAhead] === "!")) {
131
+ i++;
132
+ continue;
133
+ }
134
+ break;
135
+ }
136
+ i++;
137
+ }
138
+ let word = input.slice(pos, i).trimEnd();
139
+ i = pos + word.length;
128
140
  if (word.toUpperCase() === "TRUE" || word.toUpperCase() === "FALSE") {
129
141
  tokens.push({ type: "boolean", value: word.toUpperCase(), position: pos });
130
142
  continue;
131
143
  }
144
+ if (i < len && input[i] === "!") {
145
+ const sheetName = word;
146
+ i++;
147
+ const refStart = i;
148
+ while (i < len && (input[i] >= "A" && input[i] <= "Z" || input[i] >= "a" && input[i] <= "z" || input[i] >= "0" && input[i] <= "9")) i++;
149
+ const ref1 = input.slice(refStart, i);
150
+ if (i < len && input[i] === ":") {
151
+ i++;
152
+ const ref2Start = i;
153
+ while (i < len && (input[i] >= "A" && input[i] <= "Z" || input[i] >= "a" && input[i] <= "z" || input[i] >= "0" && input[i] <= "9")) i++;
154
+ const ref2 = input.slice(ref2Start, i);
155
+ tokens.push({ type: "sheetRangeRef", value: sheetName + "!" + ref1 + ":" + ref2, position: pos });
156
+ } else {
157
+ tokens.push({ type: "sheetCellRef", value: sheetName + "!" + ref1.toUpperCase(), position: pos });
158
+ }
159
+ continue;
160
+ }
132
161
  if (i < len && input[i] === ":") {
133
162
  const colonPos = i;
134
163
  i++;
@@ -272,6 +301,19 @@ function parseFormula(tokens) {
272
301
  const parts = t.value.split(":");
273
302
  return { type: "rangeRef", start: parts[0], end: parts[1] };
274
303
  }
304
+ if (t.type === "sheetCellRef") {
305
+ advance();
306
+ const bangIdx = t.value.indexOf("!");
307
+ return { type: "sheetCellRef", sheet: t.value.slice(0, bangIdx), address: t.value.slice(bangIdx + 1).toUpperCase() };
308
+ }
309
+ if (t.type === "sheetRangeRef") {
310
+ advance();
311
+ const bangIdx = t.value.indexOf("!");
312
+ const sheetName = t.value.slice(0, bangIdx);
313
+ const rangePart = t.value.slice(bangIdx + 1);
314
+ const parts = rangePart.split(":");
315
+ return { type: "sheetRangeRef", sheet: sheetName, start: parts[0].toUpperCase(), end: parts[1].toUpperCase() };
316
+ }
275
317
  if (t.type === "function") {
276
318
  const name = advance().value;
277
319
  expect("paren", "(");
@@ -1005,7 +1047,7 @@ registerFunction("TYPE", (args) => {
1005
1047
  });
1006
1048
 
1007
1049
  // src/engine/formula/evaluator.ts
1008
- function evaluateAST(node, getCellValue, getRangeValues) {
1050
+ function evaluateAST(node, getCellValue, getRangeValues, ctx) {
1009
1051
  switch (node.type) {
1010
1052
  case "number":
1011
1053
  return node.value;
@@ -1015,9 +1057,19 @@ function evaluateAST(node, getCellValue, getRangeValues) {
1015
1057
  return node.value;
1016
1058
  case "cellRef":
1017
1059
  return getCellValue(node.address);
1018
- case "rangeRef":
1060
+ case "rangeRef": {
1019
1061
  const vals = getRangeValues(node.start, node.end);
1020
1062
  return vals[0] ?? null;
1063
+ }
1064
+ case "sheetCellRef": {
1065
+ if (!ctx?.getSheetCellValue) return "#REF!";
1066
+ return ctx.getSheetCellValue(node.sheet, node.address);
1067
+ }
1068
+ case "sheetRangeRef": {
1069
+ if (!ctx?.getSheetRangeValues) return "#REF!";
1070
+ const vals = ctx.getSheetRangeValues(node.sheet, node.start, node.end);
1071
+ return vals[0] ?? null;
1072
+ }
1021
1073
  case "functionCall": {
1022
1074
  const entry = getFunction(node.name);
1023
1075
  if (!entry) return `#NAME?`;
@@ -1025,7 +1077,11 @@ function evaluateAST(node, getCellValue, getRangeValues) {
1025
1077
  if (arg.type === "rangeRef") {
1026
1078
  return getRangeValues(arg.start, arg.end);
1027
1079
  }
1028
- const val = evaluateAST(arg, getCellValue, getRangeValues);
1080
+ if (arg.type === "sheetRangeRef") {
1081
+ if (!ctx?.getSheetRangeValues) return ["#REF!"];
1082
+ return ctx.getSheetRangeValues(arg.sheet, arg.start, arg.end);
1083
+ }
1084
+ const val = evaluateAST(arg, getCellValue, getRangeValues, ctx);
1029
1085
  return [val];
1030
1086
  });
1031
1087
  try {
@@ -1035,8 +1091,8 @@ function evaluateAST(node, getCellValue, getRangeValues) {
1035
1091
  }
1036
1092
  }
1037
1093
  case "binaryOp": {
1038
- const left = evaluateAST(node.left, getCellValue, getRangeValues);
1039
- const right = evaluateAST(node.right, getCellValue, getRangeValues);
1094
+ const left = evaluateAST(node.left, getCellValue, getRangeValues, ctx);
1095
+ const right = evaluateAST(node.right, getCellValue, getRangeValues, ctx);
1040
1096
  const lNum = typeof left === "number" ? left : Number(left);
1041
1097
  const rNum = typeof right === "number" ? right : Number(right);
1042
1098
  switch (node.operator) {
@@ -1069,7 +1125,7 @@ function evaluateAST(node, getCellValue, getRangeValues) {
1069
1125
  }
1070
1126
  }
1071
1127
  case "unaryOp": {
1072
- const operand = evaluateAST(node.operand, getCellValue, getRangeValues);
1128
+ const operand = evaluateAST(node.operand, getCellValue, getRangeValues, ctx);
1073
1129
  const num = typeof operand === "number" ? operand : Number(operand);
1074
1130
  if (isNaN(num)) return "#VALUE!";
1075
1131
  return node.operator === "-" ? -num : num;
@@ -1183,7 +1239,7 @@ function getRecalculationOrder(graph) {
1183
1239
  function recalculateWorkbook(workbook) {
1184
1240
  return {
1185
1241
  ...workbook,
1186
- sheets: workbook.sheets.map(recalculateSheet)
1242
+ sheets: workbook.sheets.map((s) => recalculateSheet(s, workbook.sheets))
1187
1243
  };
1188
1244
  }
1189
1245
  function createInitialState(data) {
@@ -1213,7 +1269,7 @@ function pushUndo(state) {
1213
1269
  if (stack.length > 50) stack.shift();
1214
1270
  return { undoStack: stack, redoStack: [] };
1215
1271
  }
1216
- function recalculateSheet(sheet) {
1272
+ function recalculateSheet(sheet, allSheets) {
1217
1273
  const graph = buildDependencyGraph(sheet.cells);
1218
1274
  if (graph.size === 0) return sheet;
1219
1275
  const circular = detectCircularRefs(graph);
@@ -1229,6 +1285,28 @@ function recalculateSheet(sheet) {
1229
1285
  const addresses = expandRange(startAddr, endAddr);
1230
1286
  return addresses.map(getCellValue);
1231
1287
  };
1288
+ const getSheetCellValue = (sheetName, addr) => {
1289
+ if (!allSheets) return "#REF!";
1290
+ const target = allSheets.find((s) => s.name === sheetName || s.id === sheetName);
1291
+ if (!target) return "#REF!";
1292
+ const c = target.cells[addr];
1293
+ if (!c) return null;
1294
+ if (c.formula && c.computedValue !== void 0) return c.computedValue;
1295
+ return c.value;
1296
+ };
1297
+ const getSheetRangeValues = (sheetName, startAddr, endAddr) => {
1298
+ if (!allSheets) return [];
1299
+ const target = allSheets.find((s) => s.name === sheetName || s.id === sheetName);
1300
+ if (!target) return [];
1301
+ const addresses = expandRange(startAddr, endAddr);
1302
+ return addresses.map((a) => {
1303
+ const c = target.cells[a];
1304
+ if (!c) return null;
1305
+ if (c.formula && c.computedValue !== void 0) return c.computedValue;
1306
+ return c.value;
1307
+ });
1308
+ };
1309
+ const ctx = { getSheetCellValue, getSheetRangeValues };
1232
1310
  for (const addr of order) {
1233
1311
  const cell = cells[addr];
1234
1312
  if (!cell?.formula) continue;
@@ -1239,7 +1317,7 @@ function recalculateSheet(sheet) {
1239
1317
  try {
1240
1318
  const tokens = lexFormula(cell.formula);
1241
1319
  const ast = parseFormula(tokens);
1242
- const result = evaluateAST(ast, getCellValue, getRangeValues);
1320
+ const result = evaluateAST(ast, getCellValue, getRangeValues, ctx);
1243
1321
  cells[addr] = { ...cell, computedValue: result };
1244
1322
  } catch {
1245
1323
  cells[addr] = { ...cell, computedValue: "#ERROR!" };
@@ -1264,7 +1342,7 @@ function reducer(state, action) {
1264
1342
  if (existing?.format) cellData.format = existing.format;
1265
1343
  const workbook = updateActiveSheet(state, (s) => {
1266
1344
  const updated = { ...s, cells: { ...s.cells, [action.address]: cellData } };
1267
- return recalculateSheet(updated);
1345
+ return recalculateSheet(updated, state.workbook.sheets);
1268
1346
  });
1269
1347
  return { ...state, workbook, ...history };
1270
1348
  }
@@ -1420,6 +1498,20 @@ function reducer(state, action) {
1420
1498
  selection: { activeCell: "A1", ranges: [{ start: "A1", end: "A1" }] },
1421
1499
  editingCell: null
1422
1500
  };
1501
+ case "SET_FROZEN_ROWS": {
1502
+ const workbook = updateActiveSheet(state, (s) => ({
1503
+ ...s,
1504
+ frozenRows: Math.max(0, action.count)
1505
+ }));
1506
+ return { ...state, workbook };
1507
+ }
1508
+ case "SET_FROZEN_COLS": {
1509
+ const workbook = updateActiveSheet(state, (s) => ({
1510
+ ...s,
1511
+ frozenCols: Math.max(0, action.count)
1512
+ }));
1513
+ return { ...state, workbook };
1514
+ }
1423
1515
  case "UNDO": {
1424
1516
  if (state.undoStack.length === 0) return state;
1425
1517
  const prev = state.undoStack[state.undoStack.length - 1];
@@ -1464,6 +1556,8 @@ function useSpreadsheetStore(initialData) {
1464
1556
  addSheet: () => dispatch({ type: "ADD_SHEET" }),
1465
1557
  renameSheet: (sheetId, name) => dispatch({ type: "RENAME_SHEET", sheetId, name }),
1466
1558
  deleteSheet: (sheetId) => dispatch({ type: "DELETE_SHEET", sheetId }),
1559
+ setFrozenRows: (count) => dispatch({ type: "SET_FROZEN_ROWS", count }),
1560
+ setFrozenCols: (count) => dispatch({ type: "SET_FROZEN_COLS", count }),
1467
1561
  setActiveSheet: (sheetId) => dispatch({ type: "SET_ACTIVE_SHEET", sheetId }),
1468
1562
  undo: () => dispatch({ type: "UNDO" }),
1469
1563
  redo: () => dispatch({ type: "REDO" }),
@@ -1507,7 +1601,15 @@ function ColumnResizeHandle({ colIndex }) {
1507
1601
  }
1508
1602
  ColumnResizeHandle.displayName = "ColumnResizeHandle";
1509
1603
  function ColumnHeaders() {
1510
- const { columnCount, rowHeight, getColumnWidth } = useSpreadsheet();
1604
+ const { columnCount, rowCount, rowHeight, getColumnWidth, selectRange } = useSpreadsheet();
1605
+ const handleColumnClick = react.useCallback(
1606
+ (colIdx) => {
1607
+ const start = toAddress(0, colIdx);
1608
+ const end = toAddress(rowCount - 1, colIdx);
1609
+ selectRange(start, end);
1610
+ },
1611
+ [rowCount, selectRange]
1612
+ );
1511
1613
  return /* @__PURE__ */ jsxRuntime.jsxs(
1512
1614
  "div",
1513
1615
  {
@@ -1525,8 +1627,9 @@ function ColumnHeaders() {
1525
1627
  Array.from({ length: columnCount }, (_, i) => /* @__PURE__ */ jsxRuntime.jsxs(
1526
1628
  "div",
1527
1629
  {
1528
- className: "relative flex shrink-0 items-center justify-center border-r border-zinc-300 text-[11px] font-medium text-zinc-500 select-none dark:border-zinc-600 dark:text-zinc-400",
1630
+ 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",
1529
1631
  style: { width: getColumnWidth(i), minWidth: getColumnWidth(i) },
1632
+ onClick: () => handleColumnClick(i),
1530
1633
  children: [
1531
1634
  columnToLetter(i),
1532
1635
  /* @__PURE__ */ jsxRuntime.jsx(ColumnResizeHandle, { colIndex: i })
@@ -1540,23 +1643,67 @@ function ColumnHeaders() {
1540
1643
  }
1541
1644
  ColumnHeaders.displayName = "ColumnHeaders";
1542
1645
  function RowHeader({ rowIndex }) {
1543
- const { rowHeight } = useSpreadsheet();
1646
+ const { rowHeight, columnCount, selectRange } = useSpreadsheet();
1647
+ const handleClick = react.useCallback(() => {
1648
+ const start = toAddress(rowIndex, 0);
1649
+ const end = toAddress(rowIndex, columnCount - 1);
1650
+ selectRange(start, end);
1651
+ }, [rowIndex, columnCount, selectRange]);
1544
1652
  return /* @__PURE__ */ jsxRuntime.jsx(
1545
1653
  "div",
1546
1654
  {
1547
1655
  "data-fancy-sheets-row-header": "",
1548
- className: "flex shrink-0 items-center justify-center border-r border-b border-zinc-300 bg-zinc-100 text-[11px] font-medium text-zinc-500 select-none dark:border-zinc-600 dark:bg-zinc-800 dark:text-zinc-400",
1656
+ 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",
1549
1657
  style: { width: 48, minWidth: 48, height: rowHeight },
1658
+ onClick: handleClick,
1550
1659
  children: rowIndex + 1
1551
1660
  }
1552
1661
  );
1553
1662
  }
1554
1663
  RowHeader.displayName = "RowHeader";
1664
+ var EXCEL_EPOCH2 = new Date(1899, 11, 30).getTime();
1665
+ function serialToDateStr(serial) {
1666
+ const d = new Date(EXCEL_EPOCH2 + Math.floor(serial) * 864e5);
1667
+ const y = d.getFullYear();
1668
+ const m = String(d.getMonth() + 1).padStart(2, "0");
1669
+ const day = String(d.getDate()).padStart(2, "0");
1670
+ return `${y}-${m}-${day}`;
1671
+ }
1672
+ function serialToDateTimeStr(serial) {
1673
+ const date = serialToDateStr(serial);
1674
+ const fraction = serial % 1;
1675
+ const totalSeconds = Math.round(fraction * 86400);
1676
+ const h = String(Math.floor(totalSeconds / 3600)).padStart(2, "0");
1677
+ const min = String(Math.floor(totalSeconds % 3600 / 60)).padStart(2, "0");
1678
+ const s = String(totalSeconds % 60).padStart(2, "0");
1679
+ return `${date} ${h}:${min}:${s}`;
1680
+ }
1681
+ function isDateFormula(formula) {
1682
+ if (!formula) return false;
1683
+ const f = formula.toUpperCase();
1684
+ return /^(TODAY|NOW|DATE|EDATE)\b/.test(f) || /\b(TODAY|NOW|DATE|EDATE)\s*\(/.test(f);
1685
+ }
1686
+ function formatCellValue(val, cell) {
1687
+ if (val === null || val === void 0) return "";
1688
+ const fmt = cell?.format?.displayFormat;
1689
+ if (typeof val === "number") {
1690
+ if (fmt === "date") return serialToDateStr(val);
1691
+ if (fmt === "datetime") return serialToDateTimeStr(val);
1692
+ if (fmt === "percentage") return (val * 100).toFixed(1) + "%";
1693
+ if (fmt === "currency") return "$" + val.toFixed(2);
1694
+ if (fmt === "auto" || !fmt) {
1695
+ if (cell?.formula && isDateFormula(cell.formula)) {
1696
+ return val % 1 === 0 ? serialToDateStr(val) : serialToDateTimeStr(val);
1697
+ }
1698
+ }
1699
+ }
1700
+ if (typeof val === "boolean") return val ? "TRUE" : "FALSE";
1701
+ return String(val);
1702
+ }
1555
1703
  function getCellDisplayValue2(cell) {
1556
1704
  if (!cell) return "";
1557
- if (cell.formula && cell.computedValue !== void 0) return String(cell.computedValue ?? "");
1558
- if (cell.value === null) return "";
1559
- return String(cell.value);
1705
+ const val = cell.formula && cell.computedValue !== void 0 ? cell.computedValue : cell.value;
1706
+ return formatCellValue(val, cell);
1560
1707
  }
1561
1708
  var Cell = react.memo(function Cell2({ address, row, col }) {
1562
1709
  const {
@@ -1852,13 +1999,42 @@ function SpreadsheetGrid({ className }) {
1852
1999
  children: [
1853
2000
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-10", children: /* @__PURE__ */ jsxRuntime.jsx(ColumnHeaders, {}) }),
1854
2001
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1855
- Array.from({ length: rowCount }, (_, rowIdx) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex", children: [
1856
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky left-0 z-[5]", children: /* @__PURE__ */ jsxRuntime.jsx(RowHeader, { rowIndex: rowIdx }) }),
1857
- Array.from({ length: columnCount }, (_2, colIdx) => {
1858
- const addr = toAddress(rowIdx, colIdx);
1859
- return /* @__PURE__ */ jsxRuntime.jsx(Cell, { address: addr, row: rowIdx, col: colIdx }, addr);
1860
- })
1861
- ] }, rowIdx)),
2002
+ Array.from({ length: rowCount }, (_, rowIdx) => {
2003
+ const isFrozenRow = rowIdx < activeSheet.frozenRows;
2004
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2005
+ "div",
2006
+ {
2007
+ className: "flex",
2008
+ style: isFrozenRow ? {
2009
+ position: "sticky",
2010
+ top: rowHeight + rowIdx * rowHeight,
2011
+ zIndex: 8,
2012
+ backgroundColor: "inherit"
2013
+ } : void 0,
2014
+ children: [
2015
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky left-0 z-[5]", children: /* @__PURE__ */ jsxRuntime.jsx(RowHeader, { rowIndex: rowIdx }) }),
2016
+ Array.from({ length: columnCount }, (_2, colIdx) => {
2017
+ const addr = toAddress(rowIdx, colIdx);
2018
+ const isFrozenCol = colIdx < activeSheet.frozenCols;
2019
+ return /* @__PURE__ */ jsxRuntime.jsx(
2020
+ "div",
2021
+ {
2022
+ style: isFrozenCol ? {
2023
+ position: "sticky",
2024
+ left: 48 + Array.from({ length: colIdx }, (_3, c) => getColumnWidth(c)).reduce((a, b) => a + b, 0),
2025
+ zIndex: isFrozenRow ? 9 : 6,
2026
+ backgroundColor: "inherit"
2027
+ } : void 0,
2028
+ children: /* @__PURE__ */ jsxRuntime.jsx(Cell, { address: addr, row: rowIdx, col: colIdx })
2029
+ },
2030
+ addr
2031
+ );
2032
+ })
2033
+ ]
2034
+ },
2035
+ rowIdx
2036
+ );
2037
+ }),
1862
2038
  /* @__PURE__ */ jsxRuntime.jsx(SelectionOverlay, {}),
1863
2039
  editorPosition && /* @__PURE__ */ jsxRuntime.jsx(
1864
2040
  "div",
@@ -1886,6 +2062,8 @@ function DefaultToolbar() {
1886
2062
  confirmEdit,
1887
2063
  startEdit,
1888
2064
  setCellFormat,
2065
+ setFrozenRows,
2066
+ setFrozenCols,
1889
2067
  undo,
1890
2068
  redo,
1891
2069
  canUndo,
@@ -1957,7 +2135,57 @@ function DefaultToolbar() {
1957
2135
  ] })
1958
2136
  },
1959
2137
  align
1960
- ))
2138
+ )),
2139
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-1 h-4 w-px bg-zinc-200 dark:bg-zinc-700" }),
2140
+ /* @__PURE__ */ jsxRuntime.jsx(
2141
+ "button",
2142
+ {
2143
+ className: reactFancy.cn(btnClass, activeSheet.frozenRows > 0 && activeBtnClass),
2144
+ onClick: () => {
2145
+ if (activeSheet.frozenRows > 0) {
2146
+ setFrozenRows(0);
2147
+ } else {
2148
+ const row = selection.activeCell.match(/\d+/);
2149
+ setFrozenRows(row ? parseInt(row[0], 10) - 1 || 1 : 1);
2150
+ }
2151
+ },
2152
+ disabled: readOnly,
2153
+ title: activeSheet.frozenRows > 0 ? `Unfreeze rows (${activeSheet.frozenRows} frozen)` : "Freeze rows above current cell",
2154
+ children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
2155
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }),
2156
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "4", x2: "21", y2: "4" }),
2157
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "14", x2: "21", y2: "14", strokeDasharray: "3 3" }),
2158
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "19", x2: "21", y2: "19", strokeDasharray: "3 3" })
2159
+ ] })
2160
+ }
2161
+ ),
2162
+ /* @__PURE__ */ jsxRuntime.jsx(
2163
+ "button",
2164
+ {
2165
+ className: reactFancy.cn(btnClass, activeSheet.frozenCols > 0 && activeBtnClass),
2166
+ onClick: () => {
2167
+ if (activeSheet.frozenCols > 0) {
2168
+ setFrozenCols(0);
2169
+ } else {
2170
+ const colMatch = selection.activeCell.match(/^([A-Z]+)/);
2171
+ if (colMatch) {
2172
+ const col = colMatch[1].split("").reduce((acc, ch) => acc * 26 + ch.charCodeAt(0) - 64, 0) - 1;
2173
+ setFrozenCols(col || 1);
2174
+ } else {
2175
+ setFrozenCols(1);
2176
+ }
2177
+ }
2178
+ },
2179
+ disabled: readOnly,
2180
+ title: activeSheet.frozenCols > 0 ? `Unfreeze columns (${activeSheet.frozenCols} frozen)` : "Freeze columns left of current cell",
2181
+ children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
2182
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "3", x2: "9", y2: "21" }),
2183
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4", y1: "3", x2: "4", y2: "21" }),
2184
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "14", y1: "3", x2: "14", y2: "21", strokeDasharray: "3 3" }),
2185
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "19", y1: "3", x2: "19", y2: "21", strokeDasharray: "3 3" })
2186
+ ] })
2187
+ }
2188
+ )
1961
2189
  ] }),
1962
2190
  /* @__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: [
1963
2191
  /* @__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 }),