@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.cjs CHANGED
@@ -6473,7 +6473,7 @@ function getSheetsWasmModule() {
6473
6473
  if (!wasmModulePromise) {
6474
6474
  wasmModulePromise = import("@dukelib/sheets-wasm").then(async (mod) => {
6475
6475
  if (configuredWasmSource !== void 0) {
6476
- await mod.default(configuredWasmSource);
6476
+ await mod.default({ module_or_path: configuredWasmSource });
6477
6477
  } else {
6478
6478
  await mod.default();
6479
6479
  }
@@ -7212,6 +7212,22 @@ function pushHistoryEntry(stack, entry) {
7212
7212
  function normalizeCellValue(value) {
7213
7213
  return value ?? "";
7214
7214
  }
7215
+ function cloneCellStyle(style) {
7216
+ if (!style || typeof style !== "object") {
7217
+ return style;
7218
+ }
7219
+ if (typeof structuredClone === "function") {
7220
+ try {
7221
+ return structuredClone(style);
7222
+ } catch {
7223
+ }
7224
+ }
7225
+ try {
7226
+ return JSON.parse(JSON.stringify(style));
7227
+ } catch {
7228
+ return style;
7229
+ }
7230
+ }
7215
7231
  function coerceUserEnteredValue(value) {
7216
7232
  const trimmed = value.trim();
7217
7233
  if (!trimmed) {
@@ -7234,9 +7250,12 @@ function coerceUserEnteredValue(value) {
7234
7250
  function applyCellMutationState(worksheet, cell, state) {
7235
7251
  if (state.formula) {
7236
7252
  worksheet.setFormula(cellAddressToA1(cell), state.formula);
7237
- return;
7253
+ } else {
7254
+ worksheet.setCell(cellAddressToA1(cell), normalizeCellValue(state.value));
7255
+ }
7256
+ if (state.style && typeof state.style === "object") {
7257
+ worksheet.setCellStyleAt(cell.row, cell.col, state.style);
7238
7258
  }
7239
- worksheet.setCell(cellAddressToA1(cell), normalizeCellValue(state.value));
7240
7259
  }
7241
7260
  function escapeHtml(value) {
7242
7261
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
@@ -7968,7 +7987,11 @@ function useXlsxViewerController(options) {
7968
7987
  return false;
7969
7988
  }, []);
7970
7989
  const ensureChartAssetsHydrated = React.useCallback((targetWorkbook, targetSheets) => {
7971
- if (chartAssetsRef.current || !targetWorkbook || !imageAssetsRef.current) {
7990
+ const currentAssets = chartAssetsRef.current;
7991
+ if (currentAssets && (currentAssets.chartOriginsById.size > 0 || !targetWorkbook || !imageAssetsRef.current)) {
7992
+ return currentAssets;
7993
+ }
7994
+ if (!targetWorkbook || !imageAssetsRef.current) {
7972
7995
  return chartAssetsRef.current;
7973
7996
  }
7974
7997
  const assets = loadWorkbookChartAssets(
@@ -8943,6 +8966,7 @@ function useXlsxViewerController(options) {
8943
8966
  }
8944
8967
  return {
8945
8968
  formula: worksheet.getFormulaAt(cell.row, cell.col) ?? null,
8969
+ style: worksheet.getCellStyleAt(cell.row, cell.col),
8946
8970
  value: worksheet.getCellAt(cell.row, cell.col).toJs()
8947
8971
  };
8948
8972
  }, [getActiveWorksheet]);
@@ -9087,6 +9111,7 @@ function useXlsxViewerController(options) {
9087
9111
  for (let col = startCol; col <= endCol; col += 1) {
9088
9112
  cells.push({
9089
9113
  formula: worksheet.getFormulaAt(row, col) ?? null,
9114
+ style: worksheet.getCellStyleAt(row, col),
9090
9115
  value: worksheet.getCellAt(row, col).toJs()
9091
9116
  });
9092
9117
  }
@@ -9253,11 +9278,27 @@ function useXlsxViewerController(options) {
9253
9278
  }, [getColumnWidthPx, getRowHeightPx]);
9254
9279
  const setChartRect = React.useCallback((id, rect) => {
9255
9280
  const hydratedChartAssets = ensureChartAssetsHydrated(workbook, sheets);
9281
+ console.info("[react-xlsx debug] setChartRect", {
9282
+ hasActiveSheet: Boolean(activeSheet),
9283
+ hasHydratedChartAssets: Boolean(hydratedChartAssets),
9284
+ hasImageAssets: Boolean(imageAssetsRef.current),
9285
+ hasWorkbook: Boolean(workbook),
9286
+ id,
9287
+ readOnly,
9288
+ rect
9289
+ });
9256
9290
  if (readOnly || !workbook || !activeSheet || !imageAssetsRef.current || !hydratedChartAssets) {
9257
9291
  return;
9258
9292
  }
9259
9293
  const worksheet = workbook.getSheet(activeSheet.workbookSheetIndex);
9260
9294
  const currentChart = getChartById(id);
9295
+ console.info("[react-xlsx debug] currentChart", {
9296
+ activeWorkbookSheetIndex: activeSheet.workbookSheetIndex,
9297
+ editable: currentChart?.editable,
9298
+ found: Boolean(currentChart),
9299
+ originCount: hydratedChartAssets.chartOriginsById.size,
9300
+ workbookSheetIndex: currentChart?.workbookSheetIndex
9301
+ });
9261
9302
  if (!currentChart || currentChart.editable === false || currentChart.workbookSheetIndex !== activeSheet.workbookSheetIndex) {
9262
9303
  return;
9263
9304
  }
@@ -9268,7 +9309,8 @@ function useXlsxViewerController(options) {
9268
9309
  getRowHeightPx: (row) => getRowHeightPx(worksheet, row)
9269
9310
  });
9270
9311
  recordHistoryBeforeMutation();
9271
- updateWorkbookChartAnchor(imageAssetsRef.current, hydratedChartAssets, id, nextAnchor);
9312
+ const didUpdateAnchor = updateWorkbookChartAnchor(imageAssetsRef.current, hydratedChartAssets, id, nextAnchor);
9313
+ console.info("[react-xlsx debug] updateWorkbookChartAnchor", { didUpdateAnchor, nextAnchor });
9272
9314
  hydratedChartAssets.chartsByWorkbookSheetIndex = hydratedChartAssets.chartsByWorkbookSheetIndex.map((sheetCharts) => sheetCharts.map((chart) => chart.id === id ? { ...chart, anchor: nextAnchor } : chart));
9273
9315
  setChartsByWorkbookSheetIndex((current) => current.map((sheetCharts) => sheetCharts.map((chart) => chart.id === id ? { ...chart, anchor: nextAnchor } : chart)));
9274
9316
  setRevision((current) => current + 1);
@@ -9484,8 +9526,12 @@ function useXlsxViewerController(options) {
9484
9526
  continue;
9485
9527
  }
9486
9528
  worksheet.setCell(cellAddressToA1({ row, col }), "");
9529
+ const after = captureCellMutationState(cell);
9530
+ if (!after) {
9531
+ continue;
9532
+ }
9487
9533
  mutations.push({
9488
- after: { formula: null, value: "" },
9534
+ after,
9489
9535
  before,
9490
9536
  cell
9491
9537
  });
@@ -9516,9 +9562,13 @@ function useXlsxViewerController(options) {
9516
9562
  }
9517
9563
  const nextValue = coerceUserEnteredValue(value);
9518
9564
  worksheet.setCell(cellAddressToA1(cell), nextValue);
9565
+ const after = captureCellMutationState(cell);
9566
+ if (!after) {
9567
+ return;
9568
+ }
9519
9569
  maybeRecalculateWorkbook(workbook);
9520
9570
  refreshWorkbookState(workbook);
9521
- recordCellEditHistory(cell, before, { formula: null, value: nextValue });
9571
+ recordCellEditHistory(cell, before, after);
9522
9572
  }, [captureCellMutationState, getActiveWorksheet, maybeRecalculateWorkbook, readOnly, recordCellEditHistory, refreshWorkbookState, workbook]);
9523
9573
  const setCellFormula = React.useCallback((cell, formula) => {
9524
9574
  const worksheet = getActiveWorksheet();
@@ -9535,13 +9585,31 @@ function useXlsxViewerController(options) {
9535
9585
  } else {
9536
9586
  worksheet.setFormula(cellAddressToA1(cell), formula);
9537
9587
  }
9588
+ const after = captureCellMutationState(cell);
9589
+ if (!after) {
9590
+ return;
9591
+ }
9538
9592
  maybeRecalculateWorkbook(workbook);
9539
9593
  refreshWorkbookState(workbook);
9540
- recordCellEditHistory(cell, before, {
9541
- formula: trimmedFormula || null,
9542
- value: trimmedFormula ? null : ""
9543
- });
9594
+ recordCellEditHistory(cell, before, after);
9544
9595
  }, [captureCellMutationState, getActiveWorksheet, maybeRecalculateWorkbook, readOnly, recordCellEditHistory, refreshWorkbookState, workbook]);
9596
+ const setCellStyle = React.useCallback((cell, style) => {
9597
+ const worksheet = getActiveWorksheet();
9598
+ if (readOnly || !worksheet || !workbook) {
9599
+ return;
9600
+ }
9601
+ const before = captureCellMutationState(cell);
9602
+ if (!before) {
9603
+ return;
9604
+ }
9605
+ worksheet.setCellStyleAt(cell.row, cell.col, style);
9606
+ const after = captureCellMutationState(cell);
9607
+ if (!after) {
9608
+ return;
9609
+ }
9610
+ refreshWorkbookState(workbook);
9611
+ recordCellEditHistory(cell, before, after);
9612
+ }, [captureCellMutationState, getActiveWorksheet, readOnly, recordCellEditHistory, refreshWorkbookState, workbook]);
9545
9613
  const setSelectedCellValue = React.useCallback((value) => {
9546
9614
  if (!activeCell) {
9547
9615
  return;
@@ -9554,6 +9622,51 @@ function useXlsxViewerController(options) {
9554
9622
  }
9555
9623
  setCellFormula(activeCell, formula);
9556
9624
  }, [activeCell, setCellFormula]);
9625
+ const setSelectedCellStyle = React.useCallback((style) => {
9626
+ if (!activeCell) {
9627
+ return;
9628
+ }
9629
+ setCellStyle(activeCell, style);
9630
+ }, [activeCell, setCellStyle]);
9631
+ const setRangeStyle = React.useCallback((range, style) => {
9632
+ const worksheet = getActiveWorksheet();
9633
+ if (readOnly || !worksheet || !workbook) {
9634
+ return;
9635
+ }
9636
+ const normalized = normalizeRange(range);
9637
+ const beforeStates = [];
9638
+ for (let row = normalized.start.row; row <= normalized.end.row; row += 1) {
9639
+ for (let col = normalized.start.col; col <= normalized.end.col; col += 1) {
9640
+ const cell = { row, col };
9641
+ const before = captureCellMutationState(cell);
9642
+ if (!before) {
9643
+ continue;
9644
+ }
9645
+ beforeStates.push({
9646
+ before,
9647
+ cell
9648
+ });
9649
+ }
9650
+ }
9651
+ if (beforeStates.length === 0) {
9652
+ return;
9653
+ }
9654
+ worksheet.setRangeStyle(rangeToA1(normalized), style);
9655
+ const mutations = [];
9656
+ for (const mutation of beforeStates) {
9657
+ const after = captureCellMutationState(mutation.cell);
9658
+ if (!after) {
9659
+ continue;
9660
+ }
9661
+ mutations.push({
9662
+ after,
9663
+ before: mutation.before,
9664
+ cell: mutation.cell
9665
+ });
9666
+ }
9667
+ refreshWorkbookState(workbook);
9668
+ recordRangeEditHistory(mutations, selection, activeCell);
9669
+ }, [activeCell, captureCellMutationState, getActiveWorksheet, readOnly, recordRangeEditHistory, refreshWorkbookState, selection, workbook]);
9557
9670
  const fillSelection = React.useCallback((targetRange) => {
9558
9671
  const worksheet = getActiveWorksheet();
9559
9672
  if (readOnly || !worksheet || !workbook || !selection) {
@@ -9580,19 +9693,22 @@ function useXlsxViewerController(options) {
9580
9693
  const sourceRow = sourceRange.start.row + (row - nextRange.start.row) % sourceHeight;
9581
9694
  const sourceCol = sourceRange.start.col + (col - nextRange.start.col) % sourceWidth;
9582
9695
  const sourceFormula = worksheet.getFormulaAt(sourceRow, sourceCol);
9696
+ const sourceStyle = cloneCellStyle(worksheet.getCellStyleAt(sourceRow, sourceCol));
9583
9697
  if (sourceFormula) {
9584
9698
  worksheet.setFormula(cellAddressToA1(targetCell), sourceFormula);
9585
- mutations.push({
9586
- after: { formula: sourceFormula, value: null },
9587
- before,
9588
- cell: targetCell
9589
- });
9699
+ } else {
9700
+ const sourceValue = normalizeCellValue(worksheet.getCellAt(sourceRow, sourceCol).toJs());
9701
+ worksheet.setCell(cellAddressToA1(targetCell), sourceValue);
9702
+ }
9703
+ if (sourceStyle && typeof sourceStyle === "object") {
9704
+ worksheet.setCellStyleAt(targetCell.row, targetCell.col, sourceStyle);
9705
+ }
9706
+ const after = captureCellMutationState(targetCell);
9707
+ if (!after) {
9590
9708
  continue;
9591
9709
  }
9592
- const sourceValue = normalizeCellValue(worksheet.getCellAt(sourceRow, sourceCol).toJs());
9593
- worksheet.setCell(cellAddressToA1(targetCell), sourceValue);
9594
9710
  mutations.push({
9595
- after: { formula: null, value: sourceValue },
9711
+ after,
9596
9712
  before,
9597
9713
  cell: targetCell
9598
9714
  });
@@ -9732,16 +9848,24 @@ function useXlsxViewerController(options) {
9732
9848
  }
9733
9849
  if (rawValue.startsWith("=") && rawValue.length > 1) {
9734
9850
  worksheet.setFormula(cellAddressToA1(nextCell), rawValue);
9851
+ const after = captureCellMutationState(nextCell);
9852
+ if (!after) {
9853
+ continue;
9854
+ }
9735
9855
  mutations.push({
9736
- after: { formula: rawValue, value: null },
9856
+ after,
9737
9857
  before,
9738
9858
  cell: nextCell
9739
9859
  });
9740
9860
  } else {
9741
9861
  const nextValue = coerceUserEnteredValue(rawValue);
9742
9862
  worksheet.setCell(cellAddressToA1(nextCell), nextValue);
9863
+ const after = captureCellMutationState(nextCell);
9864
+ if (!after) {
9865
+ continue;
9866
+ }
9743
9867
  mutations.push({
9744
- after: { formula: null, value: nextValue },
9868
+ after,
9745
9869
  before,
9746
9870
  cell: nextCell
9747
9871
  });
@@ -9792,8 +9916,12 @@ function useXlsxViewerController(options) {
9792
9916
  if (cell.formula) {
9793
9917
  worksheet.setFormula(cellAddressToA1(nextCell), cell.formula);
9794
9918
  if (before) {
9919
+ const after = captureCellMutationState(nextCell);
9920
+ if (!after) {
9921
+ continue;
9922
+ }
9795
9923
  mutations.push({
9796
- after: { formula: cell.formula, value: null },
9924
+ after,
9797
9925
  before,
9798
9926
  cell: nextCell
9799
9927
  });
@@ -9801,8 +9929,12 @@ function useXlsxViewerController(options) {
9801
9929
  } else {
9802
9930
  worksheet.setCell(cellAddressToA1(nextCell), cell.value);
9803
9931
  if (before) {
9932
+ const after = captureCellMutationState(nextCell);
9933
+ if (!after) {
9934
+ continue;
9935
+ }
9804
9936
  mutations.push({
9805
- after: { formula: null, value: cell.value },
9937
+ after,
9806
9938
  before,
9807
9939
  cell: nextCell
9808
9940
  });
@@ -10019,7 +10151,9 @@ function useXlsxViewerController(options) {
10019
10151
  resizeColumn,
10020
10152
  resizeRow,
10021
10153
  setCellFormula,
10154
+ setCellStyle,
10022
10155
  setCellValue,
10156
+ setRangeStyle,
10023
10157
  setZoomScale,
10024
10158
  setChartRect,
10025
10159
  setImageRect,
@@ -10038,6 +10172,7 @@ function useXlsxViewerController(options) {
10038
10172
  setActiveSheetIndex,
10039
10173
  setActiveTabIndex,
10040
10174
  setSelectedCellFormula,
10175
+ setSelectedCellStyle,
10041
10176
  setSelectedCellValue,
10042
10177
  sheets,
10043
10178
  shapes,
@@ -10121,7 +10256,9 @@ function useXlsxViewerController(options) {
10121
10256
  resizeColumn,
10122
10257
  resizeRow,
10123
10258
  setCellFormula,
10259
+ setCellStyle,
10124
10260
  setCellValue,
10261
+ setRangeStyle,
10125
10262
  setZoomScale,
10126
10263
  setChartRect,
10127
10264
  setImageRect,
@@ -10140,6 +10277,7 @@ function useXlsxViewerController(options) {
10140
10277
  setActiveSheetIndex,
10141
10278
  setActiveTabIndex,
10142
10279
  setSelectedCellFormula,
10280
+ setSelectedCellStyle,
10143
10281
  setSelectedCellValue,
10144
10282
  sheets,
10145
10283
  shapes,
@@ -16618,6 +16756,9 @@ var WHEEL_ZOOM_SENSITIVITY = 25e-5;
16618
16756
  var WHEEL_LINE_DELTA_PX = 16;
16619
16757
  var CHART_SOURCE_HIGHLIGHT_COLORS = ["#2563eb", "#dc2626", "#7c3aed", "#059669", "#ea580c", "#db2777"];
16620
16758
  var SHEET_SURFACE = "#ffffff";
16759
+ var DRAWING_SELECTION_STROKE = "#64748b";
16760
+ var DRAWING_SELECTION_HANDLE_FILL = "#ffffff";
16761
+ var DRAWING_SELECTION_HANDLE_SHADOW = "rgba(15, 23, 42, 0.18)";
16621
16762
  var DEFAULT_CELL_PADDING = "0 4px";
16622
16763
  var IMAGE_HANDLE_POSITIONS = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
16623
16764
  var CANVAS_CELL_STYLE_CACHE_LIMIT = 4096;
@@ -16703,6 +16844,38 @@ function measureCanvasTextWidth(context, text) {
16703
16844
  CANVAS_TEXT_MEASURE_CACHE_LIMIT
16704
16845
  );
16705
16846
  }
16847
+ function drawCanvasTextDecorations(context, {
16848
+ align,
16849
+ color,
16850
+ decoration,
16851
+ ellipsize = false,
16852
+ lineThroughY,
16853
+ maxWidth,
16854
+ text,
16855
+ textX,
16856
+ underlineY,
16857
+ zoomFactor
16858
+ }) {
16859
+ if (!decoration || text.length === 0) {
16860
+ return;
16861
+ }
16862
+ const measured = ellipsize && maxWidth !== void 0 ? Math.min(maxWidth, measureCanvasTextWidth(context, text)) : measureCanvasTextWidth(context, text);
16863
+ const startX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
16864
+ context.strokeStyle = color;
16865
+ context.lineWidth = Math.max(1, zoomFactor * 0.75);
16866
+ if (decoration.includes("underline")) {
16867
+ context.beginPath();
16868
+ context.moveTo(startX, underlineY);
16869
+ context.lineTo(startX + measured, underlineY);
16870
+ context.stroke();
16871
+ }
16872
+ if (decoration.includes("line-through")) {
16873
+ context.beginPath();
16874
+ context.moveTo(startX, lineThroughY);
16875
+ context.lineTo(startX + measured, lineThroughY);
16876
+ context.stroke();
16877
+ }
16878
+ }
16706
16879
  function getCachedCanvasPath2D(path) {
16707
16880
  if (typeof Path2D === "undefined") {
16708
16881
  return null;
@@ -18748,7 +18921,9 @@ function resolveImageHandleStyle(position, stroke, surface, scale = 1) {
18748
18921
  const style = {
18749
18922
  backgroundColor: surface,
18750
18923
  border: `${Math.max(1, scale)}px solid ${stroke}`,
18751
- borderRadius: 6 * scale,
18924
+ borderRadius: 3 * scale,
18925
+ boxShadow: `0 1px ${3 * scale}px ${DRAWING_SELECTION_HANDLE_SHADOW}`,
18926
+ boxSizing: "border-box",
18752
18927
  cursor: IMAGE_HANDLE_CURSOR[position],
18753
18928
  height: handleSize,
18754
18929
  pointerEvents: "auto",
@@ -21545,6 +21720,7 @@ function XlsxGrid({
21545
21720
  enableCanvasSelectionAnimation = true,
21546
21721
  errorState,
21547
21722
  fileTooLargeState,
21723
+ getCellStyle,
21548
21724
  loadingComponent,
21549
21725
  loadingState,
21550
21726
  renderChartLoading,
@@ -23504,7 +23680,7 @@ function XlsxGrid({
23504
23680
  const viewportRowBatch = getRowsBatchAsync ? asyncViewportRowBatch : syncViewportRowBatch;
23505
23681
  React4.useEffect(() => {
23506
23682
  cellRenderCacheRef.current.clear();
23507
- }, [activeSheetIndex, displayColLimit, displayRowLimit, palette, revision, viewportRowBatch, worksheet, zoomFactor]);
23683
+ }, [activeSheetIndex, displayColLimit, displayRowLimit, getCellStyle, palette, revision, viewportRowBatch, worksheet, zoomFactor]);
23508
23684
  React4.useEffect(() => {
23509
23685
  setAsyncViewportRowBatch(null);
23510
23686
  }, [activeSheetIndex, revision]);
@@ -23629,6 +23805,29 @@ function XlsxGrid({
23629
23805
  validation: resolveCellDataValidation(row, col, activeSheet),
23630
23806
  value: sparkline ? "" : checkboxState !== null ? "" : batchCoversRow || !worksheet ? batchedCell?.value ?? "" : getCellDisplayValue(worksheet, row, col, activeSheet)
23631
23807
  };
23808
+ if (getCellStyle) {
23809
+ const styleOverrides = getCellStyle({
23810
+ cell: { row, col },
23811
+ hasChartHighlight: Boolean(nextData.chartHighlight),
23812
+ hasConditionalFormat: Boolean(
23813
+ nextData.conditionalColorScale || nextData.conditionalDataBar || nextData.conditionalIcon
23814
+ ),
23815
+ hasHyperlink: Boolean(nextData.hyperlink),
23816
+ hasValidation: Boolean(nextData.validation),
23817
+ isMerged: Boolean(nextData.colSpan || nextData.rowSpan),
23818
+ isTableHeader: Boolean(nextData.isTableHeader),
23819
+ resolvedStyle: { ...nextData.style },
23820
+ sheetName: activeSheet?.name ?? "",
23821
+ value: nextData.value,
23822
+ workbookSheetIndex: activeSheet?.workbookSheetIndex ?? -1
23823
+ });
23824
+ if (styleOverrides) {
23825
+ nextData.style = { ...nextData.style, ...styleOverrides };
23826
+ if (nextData.conditionalColorScale && (styleOverrides.backgroundColor !== void 0 || styleOverrides.background !== void 0)) {
23827
+ nextData.conditionalColorScale = null;
23828
+ }
23829
+ }
23830
+ }
23632
23831
  nextData.canvas = buildCanvasCellStyleCache(nextData.style);
23633
23832
  if (canCellTextOverflow(nextData)) {
23634
23833
  const startColIndex = colIndexByActual.get(col);
@@ -23714,6 +23913,7 @@ function XlsxGrid({
23714
23913
  displayDefaultColWidth,
23715
23914
  displayEffectiveColWidths,
23716
23915
  effectiveTables,
23916
+ getCellStyle,
23717
23917
  palette,
23718
23918
  sparklinesByCell,
23719
23919
  viewportRowBatch,
@@ -26238,16 +26438,17 @@ function XlsxGrid({
26238
26438
  wrappedLines.forEach((line, lineIndex) => {
26239
26439
  const textY = textBlockTop + lineIndex * lineHeight + lineHeight / 2;
26240
26440
  paneContext.fillText(line, textX, textY);
26241
- if (canvasCellStyle.textDecoration?.includes("underline") && line.length > 0) {
26242
- const measured = Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, line));
26243
- const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
26244
- paneContext.beginPath();
26245
- paneContext.moveTo(underlineStartX, textY + Math.max(2, lineHeight * 0.24));
26246
- paneContext.lineTo(underlineStartX + measured, textY + Math.max(2, lineHeight * 0.24));
26247
- paneContext.strokeStyle = textColor;
26248
- paneContext.lineWidth = Math.max(1, zoomFactor * 0.75);
26249
- paneContext.stroke();
26250
- }
26441
+ drawCanvasTextDecorations(paneContext, {
26442
+ align,
26443
+ color: textColor,
26444
+ decoration: canvasCellStyle.textDecoration,
26445
+ lineThroughY: textY,
26446
+ maxWidth: maxTextWidth,
26447
+ text: line,
26448
+ textX,
26449
+ underlineY: textY + Math.max(2, lineHeight * 0.24),
26450
+ zoomFactor
26451
+ });
26251
26452
  });
26252
26453
  } else if (spillMaxWidth != null) {
26253
26454
  const text = shouldEllipsizeText ? truncateCanvasText(paneContext, rawText, maxTextWidth) : rawText;
@@ -26266,22 +26467,25 @@ function XlsxGrid({
26266
26467
  textDecoration: canvasCellStyle.textDecoration,
26267
26468
  textX,
26268
26469
  textY,
26470
+ lineThroughY: textY,
26269
26471
  underlineY: textY + 6 * zoomFactor
26270
26472
  });
26271
26473
  } else {
26272
26474
  const text = cellData.shrinkToFit ? rawText : shouldEllipsizeText ? truncateCanvasText(paneContext, rawText, maxTextWidth) : rawText;
26273
26475
  const textY = contentTop + contentHeight / 2;
26274
26476
  paneContext.fillText(text, textX, textY);
26275
- if (canvasCellStyle.textDecoration?.includes("underline") && text.length > 0) {
26276
- const measured = shouldEllipsizeText ? Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, text)) : measureCanvasTextWidth(paneContext, text);
26277
- const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
26278
- paneContext.beginPath();
26279
- paneContext.moveTo(underlineStartX, textY + 6 * zoomFactor);
26280
- paneContext.lineTo(underlineStartX + measured, textY + 6 * zoomFactor);
26281
- paneContext.strokeStyle = textColor;
26282
- paneContext.lineWidth = Math.max(1, zoomFactor * 0.75);
26283
- paneContext.stroke();
26284
- }
26477
+ drawCanvasTextDecorations(paneContext, {
26478
+ align,
26479
+ color: textColor,
26480
+ decoration: canvasCellStyle.textDecoration,
26481
+ ellipsize: shouldEllipsizeText,
26482
+ lineThroughY: textY,
26483
+ maxWidth: maxTextWidth,
26484
+ text,
26485
+ textX,
26486
+ underlineY: textY + 6 * zoomFactor,
26487
+ zoomFactor
26488
+ });
26285
26489
  }
26286
26490
  }
26287
26491
  if (cellData.conditionalIcon) {
@@ -26315,16 +26519,18 @@ function XlsxGrid({
26315
26519
  paneContext.textAlign = spillText.align;
26316
26520
  paneContext.textBaseline = "middle";
26317
26521
  paneContext.fillText(spillText.text, spillText.textX, spillText.textY);
26318
- if (spillText.textDecoration?.includes("underline") && spillText.text.length > 0) {
26319
- const measured = spillText.ellipsize ? Math.min(spillText.maxWidth, measureCanvasTextWidth(paneContext, spillText.text)) : measureCanvasTextWidth(paneContext, spillText.text);
26320
- const underlineStartX = spillText.align === "right" ? spillText.textX - measured : spillText.align === "center" ? spillText.textX - measured / 2 : spillText.textX;
26321
- paneContext.beginPath();
26322
- paneContext.moveTo(underlineStartX, spillText.underlineY);
26323
- paneContext.lineTo(underlineStartX + measured, spillText.underlineY);
26324
- paneContext.strokeStyle = spillText.color;
26325
- paneContext.lineWidth = Math.max(1, zoomFactor * 0.75);
26326
- paneContext.stroke();
26327
- }
26522
+ drawCanvasTextDecorations(paneContext, {
26523
+ align: spillText.align,
26524
+ color: spillText.color,
26525
+ decoration: spillText.textDecoration,
26526
+ ellipsize: spillText.ellipsize,
26527
+ lineThroughY: spillText.lineThroughY,
26528
+ maxWidth: spillText.maxWidth,
26529
+ text: spillText.text,
26530
+ textX: spillText.textX,
26531
+ underlineY: spillText.underlineY,
26532
+ zoomFactor
26533
+ });
26328
26534
  paneContext.restore();
26329
26535
  }
26330
26536
  }
@@ -27233,6 +27439,7 @@ function XlsxGrid({
27233
27439
  }
27234
27440
  const isFrozenDrawing = pane !== "scroll";
27235
27441
  const canEditImage = !readOnly && image.editable !== false;
27442
+ const drawingSelectionSurface = paletteIsDark(palette) ? palette.canvas : DRAWING_SELECTION_HANDLE_FILL;
27236
27443
  const style = {
27237
27444
  contain: "layout paint",
27238
27445
  height: rect.height,
@@ -27264,6 +27471,7 @@ function XlsxGrid({
27264
27471
  {
27265
27472
  style: {
27266
27473
  ...style,
27474
+ contain: "layout",
27267
27475
  overflow: "visible",
27268
27476
  pointerEvents: "none",
27269
27477
  zIndex: isFrozenDrawing ? image.zIndex + 22 : image.zIndex + 2
@@ -27273,8 +27481,8 @@ function XlsxGrid({
27273
27481
  "div",
27274
27482
  {
27275
27483
  style: {
27276
- border: `${Math.max(1, zoomFactor)}px solid ${selectionStroke}`,
27277
- boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${palette.surface}`,
27484
+ border: `${Math.max(1, zoomFactor)}px solid ${DRAWING_SELECTION_STROKE}`,
27485
+ boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${drawingSelectionSurface}`,
27278
27486
  boxSizing: "border-box",
27279
27487
  inset: 0,
27280
27488
  pointerEvents: "none",
@@ -27284,7 +27492,7 @@ function XlsxGrid({
27284
27492
  "div",
27285
27493
  {
27286
27494
  onPointerDown: (event) => startImageResize(event, image, rect, position),
27287
- style: resolveImageHandleStyle(position, selectionStroke, palette.surface, zoomFactor)
27495
+ style: resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor)
27288
27496
  },
27289
27497
  position
27290
27498
  )) : null
@@ -27296,7 +27504,7 @@ function XlsxGrid({
27296
27504
  startImageResize(event, image, rect, position);
27297
27505
  }
27298
27506
  },
27299
- style: canEditImage ? resolveImageHandleStyle(position, selectionStroke, palette.surface, zoomFactor) : { ...resolveImageHandleStyle(position, selectionStroke, palette.surface, zoomFactor), display: "none" }
27507
+ style: canEditImage ? resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor) : { ...resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor), display: "none" }
27300
27508
  }),
27301
27509
  image,
27302
27510
  rect
@@ -27304,8 +27512,8 @@ function XlsxGrid({
27304
27512
  "div",
27305
27513
  {
27306
27514
  style: {
27307
- border: `${Math.max(1, zoomFactor)}px solid ${selectionStroke}`,
27308
- boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${palette.surface}`,
27515
+ border: `${Math.max(1, zoomFactor)}px solid ${DRAWING_SELECTION_STROKE}`,
27516
+ boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${drawingSelectionSurface}`,
27309
27517
  boxSizing: "border-box",
27310
27518
  inset: 0,
27311
27519
  pointerEvents: "none",
@@ -27315,7 +27523,7 @@ function XlsxGrid({
27315
27523
  "div",
27316
27524
  {
27317
27525
  onPointerDown: (event) => startImageResize(event, image, rect, position),
27318
- style: resolveImageHandleStyle(position, selectionStroke, palette.surface, zoomFactor)
27526
+ style: resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor)
27319
27527
  },
27320
27528
  position
27321
27529
  )) : null
@@ -27353,6 +27561,7 @@ function XlsxGrid({
27353
27561
  }
27354
27562
  const isFrozenDrawing = pane !== "scroll";
27355
27563
  const canEditChart = !readOnly && chart.editable !== false;
27564
+ const drawingSelectionSurface = paletteIsDark(palette) ? palette.canvas : DRAWING_SELECTION_HANDLE_FILL;
27356
27565
  const style = {
27357
27566
  contain: "layout paint",
27358
27567
  height: rect.height,
@@ -27369,6 +27578,7 @@ function XlsxGrid({
27369
27578
  {
27370
27579
  style: {
27371
27580
  ...style,
27581
+ contain: "layout",
27372
27582
  overflow: "visible",
27373
27583
  pointerEvents: "none",
27374
27584
  zIndex: isFrozenDrawing ? chart.zIndex + 22 : chart.zIndex + 2
@@ -27377,8 +27587,8 @@ function XlsxGrid({
27377
27587
  "div",
27378
27588
  {
27379
27589
  style: {
27380
- border: `${Math.max(1, zoomFactor)}px solid ${selectionStroke}`,
27381
- boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${palette.surface}`,
27590
+ border: `${Math.max(1, zoomFactor)}px solid ${DRAWING_SELECTION_STROKE}`,
27591
+ boxShadow: `0 0 0 ${Math.max(1, zoomFactor)}px ${drawingSelectionSurface}`,
27382
27592
  boxSizing: "border-box",
27383
27593
  inset: 0,
27384
27594
  pointerEvents: "none",
@@ -27388,7 +27598,7 @@ function XlsxGrid({
27388
27598
  "div",
27389
27599
  {
27390
27600
  onPointerDown: (event) => startChartResize(event, chart, rect, position),
27391
- style: resolveImageHandleStyle(position, selectionStroke, palette.surface, zoomFactor)
27601
+ style: resolveImageHandleStyle(position, DRAWING_SELECTION_STROKE, drawingSelectionSurface, zoomFactor)
27392
27602
  },
27393
27603
  position
27394
27604
  )) : null
@@ -27668,6 +27878,22 @@ function XlsxGrid({
27668
27878
  }
27669
27879
  function installImageInteractionListeners(pointerId) {
27670
27880
  imageInteractionCleanupRef.current?.();
27881
+ const resolveInteractionRect = (interaction, clientX, clientY) => {
27882
+ const deltaX = clientX - interaction.startClientX;
27883
+ const deltaY = clientY - interaction.startClientY;
27884
+ return clampImageRect(
27885
+ interaction.type === "move" ? {
27886
+ ...interaction.baseRect,
27887
+ left: interaction.baseRect.left + deltaX,
27888
+ top: interaction.baseRect.top + deltaY
27889
+ } : resizeImageRect(interaction.baseRect, interaction.handle, deltaX, deltaY, displayImageMinSize),
27890
+ {
27891
+ contentOffsetLeft: displayRowHeaderWidth,
27892
+ contentOffsetTop: displayHeaderHeight,
27893
+ minSizePx: displayImageMinSize
27894
+ }
27895
+ );
27896
+ };
27671
27897
  const handlePointerMove = (event) => {
27672
27898
  if (event.pointerId !== pointerId) {
27673
27899
  return;
@@ -27681,18 +27907,7 @@ function XlsxGrid({
27681
27907
  if (!interaction.didMove && (Math.abs(deltaX) > 3 || Math.abs(deltaY) > 3)) {
27682
27908
  interaction.didMove = true;
27683
27909
  }
27684
- const nextRect = clampImageRect(
27685
- interaction.type === "move" ? {
27686
- ...interaction.baseRect,
27687
- left: interaction.baseRect.left + deltaX,
27688
- top: interaction.baseRect.top + deltaY
27689
- } : resizeImageRect(interaction.baseRect, interaction.handle, deltaX, deltaY, displayImageMinSize),
27690
- {
27691
- contentOffsetLeft: displayRowHeaderWidth,
27692
- contentOffsetTop: displayHeaderHeight,
27693
- minSizePx: displayImageMinSize
27694
- }
27695
- );
27910
+ const nextRect = resolveInteractionRect(interaction, event.clientX, event.clientY);
27696
27911
  scheduleImagePreviewRect({ id: interaction.imageId, rect: nextRect });
27697
27912
  };
27698
27913
  const cleanup = () => {
@@ -27715,18 +27930,20 @@ function XlsxGrid({
27715
27930
  imagePreviewRectRef.current = pendingPreview;
27716
27931
  setImagePreviewRect(pendingPreview);
27717
27932
  }
27718
- const preview = pendingPreview ?? imagePreviewRectRef.current;
27933
+ const finalRect = interaction ? resolveInteractionRect(interaction, event.clientX, event.clientY) : null;
27719
27934
  imageInteractionRef.current = null;
27720
27935
  imageInteractionCleanupRef.current = null;
27721
27936
  setInteractionMode("idle");
27722
27937
  document.body.style.cursor = "";
27723
27938
  document.body.style.userSelect = "";
27724
27939
  cleanup();
27725
- if (interaction && preview && preview.id === interaction.imageId) {
27940
+ if (interaction) {
27726
27941
  if (interaction.didMove) {
27727
27942
  skipNextImageClickRef.current = interaction.imageId;
27728
27943
  }
27729
- setImageRect(interaction.imageId, toLogicalRect(preview.rect));
27944
+ if (interaction.didMove && finalRect) {
27945
+ setImageRect(interaction.imageId, toLogicalRect(finalRect));
27946
+ }
27730
27947
  }
27731
27948
  imagePreviewRectRef.current = null;
27732
27949
  setImagePreviewRect(null);
@@ -27738,6 +27955,22 @@ function XlsxGrid({
27738
27955
  }
27739
27956
  function installChartInteractionListeners(pointerId) {
27740
27957
  chartInteractionCleanupRef.current?.();
27958
+ const resolveInteractionRect = (interaction, clientX, clientY) => {
27959
+ const deltaX = clientX - interaction.startClientX;
27960
+ const deltaY = clientY - interaction.startClientY;
27961
+ return clampImageRect(
27962
+ interaction.type === "move" ? {
27963
+ ...interaction.baseRect,
27964
+ left: interaction.baseRect.left + deltaX,
27965
+ top: interaction.baseRect.top + deltaY
27966
+ } : resizeImageRect(interaction.baseRect, interaction.handle, deltaX, deltaY, 48 * zoomFactor),
27967
+ {
27968
+ contentOffsetLeft: displayRowHeaderWidth,
27969
+ contentOffsetTop: displayHeaderHeight,
27970
+ minSizePx: 48 * zoomFactor
27971
+ }
27972
+ );
27973
+ };
27741
27974
  const handlePointerMove = (event) => {
27742
27975
  if (event.pointerId !== pointerId) {
27743
27976
  return;
@@ -27751,18 +27984,7 @@ function XlsxGrid({
27751
27984
  if (!interaction.didMove && (Math.abs(deltaX) > 3 || Math.abs(deltaY) > 3)) {
27752
27985
  interaction.didMove = true;
27753
27986
  }
27754
- const nextRect = clampImageRect(
27755
- interaction.type === "move" ? {
27756
- ...interaction.baseRect,
27757
- left: interaction.baseRect.left + deltaX,
27758
- top: interaction.baseRect.top + deltaY
27759
- } : resizeImageRect(interaction.baseRect, interaction.handle, deltaX, deltaY, 48 * zoomFactor),
27760
- {
27761
- contentOffsetLeft: displayRowHeaderWidth,
27762
- contentOffsetTop: displayHeaderHeight,
27763
- minSizePx: 48 * zoomFactor
27764
- }
27765
- );
27987
+ const nextRect = resolveInteractionRect(interaction, event.clientX, event.clientY);
27766
27988
  scheduleChartPreviewRect({ id: interaction.chartId, rect: nextRect });
27767
27989
  };
27768
27990
  const cleanup = () => {
@@ -27785,18 +28007,20 @@ function XlsxGrid({
27785
28007
  chartPreviewRectRef.current = pendingPreview;
27786
28008
  setChartPreviewRect(pendingPreview);
27787
28009
  }
27788
- const preview = pendingPreview ?? chartPreviewRectRef.current;
28010
+ const finalRect = interaction ? resolveInteractionRect(interaction, event.clientX, event.clientY) : null;
27789
28011
  chartInteractionRef.current = null;
27790
28012
  chartInteractionCleanupRef.current = null;
27791
28013
  setInteractionMode("idle");
27792
28014
  document.body.style.cursor = "";
27793
28015
  document.body.style.userSelect = "";
27794
28016
  cleanup();
27795
- if (interaction && preview && preview.id === interaction.chartId) {
28017
+ if (interaction) {
27796
28018
  if (interaction.didMove) {
27797
28019
  skipNextChartClickRef.current = interaction.chartId;
27798
28020
  }
27799
- setChartRect(interaction.chartId, toLogicalRect(preview.rect));
28021
+ if (interaction.didMove && finalRect) {
28022
+ setChartRect(interaction.chartId, toLogicalRect(finalRect));
28023
+ }
27800
28024
  }
27801
28025
  chartPreviewRectRef.current = null;
27802
28026
  setChartPreviewRect(null);
@@ -28099,7 +28323,6 @@ function XlsxGrid({
28099
28323
  }
28100
28324
  gridKeyboardHandlerRef.current = handleGridKeyDown;
28101
28325
  const scrollerViewportProps = {
28102
- key: activeTabIndex,
28103
28326
  ref: scrollRef,
28104
28327
  "aria-colcount": Math.max(activeSheet?.colCount ?? 0, displayColLimit),
28105
28328
  "aria-keyshortcuts": "ArrowUp ArrowDown ArrowLeft ArrowRight Home End PageUp PageDown Control+Home Control+End",
@@ -28737,7 +28960,7 @@ function XlsxGrid({
28737
28960
  )
28738
28961
  }
28739
28962
  );
28740
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { backgroundColor: palette.canvas, display: "flex", flex: 1, minHeight: 0, minWidth: 0 }, children: renderScroller ? renderScroller({ children: scrollerContent, viewportProps: scrollerViewportProps }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ...scrollerViewportProps, children: scrollerContent }) });
28963
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { backgroundColor: palette.canvas, display: "flex", flex: 1, minHeight: 0, minWidth: 0 }, children: renderScroller ? renderScroller({ children: scrollerContent, viewportProps: scrollerViewportProps }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ...scrollerViewportProps, children: scrollerContent }, activeTabIndex) });
28741
28964
  }
28742
28965
  function XlsxViewerInner({
28743
28966
  allowResizeInReadOnly = false,
@@ -28749,6 +28972,7 @@ function XlsxViewerInner({
28749
28972
  errorState,
28750
28973
  experimentalCanvas = true,
28751
28974
  fileTooLargeState,
28975
+ getCellStyle,
28752
28976
  height,
28753
28977
  isDark = false,
28754
28978
  loadingComponent,
@@ -28812,6 +29036,7 @@ function XlsxViewerInner({
28812
29036
  errorState,
28813
29037
  experimentalCanvas,
28814
29038
  fileTooLargeState,
29039
+ getCellStyle,
28815
29040
  loadingComponent,
28816
29041
  loadingState,
28817
29042
  palette,
@@ -28940,8 +29165,11 @@ function useXlsxViewerEditing() {
28940
29165
  selectedFormula,
28941
29166
  selectedValue,
28942
29167
  setCellFormula,
29168
+ setCellStyle,
28943
29169
  setCellValue,
29170
+ setRangeStyle,
28944
29171
  setSelectedCellFormula,
29172
+ setSelectedCellStyle,
28945
29173
  setSelectedCellValue,
28946
29174
  undo,
28947
29175
  unmergeSelection
@@ -28968,8 +29196,11 @@ function useXlsxViewerEditing() {
28968
29196
  selectedFormula,
28969
29197
  selectedValue,
28970
29198
  setCellFormula,
29199
+ setCellStyle,
28971
29200
  setCellValue,
29201
+ setRangeStyle,
28972
29202
  setSelectedCellFormula,
29203
+ setSelectedCellStyle,
28973
29204
  setSelectedCellValue,
28974
29205
  undo,
28975
29206
  unmergeSelection
@@ -28995,8 +29226,11 @@ function useXlsxViewerEditing() {
28995
29226
  selectedFormula,
28996
29227
  selectedValue,
28997
29228
  setCellFormula,
29229
+ setCellStyle,
28998
29230
  setCellValue,
29231
+ setRangeStyle,
28999
29232
  setSelectedCellFormula,
29233
+ setSelectedCellStyle,
29000
29234
  setSelectedCellValue,
29001
29235
  undo,
29002
29236
  unmergeSelection