@extend-ai/react-xlsx 0.10.4 → 0.12.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.js CHANGED
@@ -6423,7 +6423,7 @@ function getSheetsWasmModule() {
6423
6423
  if (!wasmModulePromise) {
6424
6424
  wasmModulePromise = import("@dukelib/sheets-wasm").then(async (mod) => {
6425
6425
  if (configuredWasmSource !== void 0) {
6426
- await mod.default(configuredWasmSource);
6426
+ await mod.default({ module_or_path: configuredWasmSource });
6427
6427
  } else {
6428
6428
  await mod.default();
6429
6429
  }
@@ -7161,6 +7161,22 @@ function pushHistoryEntry(stack, entry) {
7161
7161
  function normalizeCellValue(value) {
7162
7162
  return value ?? "";
7163
7163
  }
7164
+ function cloneCellStyle(style) {
7165
+ if (!style || typeof style !== "object") {
7166
+ return style;
7167
+ }
7168
+ if (typeof structuredClone === "function") {
7169
+ try {
7170
+ return structuredClone(style);
7171
+ } catch {
7172
+ }
7173
+ }
7174
+ try {
7175
+ return JSON.parse(JSON.stringify(style));
7176
+ } catch {
7177
+ return style;
7178
+ }
7179
+ }
7164
7180
  function coerceUserEnteredValue(value) {
7165
7181
  const trimmed = value.trim();
7166
7182
  if (!trimmed) {
@@ -7183,9 +7199,12 @@ function coerceUserEnteredValue(value) {
7183
7199
  function applyCellMutationState(worksheet, cell, state) {
7184
7200
  if (state.formula) {
7185
7201
  worksheet.setFormula(cellAddressToA1(cell), state.formula);
7186
- return;
7202
+ } else {
7203
+ worksheet.setCell(cellAddressToA1(cell), normalizeCellValue(state.value));
7204
+ }
7205
+ if (state.style && typeof state.style === "object") {
7206
+ worksheet.setCellStyleAt(cell.row, cell.col, state.style);
7187
7207
  }
7188
- worksheet.setCell(cellAddressToA1(cell), normalizeCellValue(state.value));
7189
7208
  }
7190
7209
  function escapeHtml(value) {
7191
7210
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
@@ -7917,7 +7936,11 @@ function useXlsxViewerController(options) {
7917
7936
  return false;
7918
7937
  }, []);
7919
7938
  const ensureChartAssetsHydrated = React.useCallback((targetWorkbook, targetSheets) => {
7920
- if (chartAssetsRef.current || !targetWorkbook || !imageAssetsRef.current) {
7939
+ const currentAssets = chartAssetsRef.current;
7940
+ if (currentAssets && (currentAssets.chartOriginsById.size > 0 || !targetWorkbook || !imageAssetsRef.current)) {
7941
+ return currentAssets;
7942
+ }
7943
+ if (!targetWorkbook || !imageAssetsRef.current) {
7921
7944
  return chartAssetsRef.current;
7922
7945
  }
7923
7946
  const assets = loadWorkbookChartAssets(
@@ -8892,6 +8915,7 @@ function useXlsxViewerController(options) {
8892
8915
  }
8893
8916
  return {
8894
8917
  formula: worksheet.getFormulaAt(cell.row, cell.col) ?? null,
8918
+ style: worksheet.getCellStyleAt(cell.row, cell.col),
8895
8919
  value: worksheet.getCellAt(cell.row, cell.col).toJs()
8896
8920
  };
8897
8921
  }, [getActiveWorksheet]);
@@ -9036,6 +9060,7 @@ function useXlsxViewerController(options) {
9036
9060
  for (let col = startCol; col <= endCol; col += 1) {
9037
9061
  cells.push({
9038
9062
  formula: worksheet.getFormulaAt(row, col) ?? null,
9063
+ style: worksheet.getCellStyleAt(row, col),
9039
9064
  value: worksheet.getCellAt(row, col).toJs()
9040
9065
  });
9041
9066
  }
@@ -9202,11 +9227,27 @@ function useXlsxViewerController(options) {
9202
9227
  }, [getColumnWidthPx, getRowHeightPx]);
9203
9228
  const setChartRect = React.useCallback((id, rect) => {
9204
9229
  const hydratedChartAssets = ensureChartAssetsHydrated(workbook, sheets);
9230
+ console.info("[react-xlsx debug] setChartRect", {
9231
+ hasActiveSheet: Boolean(activeSheet),
9232
+ hasHydratedChartAssets: Boolean(hydratedChartAssets),
9233
+ hasImageAssets: Boolean(imageAssetsRef.current),
9234
+ hasWorkbook: Boolean(workbook),
9235
+ id,
9236
+ readOnly,
9237
+ rect
9238
+ });
9205
9239
  if (readOnly || !workbook || !activeSheet || !imageAssetsRef.current || !hydratedChartAssets) {
9206
9240
  return;
9207
9241
  }
9208
9242
  const worksheet = workbook.getSheet(activeSheet.workbookSheetIndex);
9209
9243
  const currentChart = getChartById(id);
9244
+ console.info("[react-xlsx debug] currentChart", {
9245
+ activeWorkbookSheetIndex: activeSheet.workbookSheetIndex,
9246
+ editable: currentChart?.editable,
9247
+ found: Boolean(currentChart),
9248
+ originCount: hydratedChartAssets.chartOriginsById.size,
9249
+ workbookSheetIndex: currentChart?.workbookSheetIndex
9250
+ });
9210
9251
  if (!currentChart || currentChart.editable === false || currentChart.workbookSheetIndex !== activeSheet.workbookSheetIndex) {
9211
9252
  return;
9212
9253
  }
@@ -9217,7 +9258,8 @@ function useXlsxViewerController(options) {
9217
9258
  getRowHeightPx: (row) => getRowHeightPx(worksheet, row)
9218
9259
  });
9219
9260
  recordHistoryBeforeMutation();
9220
- updateWorkbookChartAnchor(imageAssetsRef.current, hydratedChartAssets, id, nextAnchor);
9261
+ const didUpdateAnchor = updateWorkbookChartAnchor(imageAssetsRef.current, hydratedChartAssets, id, nextAnchor);
9262
+ console.info("[react-xlsx debug] updateWorkbookChartAnchor", { didUpdateAnchor, nextAnchor });
9221
9263
  hydratedChartAssets.chartsByWorkbookSheetIndex = hydratedChartAssets.chartsByWorkbookSheetIndex.map((sheetCharts) => sheetCharts.map((chart) => chart.id === id ? { ...chart, anchor: nextAnchor } : chart));
9222
9264
  setChartsByWorkbookSheetIndex((current) => current.map((sheetCharts) => sheetCharts.map((chart) => chart.id === id ? { ...chart, anchor: nextAnchor } : chart)));
9223
9265
  setRevision((current) => current + 1);
@@ -9433,8 +9475,12 @@ function useXlsxViewerController(options) {
9433
9475
  continue;
9434
9476
  }
9435
9477
  worksheet.setCell(cellAddressToA1({ row, col }), "");
9478
+ const after = captureCellMutationState(cell);
9479
+ if (!after) {
9480
+ continue;
9481
+ }
9436
9482
  mutations.push({
9437
- after: { formula: null, value: "" },
9483
+ after,
9438
9484
  before,
9439
9485
  cell
9440
9486
  });
@@ -9465,9 +9511,13 @@ function useXlsxViewerController(options) {
9465
9511
  }
9466
9512
  const nextValue = coerceUserEnteredValue(value);
9467
9513
  worksheet.setCell(cellAddressToA1(cell), nextValue);
9514
+ const after = captureCellMutationState(cell);
9515
+ if (!after) {
9516
+ return;
9517
+ }
9468
9518
  maybeRecalculateWorkbook(workbook);
9469
9519
  refreshWorkbookState(workbook);
9470
- recordCellEditHistory(cell, before, { formula: null, value: nextValue });
9520
+ recordCellEditHistory(cell, before, after);
9471
9521
  }, [captureCellMutationState, getActiveWorksheet, maybeRecalculateWorkbook, readOnly, recordCellEditHistory, refreshWorkbookState, workbook]);
9472
9522
  const setCellFormula = React.useCallback((cell, formula) => {
9473
9523
  const worksheet = getActiveWorksheet();
@@ -9484,13 +9534,31 @@ function useXlsxViewerController(options) {
9484
9534
  } else {
9485
9535
  worksheet.setFormula(cellAddressToA1(cell), formula);
9486
9536
  }
9537
+ const after = captureCellMutationState(cell);
9538
+ if (!after) {
9539
+ return;
9540
+ }
9487
9541
  maybeRecalculateWorkbook(workbook);
9488
9542
  refreshWorkbookState(workbook);
9489
- recordCellEditHistory(cell, before, {
9490
- formula: trimmedFormula || null,
9491
- value: trimmedFormula ? null : ""
9492
- });
9543
+ recordCellEditHistory(cell, before, after);
9493
9544
  }, [captureCellMutationState, getActiveWorksheet, maybeRecalculateWorkbook, readOnly, recordCellEditHistory, refreshWorkbookState, workbook]);
9545
+ const setCellStyle = React.useCallback((cell, style) => {
9546
+ const worksheet = getActiveWorksheet();
9547
+ if (readOnly || !worksheet || !workbook) {
9548
+ return;
9549
+ }
9550
+ const before = captureCellMutationState(cell);
9551
+ if (!before) {
9552
+ return;
9553
+ }
9554
+ worksheet.setCellStyleAt(cell.row, cell.col, style);
9555
+ const after = captureCellMutationState(cell);
9556
+ if (!after) {
9557
+ return;
9558
+ }
9559
+ refreshWorkbookState(workbook);
9560
+ recordCellEditHistory(cell, before, after);
9561
+ }, [captureCellMutationState, getActiveWorksheet, readOnly, recordCellEditHistory, refreshWorkbookState, workbook]);
9494
9562
  const setSelectedCellValue = React.useCallback((value) => {
9495
9563
  if (!activeCell) {
9496
9564
  return;
@@ -9503,6 +9571,51 @@ function useXlsxViewerController(options) {
9503
9571
  }
9504
9572
  setCellFormula(activeCell, formula);
9505
9573
  }, [activeCell, setCellFormula]);
9574
+ const setSelectedCellStyle = React.useCallback((style) => {
9575
+ if (!activeCell) {
9576
+ return;
9577
+ }
9578
+ setCellStyle(activeCell, style);
9579
+ }, [activeCell, setCellStyle]);
9580
+ const setRangeStyle = React.useCallback((range, style) => {
9581
+ const worksheet = getActiveWorksheet();
9582
+ if (readOnly || !worksheet || !workbook) {
9583
+ return;
9584
+ }
9585
+ const normalized = normalizeRange(range);
9586
+ const beforeStates = [];
9587
+ for (let row = normalized.start.row; row <= normalized.end.row; row += 1) {
9588
+ for (let col = normalized.start.col; col <= normalized.end.col; col += 1) {
9589
+ const cell = { row, col };
9590
+ const before = captureCellMutationState(cell);
9591
+ if (!before) {
9592
+ continue;
9593
+ }
9594
+ beforeStates.push({
9595
+ before,
9596
+ cell
9597
+ });
9598
+ }
9599
+ }
9600
+ if (beforeStates.length === 0) {
9601
+ return;
9602
+ }
9603
+ worksheet.setRangeStyle(rangeToA1(normalized), style);
9604
+ const mutations = [];
9605
+ for (const mutation of beforeStates) {
9606
+ const after = captureCellMutationState(mutation.cell);
9607
+ if (!after) {
9608
+ continue;
9609
+ }
9610
+ mutations.push({
9611
+ after,
9612
+ before: mutation.before,
9613
+ cell: mutation.cell
9614
+ });
9615
+ }
9616
+ refreshWorkbookState(workbook);
9617
+ recordRangeEditHistory(mutations, selection, activeCell);
9618
+ }, [activeCell, captureCellMutationState, getActiveWorksheet, readOnly, recordRangeEditHistory, refreshWorkbookState, selection, workbook]);
9506
9619
  const fillSelection = React.useCallback((targetRange) => {
9507
9620
  const worksheet = getActiveWorksheet();
9508
9621
  if (readOnly || !worksheet || !workbook || !selection) {
@@ -9529,19 +9642,22 @@ function useXlsxViewerController(options) {
9529
9642
  const sourceRow = sourceRange.start.row + (row - nextRange.start.row) % sourceHeight;
9530
9643
  const sourceCol = sourceRange.start.col + (col - nextRange.start.col) % sourceWidth;
9531
9644
  const sourceFormula = worksheet.getFormulaAt(sourceRow, sourceCol);
9645
+ const sourceStyle = cloneCellStyle(worksheet.getCellStyleAt(sourceRow, sourceCol));
9532
9646
  if (sourceFormula) {
9533
9647
  worksheet.setFormula(cellAddressToA1(targetCell), sourceFormula);
9534
- mutations.push({
9535
- after: { formula: sourceFormula, value: null },
9536
- before,
9537
- cell: targetCell
9538
- });
9648
+ } else {
9649
+ const sourceValue = normalizeCellValue(worksheet.getCellAt(sourceRow, sourceCol).toJs());
9650
+ worksheet.setCell(cellAddressToA1(targetCell), sourceValue);
9651
+ }
9652
+ if (sourceStyle && typeof sourceStyle === "object") {
9653
+ worksheet.setCellStyleAt(targetCell.row, targetCell.col, sourceStyle);
9654
+ }
9655
+ const after = captureCellMutationState(targetCell);
9656
+ if (!after) {
9539
9657
  continue;
9540
9658
  }
9541
- const sourceValue = normalizeCellValue(worksheet.getCellAt(sourceRow, sourceCol).toJs());
9542
- worksheet.setCell(cellAddressToA1(targetCell), sourceValue);
9543
9659
  mutations.push({
9544
- after: { formula: null, value: sourceValue },
9660
+ after,
9545
9661
  before,
9546
9662
  cell: targetCell
9547
9663
  });
@@ -9681,16 +9797,24 @@ function useXlsxViewerController(options) {
9681
9797
  }
9682
9798
  if (rawValue.startsWith("=") && rawValue.length > 1) {
9683
9799
  worksheet.setFormula(cellAddressToA1(nextCell), rawValue);
9800
+ const after = captureCellMutationState(nextCell);
9801
+ if (!after) {
9802
+ continue;
9803
+ }
9684
9804
  mutations.push({
9685
- after: { formula: rawValue, value: null },
9805
+ after,
9686
9806
  before,
9687
9807
  cell: nextCell
9688
9808
  });
9689
9809
  } else {
9690
9810
  const nextValue = coerceUserEnteredValue(rawValue);
9691
9811
  worksheet.setCell(cellAddressToA1(nextCell), nextValue);
9812
+ const after = captureCellMutationState(nextCell);
9813
+ if (!after) {
9814
+ continue;
9815
+ }
9692
9816
  mutations.push({
9693
- after: { formula: null, value: nextValue },
9817
+ after,
9694
9818
  before,
9695
9819
  cell: nextCell
9696
9820
  });
@@ -9741,8 +9865,12 @@ function useXlsxViewerController(options) {
9741
9865
  if (cell.formula) {
9742
9866
  worksheet.setFormula(cellAddressToA1(nextCell), cell.formula);
9743
9867
  if (before) {
9868
+ const after = captureCellMutationState(nextCell);
9869
+ if (!after) {
9870
+ continue;
9871
+ }
9744
9872
  mutations.push({
9745
- after: { formula: cell.formula, value: null },
9873
+ after,
9746
9874
  before,
9747
9875
  cell: nextCell
9748
9876
  });
@@ -9750,8 +9878,12 @@ function useXlsxViewerController(options) {
9750
9878
  } else {
9751
9879
  worksheet.setCell(cellAddressToA1(nextCell), cell.value);
9752
9880
  if (before) {
9881
+ const after = captureCellMutationState(nextCell);
9882
+ if (!after) {
9883
+ continue;
9884
+ }
9753
9885
  mutations.push({
9754
- after: { formula: null, value: cell.value },
9886
+ after,
9755
9887
  before,
9756
9888
  cell: nextCell
9757
9889
  });
@@ -9968,7 +10100,9 @@ function useXlsxViewerController(options) {
9968
10100
  resizeColumn,
9969
10101
  resizeRow,
9970
10102
  setCellFormula,
10103
+ setCellStyle,
9971
10104
  setCellValue,
10105
+ setRangeStyle,
9972
10106
  setZoomScale,
9973
10107
  setChartRect,
9974
10108
  setImageRect,
@@ -9987,6 +10121,7 @@ function useXlsxViewerController(options) {
9987
10121
  setActiveSheetIndex,
9988
10122
  setActiveTabIndex,
9989
10123
  setSelectedCellFormula,
10124
+ setSelectedCellStyle,
9990
10125
  setSelectedCellValue,
9991
10126
  sheets,
9992
10127
  shapes,
@@ -10070,7 +10205,9 @@ function useXlsxViewerController(options) {
10070
10205
  resizeColumn,
10071
10206
  resizeRow,
10072
10207
  setCellFormula,
10208
+ setCellStyle,
10073
10209
  setCellValue,
10210
+ setRangeStyle,
10074
10211
  setZoomScale,
10075
10212
  setChartRect,
10076
10213
  setImageRect,
@@ -10089,6 +10226,7 @@ function useXlsxViewerController(options) {
10089
10226
  setActiveSheetIndex,
10090
10227
  setActiveTabIndex,
10091
10228
  setSelectedCellFormula,
10229
+ setSelectedCellStyle,
10092
10230
  setSelectedCellValue,
10093
10231
  sheets,
10094
10232
  shapes,
@@ -16584,6 +16722,9 @@ var WHEEL_ZOOM_SENSITIVITY = 25e-5;
16584
16722
  var WHEEL_LINE_DELTA_PX = 16;
16585
16723
  var CHART_SOURCE_HIGHLIGHT_COLORS = ["#2563eb", "#dc2626", "#7c3aed", "#059669", "#ea580c", "#db2777"];
16586
16724
  var SHEET_SURFACE = "#ffffff";
16725
+ var DRAWING_SELECTION_STROKE = "#64748b";
16726
+ var DRAWING_SELECTION_HANDLE_FILL = "#ffffff";
16727
+ var DRAWING_SELECTION_HANDLE_SHADOW = "rgba(15, 23, 42, 0.18)";
16587
16728
  var DEFAULT_CELL_PADDING = "0 4px";
16588
16729
  var IMAGE_HANDLE_POSITIONS = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
16589
16730
  var CANVAS_CELL_STYLE_CACHE_LIMIT = 4096;
@@ -16669,6 +16810,38 @@ function measureCanvasTextWidth(context, text) {
16669
16810
  CANVAS_TEXT_MEASURE_CACHE_LIMIT
16670
16811
  );
16671
16812
  }
16813
+ function drawCanvasTextDecorations(context, {
16814
+ align,
16815
+ color,
16816
+ decoration,
16817
+ ellipsize = false,
16818
+ lineThroughY,
16819
+ maxWidth,
16820
+ text,
16821
+ textX,
16822
+ underlineY,
16823
+ zoomFactor
16824
+ }) {
16825
+ if (!decoration || text.length === 0) {
16826
+ return;
16827
+ }
16828
+ const measured = ellipsize && maxWidth !== void 0 ? Math.min(maxWidth, measureCanvasTextWidth(context, text)) : measureCanvasTextWidth(context, text);
16829
+ const startX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
16830
+ context.strokeStyle = color;
16831
+ context.lineWidth = Math.max(1, zoomFactor * 0.75);
16832
+ if (decoration.includes("underline")) {
16833
+ context.beginPath();
16834
+ context.moveTo(startX, underlineY);
16835
+ context.lineTo(startX + measured, underlineY);
16836
+ context.stroke();
16837
+ }
16838
+ if (decoration.includes("line-through")) {
16839
+ context.beginPath();
16840
+ context.moveTo(startX, lineThroughY);
16841
+ context.lineTo(startX + measured, lineThroughY);
16842
+ context.stroke();
16843
+ }
16844
+ }
16672
16845
  function getCachedCanvasPath2D(path) {
16673
16846
  if (typeof Path2D === "undefined") {
16674
16847
  return null;
@@ -18714,7 +18887,9 @@ function resolveImageHandleStyle(position, stroke, surface, scale = 1) {
18714
18887
  const style = {
18715
18888
  backgroundColor: surface,
18716
18889
  border: `${Math.max(1, scale)}px solid ${stroke}`,
18717
- borderRadius: 6 * scale,
18890
+ borderRadius: 3 * scale,
18891
+ boxShadow: `0 1px ${3 * scale}px ${DRAWING_SELECTION_HANDLE_SHADOW}`,
18892
+ boxSizing: "border-box",
18718
18893
  cursor: IMAGE_HANDLE_CURSOR[position],
18719
18894
  height: handleSize,
18720
18895
  pointerEvents: "auto",
@@ -21511,6 +21686,7 @@ function XlsxGrid({
21511
21686
  enableCanvasSelectionAnimation = true,
21512
21687
  errorState,
21513
21688
  fileTooLargeState,
21689
+ getCellStyle,
21514
21690
  loadingComponent,
21515
21691
  loadingState,
21516
21692
  renderChartLoading,
@@ -23470,7 +23646,7 @@ function XlsxGrid({
23470
23646
  const viewportRowBatch = getRowsBatchAsync ? asyncViewportRowBatch : syncViewportRowBatch;
23471
23647
  React4.useEffect(() => {
23472
23648
  cellRenderCacheRef.current.clear();
23473
- }, [activeSheetIndex, displayColLimit, displayRowLimit, palette, revision, viewportRowBatch, worksheet, zoomFactor]);
23649
+ }, [activeSheetIndex, displayColLimit, displayRowLimit, getCellStyle, palette, revision, viewportRowBatch, worksheet, zoomFactor]);
23474
23650
  React4.useEffect(() => {
23475
23651
  setAsyncViewportRowBatch(null);
23476
23652
  }, [activeSheetIndex, revision]);
@@ -23595,6 +23771,29 @@ function XlsxGrid({
23595
23771
  validation: resolveCellDataValidation(row, col, activeSheet),
23596
23772
  value: sparkline ? "" : checkboxState !== null ? "" : batchCoversRow || !worksheet ? batchedCell?.value ?? "" : getCellDisplayValue(worksheet, row, col, activeSheet)
23597
23773
  };
23774
+ if (getCellStyle) {
23775
+ const styleOverrides = getCellStyle({
23776
+ cell: { row, col },
23777
+ hasChartHighlight: Boolean(nextData.chartHighlight),
23778
+ hasConditionalFormat: Boolean(
23779
+ nextData.conditionalColorScale || nextData.conditionalDataBar || nextData.conditionalIcon
23780
+ ),
23781
+ hasHyperlink: Boolean(nextData.hyperlink),
23782
+ hasValidation: Boolean(nextData.validation),
23783
+ isMerged: Boolean(nextData.colSpan || nextData.rowSpan),
23784
+ isTableHeader: Boolean(nextData.isTableHeader),
23785
+ resolvedStyle: { ...nextData.style },
23786
+ sheetName: activeSheet?.name ?? "",
23787
+ value: nextData.value,
23788
+ workbookSheetIndex: activeSheet?.workbookSheetIndex ?? -1
23789
+ });
23790
+ if (styleOverrides) {
23791
+ nextData.style = { ...nextData.style, ...styleOverrides };
23792
+ if (nextData.conditionalColorScale && (styleOverrides.backgroundColor !== void 0 || styleOverrides.background !== void 0)) {
23793
+ nextData.conditionalColorScale = null;
23794
+ }
23795
+ }
23796
+ }
23598
23797
  nextData.canvas = buildCanvasCellStyleCache(nextData.style);
23599
23798
  if (canCellTextOverflow(nextData)) {
23600
23799
  const startColIndex = colIndexByActual.get(col);
@@ -23680,6 +23879,7 @@ function XlsxGrid({
23680
23879
  displayDefaultColWidth,
23681
23880
  displayEffectiveColWidths,
23682
23881
  effectiveTables,
23882
+ getCellStyle,
23683
23883
  palette,
23684
23884
  sparklinesByCell,
23685
23885
  viewportRowBatch,
@@ -26204,16 +26404,17 @@ function XlsxGrid({
26204
26404
  wrappedLines.forEach((line, lineIndex) => {
26205
26405
  const textY = textBlockTop + lineIndex * lineHeight + lineHeight / 2;
26206
26406
  paneContext.fillText(line, textX, textY);
26207
- if (canvasCellStyle.textDecoration?.includes("underline") && line.length > 0) {
26208
- const measured = Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, line));
26209
- const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
26210
- paneContext.beginPath();
26211
- paneContext.moveTo(underlineStartX, textY + Math.max(2, lineHeight * 0.24));
26212
- paneContext.lineTo(underlineStartX + measured, textY + Math.max(2, lineHeight * 0.24));
26213
- paneContext.strokeStyle = textColor;
26214
- paneContext.lineWidth = Math.max(1, zoomFactor * 0.75);
26215
- paneContext.stroke();
26216
- }
26407
+ drawCanvasTextDecorations(paneContext, {
26408
+ align,
26409
+ color: textColor,
26410
+ decoration: canvasCellStyle.textDecoration,
26411
+ lineThroughY: textY,
26412
+ maxWidth: maxTextWidth,
26413
+ text: line,
26414
+ textX,
26415
+ underlineY: textY + Math.max(2, lineHeight * 0.24),
26416
+ zoomFactor
26417
+ });
26217
26418
  });
26218
26419
  } else if (spillMaxWidth != null) {
26219
26420
  const text = shouldEllipsizeText ? truncateCanvasText(paneContext, rawText, maxTextWidth) : rawText;
@@ -26232,22 +26433,25 @@ function XlsxGrid({
26232
26433
  textDecoration: canvasCellStyle.textDecoration,
26233
26434
  textX,
26234
26435
  textY,
26436
+ lineThroughY: textY,
26235
26437
  underlineY: textY + 6 * zoomFactor
26236
26438
  });
26237
26439
  } else {
26238
26440
  const text = cellData.shrinkToFit ? rawText : shouldEllipsizeText ? truncateCanvasText(paneContext, rawText, maxTextWidth) : rawText;
26239
26441
  const textY = contentTop + contentHeight / 2;
26240
26442
  paneContext.fillText(text, textX, textY);
26241
- if (canvasCellStyle.textDecoration?.includes("underline") && text.length > 0) {
26242
- const measured = shouldEllipsizeText ? Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, text)) : measureCanvasTextWidth(paneContext, text);
26243
- const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
26244
- paneContext.beginPath();
26245
- paneContext.moveTo(underlineStartX, textY + 6 * zoomFactor);
26246
- paneContext.lineTo(underlineStartX + measured, textY + 6 * zoomFactor);
26247
- paneContext.strokeStyle = textColor;
26248
- paneContext.lineWidth = Math.max(1, zoomFactor * 0.75);
26249
- paneContext.stroke();
26250
- }
26443
+ drawCanvasTextDecorations(paneContext, {
26444
+ align,
26445
+ color: textColor,
26446
+ decoration: canvasCellStyle.textDecoration,
26447
+ ellipsize: shouldEllipsizeText,
26448
+ lineThroughY: textY,
26449
+ maxWidth: maxTextWidth,
26450
+ text,
26451
+ textX,
26452
+ underlineY: textY + 6 * zoomFactor,
26453
+ zoomFactor
26454
+ });
26251
26455
  }
26252
26456
  }
26253
26457
  if (cellData.conditionalIcon) {
@@ -26281,16 +26485,18 @@ function XlsxGrid({
26281
26485
  paneContext.textAlign = spillText.align;
26282
26486
  paneContext.textBaseline = "middle";
26283
26487
  paneContext.fillText(spillText.text, spillText.textX, spillText.textY);
26284
- if (spillText.textDecoration?.includes("underline") && spillText.text.length > 0) {
26285
- const measured = spillText.ellipsize ? Math.min(spillText.maxWidth, measureCanvasTextWidth(paneContext, spillText.text)) : measureCanvasTextWidth(paneContext, spillText.text);
26286
- const underlineStartX = spillText.align === "right" ? spillText.textX - measured : spillText.align === "center" ? spillText.textX - measured / 2 : spillText.textX;
26287
- paneContext.beginPath();
26288
- paneContext.moveTo(underlineStartX, spillText.underlineY);
26289
- paneContext.lineTo(underlineStartX + measured, spillText.underlineY);
26290
- paneContext.strokeStyle = spillText.color;
26291
- paneContext.lineWidth = Math.max(1, zoomFactor * 0.75);
26292
- paneContext.stroke();
26293
- }
26488
+ drawCanvasTextDecorations(paneContext, {
26489
+ align: spillText.align,
26490
+ color: spillText.color,
26491
+ decoration: spillText.textDecoration,
26492
+ ellipsize: spillText.ellipsize,
26493
+ lineThroughY: spillText.lineThroughY,
26494
+ maxWidth: spillText.maxWidth,
26495
+ text: spillText.text,
26496
+ textX: spillText.textX,
26497
+ underlineY: spillText.underlineY,
26498
+ zoomFactor
26499
+ });
26294
26500
  paneContext.restore();
26295
26501
  }
26296
26502
  }
@@ -27199,6 +27405,7 @@ function XlsxGrid({
27199
27405
  }
27200
27406
  const isFrozenDrawing = pane !== "scroll";
27201
27407
  const canEditImage = !readOnly && image.editable !== false;
27408
+ const drawingSelectionSurface = paletteIsDark(palette) ? palette.canvas : DRAWING_SELECTION_HANDLE_FILL;
27202
27409
  const style = {
27203
27410
  contain: "layout paint",
27204
27411
  height: rect.height,
@@ -27230,6 +27437,7 @@ function XlsxGrid({
27230
27437
  {
27231
27438
  style: {
27232
27439
  ...style,
27440
+ contain: "layout",
27233
27441
  overflow: "visible",
27234
27442
  pointerEvents: "none",
27235
27443
  zIndex: isFrozenDrawing ? image.zIndex + 22 : image.zIndex + 2
@@ -27239,8 +27447,8 @@ function XlsxGrid({
27239
27447
  "div",
27240
27448
  {
27241
27449
  style: {
27242
- border: `${Math.max(1, zoomFactor)}px solid ${selectionStroke}`,
27243
- boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${palette.surface}`,
27450
+ border: `${Math.max(1, zoomFactor)}px solid ${DRAWING_SELECTION_STROKE}`,
27451
+ boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${drawingSelectionSurface}`,
27244
27452
  boxSizing: "border-box",
27245
27453
  inset: 0,
27246
27454
  pointerEvents: "none",
@@ -27250,7 +27458,7 @@ function XlsxGrid({
27250
27458
  "div",
27251
27459
  {
27252
27460
  onPointerDown: (event) => startImageResize(event, image, rect, position),
27253
- style: resolveImageHandleStyle(position, selectionStroke, palette.surface, zoomFactor)
27461
+ style: resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor)
27254
27462
  },
27255
27463
  position
27256
27464
  )) : null
@@ -27262,7 +27470,7 @@ function XlsxGrid({
27262
27470
  startImageResize(event, image, rect, position);
27263
27471
  }
27264
27472
  },
27265
- style: canEditImage ? resolveImageHandleStyle(position, selectionStroke, palette.surface, zoomFactor) : { ...resolveImageHandleStyle(position, selectionStroke, palette.surface, zoomFactor), display: "none" }
27473
+ style: canEditImage ? resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor) : { ...resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor), display: "none" }
27266
27474
  }),
27267
27475
  image,
27268
27476
  rect
@@ -27270,8 +27478,8 @@ function XlsxGrid({
27270
27478
  "div",
27271
27479
  {
27272
27480
  style: {
27273
- border: `${Math.max(1, zoomFactor)}px solid ${selectionStroke}`,
27274
- boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${palette.surface}`,
27481
+ border: `${Math.max(1, zoomFactor)}px solid ${DRAWING_SELECTION_STROKE}`,
27482
+ boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${drawingSelectionSurface}`,
27275
27483
  boxSizing: "border-box",
27276
27484
  inset: 0,
27277
27485
  pointerEvents: "none",
@@ -27281,7 +27489,7 @@ function XlsxGrid({
27281
27489
  "div",
27282
27490
  {
27283
27491
  onPointerDown: (event) => startImageResize(event, image, rect, position),
27284
- style: resolveImageHandleStyle(position, selectionStroke, palette.surface, zoomFactor)
27492
+ style: resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor)
27285
27493
  },
27286
27494
  position
27287
27495
  )) : null
@@ -27319,6 +27527,7 @@ function XlsxGrid({
27319
27527
  }
27320
27528
  const isFrozenDrawing = pane !== "scroll";
27321
27529
  const canEditChart = !readOnly && chart.editable !== false;
27530
+ const drawingSelectionSurface = paletteIsDark(palette) ? palette.canvas : DRAWING_SELECTION_HANDLE_FILL;
27322
27531
  const style = {
27323
27532
  contain: "layout paint",
27324
27533
  height: rect.height,
@@ -27335,6 +27544,7 @@ function XlsxGrid({
27335
27544
  {
27336
27545
  style: {
27337
27546
  ...style,
27547
+ contain: "layout",
27338
27548
  overflow: "visible",
27339
27549
  pointerEvents: "none",
27340
27550
  zIndex: isFrozenDrawing ? chart.zIndex + 22 : chart.zIndex + 2
@@ -27343,8 +27553,8 @@ function XlsxGrid({
27343
27553
  "div",
27344
27554
  {
27345
27555
  style: {
27346
- border: `${Math.max(1, zoomFactor)}px solid ${selectionStroke}`,
27347
- boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${palette.surface}`,
27556
+ border: `${Math.max(1, zoomFactor)}px solid ${DRAWING_SELECTION_STROKE}`,
27557
+ boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${drawingSelectionSurface}`,
27348
27558
  boxSizing: "border-box",
27349
27559
  inset: 0,
27350
27560
  pointerEvents: "none",
@@ -27354,7 +27564,7 @@ function XlsxGrid({
27354
27564
  "div",
27355
27565
  {
27356
27566
  onPointerDown: (event) => startChartResize(event, chart, rect, position),
27357
- style: resolveImageHandleStyle(position, selectionStroke, palette.surface, zoomFactor)
27567
+ style: resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor)
27358
27568
  },
27359
27569
  position
27360
27570
  )) : null
@@ -27634,6 +27844,22 @@ function XlsxGrid({
27634
27844
  }
27635
27845
  function installImageInteractionListeners(pointerId) {
27636
27846
  imageInteractionCleanupRef.current?.();
27847
+ const resolveInteractionRect = (interaction, clientX, clientY) => {
27848
+ const deltaX = clientX - interaction.startClientX;
27849
+ const deltaY = clientY - interaction.startClientY;
27850
+ return clampImageRect(
27851
+ interaction.type === "move" ? {
27852
+ ...interaction.baseRect,
27853
+ left: interaction.baseRect.left + deltaX,
27854
+ top: interaction.baseRect.top + deltaY
27855
+ } : resizeImageRect(interaction.baseRect, interaction.handle, deltaX, deltaY, displayImageMinSize),
27856
+ {
27857
+ contentOffsetLeft: displayRowHeaderWidth,
27858
+ contentOffsetTop: displayHeaderHeight,
27859
+ minSizePx: displayImageMinSize
27860
+ }
27861
+ );
27862
+ };
27637
27863
  const handlePointerMove = (event) => {
27638
27864
  if (event.pointerId !== pointerId) {
27639
27865
  return;
@@ -27647,18 +27873,7 @@ function XlsxGrid({
27647
27873
  if (!interaction.didMove && (Math.abs(deltaX) > 3 || Math.abs(deltaY) > 3)) {
27648
27874
  interaction.didMove = true;
27649
27875
  }
27650
- const nextRect = clampImageRect(
27651
- interaction.type === "move" ? {
27652
- ...interaction.baseRect,
27653
- left: interaction.baseRect.left + deltaX,
27654
- top: interaction.baseRect.top + deltaY
27655
- } : resizeImageRect(interaction.baseRect, interaction.handle, deltaX, deltaY, displayImageMinSize),
27656
- {
27657
- contentOffsetLeft: displayRowHeaderWidth,
27658
- contentOffsetTop: displayHeaderHeight,
27659
- minSizePx: displayImageMinSize
27660
- }
27661
- );
27876
+ const nextRect = resolveInteractionRect(interaction, event.clientX, event.clientY);
27662
27877
  scheduleImagePreviewRect({ id: interaction.imageId, rect: nextRect });
27663
27878
  };
27664
27879
  const cleanup = () => {
@@ -27681,18 +27896,20 @@ function XlsxGrid({
27681
27896
  imagePreviewRectRef.current = pendingPreview;
27682
27897
  setImagePreviewRect(pendingPreview);
27683
27898
  }
27684
- const preview = pendingPreview ?? imagePreviewRectRef.current;
27899
+ const finalRect = interaction ? resolveInteractionRect(interaction, event.clientX, event.clientY) : null;
27685
27900
  imageInteractionRef.current = null;
27686
27901
  imageInteractionCleanupRef.current = null;
27687
27902
  setInteractionMode("idle");
27688
27903
  document.body.style.cursor = "";
27689
27904
  document.body.style.userSelect = "";
27690
27905
  cleanup();
27691
- if (interaction && preview && preview.id === interaction.imageId) {
27906
+ if (interaction) {
27692
27907
  if (interaction.didMove) {
27693
27908
  skipNextImageClickRef.current = interaction.imageId;
27694
27909
  }
27695
- setImageRect(interaction.imageId, toLogicalRect(preview.rect));
27910
+ if (interaction.didMove && finalRect) {
27911
+ setImageRect(interaction.imageId, toLogicalRect(finalRect));
27912
+ }
27696
27913
  }
27697
27914
  imagePreviewRectRef.current = null;
27698
27915
  setImagePreviewRect(null);
@@ -27704,6 +27921,22 @@ function XlsxGrid({
27704
27921
  }
27705
27922
  function installChartInteractionListeners(pointerId) {
27706
27923
  chartInteractionCleanupRef.current?.();
27924
+ const resolveInteractionRect = (interaction, clientX, clientY) => {
27925
+ const deltaX = clientX - interaction.startClientX;
27926
+ const deltaY = clientY - interaction.startClientY;
27927
+ return clampImageRect(
27928
+ interaction.type === "move" ? {
27929
+ ...interaction.baseRect,
27930
+ left: interaction.baseRect.left + deltaX,
27931
+ top: interaction.baseRect.top + deltaY
27932
+ } : resizeImageRect(interaction.baseRect, interaction.handle, deltaX, deltaY, 48 * zoomFactor),
27933
+ {
27934
+ contentOffsetLeft: displayRowHeaderWidth,
27935
+ contentOffsetTop: displayHeaderHeight,
27936
+ minSizePx: 48 * zoomFactor
27937
+ }
27938
+ );
27939
+ };
27707
27940
  const handlePointerMove = (event) => {
27708
27941
  if (event.pointerId !== pointerId) {
27709
27942
  return;
@@ -27717,18 +27950,7 @@ function XlsxGrid({
27717
27950
  if (!interaction.didMove && (Math.abs(deltaX) > 3 || Math.abs(deltaY) > 3)) {
27718
27951
  interaction.didMove = true;
27719
27952
  }
27720
- const nextRect = clampImageRect(
27721
- interaction.type === "move" ? {
27722
- ...interaction.baseRect,
27723
- left: interaction.baseRect.left + deltaX,
27724
- top: interaction.baseRect.top + deltaY
27725
- } : resizeImageRect(interaction.baseRect, interaction.handle, deltaX, deltaY, 48 * zoomFactor),
27726
- {
27727
- contentOffsetLeft: displayRowHeaderWidth,
27728
- contentOffsetTop: displayHeaderHeight,
27729
- minSizePx: 48 * zoomFactor
27730
- }
27731
- );
27953
+ const nextRect = resolveInteractionRect(interaction, event.clientX, event.clientY);
27732
27954
  scheduleChartPreviewRect({ id: interaction.chartId, rect: nextRect });
27733
27955
  };
27734
27956
  const cleanup = () => {
@@ -27751,18 +27973,20 @@ function XlsxGrid({
27751
27973
  chartPreviewRectRef.current = pendingPreview;
27752
27974
  setChartPreviewRect(pendingPreview);
27753
27975
  }
27754
- const preview = pendingPreview ?? chartPreviewRectRef.current;
27976
+ const finalRect = interaction ? resolveInteractionRect(interaction, event.clientX, event.clientY) : null;
27755
27977
  chartInteractionRef.current = null;
27756
27978
  chartInteractionCleanupRef.current = null;
27757
27979
  setInteractionMode("idle");
27758
27980
  document.body.style.cursor = "";
27759
27981
  document.body.style.userSelect = "";
27760
27982
  cleanup();
27761
- if (interaction && preview && preview.id === interaction.chartId) {
27983
+ if (interaction) {
27762
27984
  if (interaction.didMove) {
27763
27985
  skipNextChartClickRef.current = interaction.chartId;
27764
27986
  }
27765
- setChartRect(interaction.chartId, toLogicalRect(preview.rect));
27987
+ if (interaction.didMove && finalRect) {
27988
+ setChartRect(interaction.chartId, toLogicalRect(finalRect));
27989
+ }
27766
27990
  }
27767
27991
  chartPreviewRectRef.current = null;
27768
27992
  setChartPreviewRect(null);
@@ -28065,7 +28289,6 @@ function XlsxGrid({
28065
28289
  }
28066
28290
  gridKeyboardHandlerRef.current = handleGridKeyDown;
28067
28291
  const scrollerViewportProps = {
28068
- key: activeTabIndex,
28069
28292
  ref: scrollRef,
28070
28293
  "aria-colcount": Math.max(activeSheet?.colCount ?? 0, displayColLimit),
28071
28294
  "aria-keyshortcuts": "ArrowUp ArrowDown ArrowLeft ArrowRight Home End PageUp PageDown Control+Home Control+End",
@@ -28703,7 +28926,7 @@ function XlsxGrid({
28703
28926
  )
28704
28927
  }
28705
28928
  );
28706
- return /* @__PURE__ */ jsx3("div", { style: { backgroundColor: palette.canvas, display: "flex", flex: 1, minHeight: 0, minWidth: 0 }, children: renderScroller ? renderScroller({ children: scrollerContent, viewportProps: scrollerViewportProps }) : /* @__PURE__ */ jsx3("div", { ...scrollerViewportProps, children: scrollerContent }) });
28929
+ return /* @__PURE__ */ jsx3("div", { style: { backgroundColor: palette.canvas, display: "flex", flex: 1, minHeight: 0, minWidth: 0 }, children: renderScroller ? renderScroller({ children: scrollerContent, viewportProps: scrollerViewportProps }) : /* @__PURE__ */ jsx3("div", { ...scrollerViewportProps, children: scrollerContent }, activeTabIndex) });
28707
28930
  }
28708
28931
  function XlsxViewerInner({
28709
28932
  allowResizeInReadOnly = false,
@@ -28715,6 +28938,7 @@ function XlsxViewerInner({
28715
28938
  errorState,
28716
28939
  experimentalCanvas = true,
28717
28940
  fileTooLargeState,
28941
+ getCellStyle,
28718
28942
  height,
28719
28943
  isDark = false,
28720
28944
  loadingComponent,
@@ -28778,6 +29002,7 @@ function XlsxViewerInner({
28778
29002
  errorState,
28779
29003
  experimentalCanvas,
28780
29004
  fileTooLargeState,
29005
+ getCellStyle,
28781
29006
  loadingComponent,
28782
29007
  loadingState,
28783
29008
  palette,
@@ -28906,8 +29131,11 @@ function useXlsxViewerEditing() {
28906
29131
  selectedFormula,
28907
29132
  selectedValue,
28908
29133
  setCellFormula,
29134
+ setCellStyle,
28909
29135
  setCellValue,
29136
+ setRangeStyle,
28910
29137
  setSelectedCellFormula,
29138
+ setSelectedCellStyle,
28911
29139
  setSelectedCellValue,
28912
29140
  undo,
28913
29141
  unmergeSelection
@@ -28934,8 +29162,11 @@ function useXlsxViewerEditing() {
28934
29162
  selectedFormula,
28935
29163
  selectedValue,
28936
29164
  setCellFormula,
29165
+ setCellStyle,
28937
29166
  setCellValue,
29167
+ setRangeStyle,
28938
29168
  setSelectedCellFormula,
29169
+ setSelectedCellStyle,
28939
29170
  setSelectedCellValue,
28940
29171
  undo,
28941
29172
  unmergeSelection
@@ -28961,8 +29192,11 @@ function useXlsxViewerEditing() {
28961
29192
  selectedFormula,
28962
29193
  selectedValue,
28963
29194
  setCellFormula,
29195
+ setCellStyle,
28964
29196
  setCellValue,
29197
+ setRangeStyle,
28965
29198
  setSelectedCellFormula,
29199
+ setSelectedCellStyle,
28966
29200
  setSelectedCellValue,
28967
29201
  undo,
28968
29202
  unmergeSelection