@extend-ai/react-xlsx 0.17.0 → 0.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -17,6 +17,7 @@ interface XlsxResolvedCellStyle {
17
17
  };
18
18
  fill?: Record<string, unknown>;
19
19
  font?: Record<string, unknown>;
20
+ tableStyleOverrides?: XlsxResolvedCellStyle;
20
21
  }
21
22
  interface XlsxTableStyleDefinition {
22
23
  [elementType: string]: XlsxResolvedCellStyle;
@@ -99,6 +100,7 @@ interface XlsxFreezePanes {
99
100
  }
100
101
  type XlsxSheetVisibility = "hidden" | "veryHidden" | "visible";
101
102
  interface XlsxSheetData {
103
+ cellStyleIds?: Record<string, number>;
102
104
  autoFilterRanges: XlsxCellRange[];
103
105
  cachedFormulaValues: Record<string, string>;
104
106
  colWidthOverridesPx: Record<number, number>;
package/dist/index.d.ts CHANGED
@@ -17,6 +17,7 @@ interface XlsxResolvedCellStyle {
17
17
  };
18
18
  fill?: Record<string, unknown>;
19
19
  font?: Record<string, unknown>;
20
+ tableStyleOverrides?: XlsxResolvedCellStyle;
20
21
  }
21
22
  interface XlsxTableStyleDefinition {
22
23
  [elementType: string]: XlsxResolvedCellStyle;
@@ -99,6 +100,7 @@ interface XlsxFreezePanes {
99
100
  }
100
101
  type XlsxSheetVisibility = "hidden" | "veryHidden" | "visible";
101
102
  interface XlsxSheetData {
103
+ cellStyleIds?: Record<string, number>;
102
104
  autoFilterRanges: XlsxCellRange[];
103
105
  cachedFormulaValues: Record<string, string>;
104
106
  colWidthOverridesPx: Record<number, number>;
package/dist/index.js CHANGED
@@ -3581,7 +3581,7 @@ function resolveWorkbookColor(color, themePalette) {
3581
3581
  if (directHex) {
3582
3582
  return normalizeHexColor2(directHex);
3583
3583
  }
3584
- const themeValue = color.theme;
3584
+ const themeValue = color.theme ?? color.themeIndex;
3585
3585
  const numericTheme = typeof themeValue === "number" ? themeValue : typeof themeValue === "string" && themeValue.trim().length > 0 ? Number(themeValue) : Number.NaN;
3586
3586
  const themeColor = Number.isFinite(numericTheme) ? themePalette?.colorsByIndex[numericTheme] ?? null : null;
3587
3587
  if (!themeColor) {
@@ -4209,6 +4209,12 @@ function parseSpreadsheetFont(node) {
4209
4209
  return void 0;
4210
4210
  }
4211
4211
  const font = {};
4212
+ for (const [tag, property] of [["b", "bold"], ["i", "italic"], ["strike", "strikethrough"]]) {
4213
+ const flag = getFirstChild(node, tag);
4214
+ if (flag) {
4215
+ font[property] = hasEnabledSpreadsheetFlag(flag);
4216
+ }
4217
+ }
4212
4218
  const size = getFirstChild(node, "sz")?.getAttribute("val");
4213
4219
  const name = getFirstChild(node, "name")?.getAttribute("val");
4214
4220
  const family = getFirstChild(node, "family")?.getAttribute("val");
@@ -4216,15 +4222,6 @@ function parseSpreadsheetFont(node) {
4216
4222
  const charset = getFirstChild(node, "charset")?.getAttribute("val");
4217
4223
  const verticalAlign = getFirstChild(node, "vertAlign")?.getAttribute("val");
4218
4224
  const color = parseSpreadsheetColor(getFirstChild(node, "color"));
4219
- if (hasEnabledSpreadsheetFlag(getFirstChild(node, "b"))) {
4220
- font.bold = true;
4221
- }
4222
- if (hasEnabledSpreadsheetFlag(getFirstChild(node, "i"))) {
4223
- font.italic = true;
4224
- }
4225
- if (hasEnabledSpreadsheetFlag(getFirstChild(node, "strike"))) {
4226
- font.strikethrough = true;
4227
- }
4228
4225
  if (getFirstChild(node, "u")) {
4229
4226
  font.underline = getFirstChild(node, "u")?.getAttribute("val") ?? "single";
4230
4227
  }
@@ -4473,7 +4470,14 @@ function parseWorkbookStyles(archive) {
4473
4470
  const styleById = {};
4474
4471
  const tableStyleByName = {};
4475
4472
  getChildElements(cellXfsNode, "xf").forEach((xfNode, index) => {
4476
- styleById[index] = parseResolvedXfStyle(xfNode, fonts, fills, borders, checkboxComplementIndices);
4473
+ const style = parseResolvedXfStyle(xfNode, fonts, fills, borders, checkboxComplementIndices);
4474
+ const overrides = {};
4475
+ for (const component of ["font", "fill", "border"]) {
4476
+ if (Number(xfNode.getAttribute(`${component}Id`) ?? 0) > 0 && xfNode.getAttribute(`apply${component[0].toUpperCase()}${component.slice(1)}`) !== "0") {
4477
+ Object.assign(overrides, { [component]: style[component] });
4478
+ }
4479
+ }
4480
+ styleById[index] = { ...style, tableStyleOverrides: overrides };
4477
4481
  });
4478
4482
  getChildElements(cellStylesNode ?? document2.documentElement, "cellStyle").forEach((cellStyleNode) => {
4479
4483
  const name = cellStyleNode.getAttribute("name");
@@ -4817,6 +4821,7 @@ function parseSheetState(archive, path, options) {
4817
4821
  const rowHeightOverridesPx = {};
4818
4822
  const colWidthOverridesPx = {};
4819
4823
  const rowStyleIds = {};
4824
+ const cellStyleIds = {};
4820
4825
  const colStyleIds = {};
4821
4826
  let minContentCol = Number.POSITIVE_INFINITY;
4822
4827
  let minContentRow = Number.POSITIVE_INFINITY;
@@ -4866,6 +4871,10 @@ function parseSheetState(archive, path, options) {
4866
4871
  }
4867
4872
  getChildElements(rowNode, "c").forEach((cellNode) => {
4868
4873
  const cellRef = cellNode.getAttribute("r");
4874
+ const cellStyleId = Number(cellNode.getAttribute("s") ?? Number.NaN);
4875
+ if (cellRef && Number.isFinite(cellStyleId)) {
4876
+ cellStyleIds[cellRef] = cellStyleId;
4877
+ }
4869
4878
  if (isMeaningfulCellNode(cellNode)) {
4870
4879
  trackContentCell(cellRef);
4871
4880
  }
@@ -4904,6 +4913,7 @@ function parseSheetState(archive, path, options) {
4904
4913
  return {
4905
4914
  autoFilterRanges,
4906
4915
  cachedFormulaValues,
4916
+ cellStyleIds,
4907
4917
  columnWidthCharacterWidthPx,
4908
4918
  colWidthOverridesPx,
4909
4919
  colStyleIds,
@@ -6646,13 +6656,52 @@ function hasUnresolvedSheetReferences(workbook) {
6646
6656
  }
6647
6657
  return false;
6648
6658
  }
6659
+ function collectFormulaCellsWithSavedValues(workbook) {
6660
+ const positions = [];
6661
+ for (let sheetIdx = 0; sheetIdx < workbook.sheetCount; sheetIdx += 1) {
6662
+ let sheet;
6663
+ try {
6664
+ sheet = workbook.getSheet(sheetIdx);
6665
+ } catch {
6666
+ continue;
6667
+ }
6668
+ const cells = sheet.formulaCells;
6669
+ if (!Array.isArray(cells)) {
6670
+ continue;
6671
+ }
6672
+ for (const cell of cells) {
6673
+ if (typeof cell?.row !== "number" || typeof cell?.col !== "number") {
6674
+ continue;
6675
+ }
6676
+ let saved;
6677
+ try {
6678
+ saved = sheet.getCalculatedValueAt(cell.row, cell.col);
6679
+ } catch {
6680
+ continue;
6681
+ }
6682
+ if (!saved.is_empty && !saved.is_error) {
6683
+ positions.push({ sheetIdx, row: cell.row, col: cell.col });
6684
+ }
6685
+ }
6686
+ }
6687
+ return positions;
6688
+ }
6689
+ function anyBecameError(workbook, positions) {
6690
+ return positions.some(({ sheetIdx, row, col }) => {
6691
+ try {
6692
+ return workbook.getSheet(sheetIdx).getCalculatedValueAt(row, col).is_error;
6693
+ } catch {
6694
+ return false;
6695
+ }
6696
+ });
6697
+ }
6649
6698
  function safeCalculate(workbook, options = {}) {
6650
6699
  if (hasUnresolvedSheetReferences(workbook)) {
6651
6700
  return { workbook, calculated: false, skipReason: "unresolved-sheet-refs" };
6652
6701
  }
6702
+ const withSavedValues = options.reparse ? collectFormulaCellsWithSavedValues(workbook) : [];
6653
6703
  try {
6654
6704
  workbook.calculate();
6655
- return { workbook, calculated: true, skipReason: null };
6656
6705
  } catch (err) {
6657
6706
  console.warn("[react-xlsx] workbook.calculate() trapped; falling back to cached formula values", err);
6658
6707
  if (options.reparse) {
@@ -6664,6 +6713,14 @@ function safeCalculate(workbook, options = {}) {
6664
6713
  }
6665
6714
  return { workbook, calculated: false, skipReason: "calculate-trapped" };
6666
6715
  }
6716
+ if (options.reparse && anyBecameError(workbook, withSavedValues)) {
6717
+ try {
6718
+ return { workbook: options.reparse(), calculated: false, skipReason: "calculate-regressed" };
6719
+ } catch (reparseErr) {
6720
+ console.warn("[react-xlsx] workbook reparse after calculate regression failed", reparseErr);
6721
+ }
6722
+ }
6723
+ return { workbook, calculated: true, skipReason: null };
6667
6724
  }
6668
6725
  function tryRecalculate(workbook) {
6669
6726
  try {
@@ -7406,6 +7463,7 @@ function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette,
7406
7463
  colCount: 0,
7407
7464
  rowHeightOverridesPx: sheetState?.rowHeightOverridesPx ?? {},
7408
7465
  rowStyleIds: sheetState?.rowStyleIds ?? {},
7466
+ cellStyleIds: sheetState?.cellStyleIds ?? {},
7409
7467
  styleById: styleById ?? {},
7410
7468
  sparklines: sheetState?.sparklines ?? [],
7411
7469
  tableStyleByName: tableStyleByName ?? {},
@@ -7492,6 +7550,7 @@ function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette,
7492
7550
  namedCellStyleByName: namedCellStyleByName ?? {},
7493
7551
  rowHeightOverridesPx: sheetState?.rowHeightOverridesPx ?? {},
7494
7552
  rowStyleIds: sheetState?.rowStyleIds ?? {},
7553
+ cellStyleIds: sheetState?.cellStyleIds ?? {},
7495
7554
  showGridLines: sheetState?.showGridLines ?? true,
7496
7555
  styleById: styleById ?? {},
7497
7556
  sparklines: sheetState?.sparklines ?? [],
@@ -9955,7 +10014,7 @@ function useXlsxViewerController(options) {
9955
10014
  return;
9956
10015
  }
9957
10016
  const dataStartRow = targetTable.start.row + Math.max(targetTable.headerRowCount, 1);
9958
- const totalsRowOffset = targetTable.totalsRowShown ? Math.max(targetTable.totalsRowCount, 1) : 0;
10017
+ const totalsRowOffset = targetTable.totalsRowCount;
9959
10018
  const dataEndRow = targetTable.end.row - totalsRowOffset;
9960
10019
  const startCol = targetTable.start.col;
9961
10020
  const endCol = targetTable.end.col;
@@ -19136,7 +19195,8 @@ function applyCanvasBorderDash(context, style, width) {
19136
19195
  context.setLineDash([]);
19137
19196
  }
19138
19197
  function strokeCanvasBorderSide(context, side, rect, border) {
19139
- const halfWidth = border.width / 2;
19198
+ const strokeWidth = border.style === "double" ? border.width / 3 : border.width;
19199
+ const halfWidth = strokeWidth / 2;
19140
19200
  const left = rect.left;
19141
19201
  const right = rect.left + rect.width;
19142
19202
  const top = rect.top;
@@ -19162,8 +19222,8 @@ function strokeCanvasBorderSide(context, side, rect, border) {
19162
19222
  context.stroke();
19163
19223
  };
19164
19224
  if (border.style === "double") {
19165
- const inset = Math.max(1, border.width);
19166
- context.lineWidth = Math.max(1, border.width / 3);
19225
+ const inset = border.width - strokeWidth;
19226
+ context.lineWidth = strokeWidth;
19167
19227
  context.setLineDash([]);
19168
19228
  strokeLine(0);
19169
19229
  strokeLine(inset);
@@ -22076,6 +22136,43 @@ function mergeResolvedCellStyle(base, overlay, options) {
22076
22136
  }
22077
22137
  return nextStyle;
22078
22138
  }
22139
+ function resolveCellTableOverrides(rawStyle, sheet, row, col) {
22140
+ const styleId = sheet?.cellStyleIds?.[cellAddressToA12({ row, col })] ?? sheet?.rowStyleIds[row] ?? sheet?.colStyleIds[col];
22141
+ const declared = styleId === void 0 ? void 0 : sheet?.styleById[styleId]?.tableStyleOverrides;
22142
+ const overrides = {};
22143
+ const font = asRecord(rawStyle?.font);
22144
+ if (font) {
22145
+ const explicitFont = {};
22146
+ const fontDefaults = {
22147
+ name: "Calibri",
22148
+ size: 11,
22149
+ bold: false,
22150
+ italic: false,
22151
+ underline: "none",
22152
+ strikethrough: false,
22153
+ verticalAlign: "baseline"
22154
+ };
22155
+ for (const [key, value] of Object.entries(font)) {
22156
+ const isExplicit = declared ? Object.hasOwn(declared.font ?? {}, key) : key === "color" ? asRecord(value)?.colorType !== "auto" : value !== fontDefaults[key];
22157
+ if (isExplicit) {
22158
+ explicitFont[key] = value;
22159
+ }
22160
+ }
22161
+ overrides.font = explicitFont;
22162
+ }
22163
+ const fill = asRecord(rawStyle?.fill);
22164
+ if (fill && fill.fillType !== "none") {
22165
+ overrides.fill = fill;
22166
+ }
22167
+ const border = asRecord(rawStyle?.border);
22168
+ if (border) {
22169
+ overrides.border = Object.fromEntries(Object.entries(border).filter(([, edge]) => {
22170
+ const style = asRecord(edge)?.style;
22171
+ return typeof style === "string" && style !== "none";
22172
+ }));
22173
+ }
22174
+ return overrides;
22175
+ }
22079
22176
  function resolveBorderEdgePriority(edge) {
22080
22177
  const style = typeof edge?.style === "string" ? edge.style : "none";
22081
22178
  const stylePriority = {
@@ -22204,7 +22301,7 @@ function resolveTableCellStyle(table, row, col, activeSheet) {
22204
22301
  return null;
22205
22302
  }
22206
22303
  const headerRowCount = Math.max(table.headerRowCount, 1);
22207
- const totalsRowCount = table.totalsRowShown ? Math.max(table.totalsRowCount, 1) : 0;
22304
+ const totalsRowCount = table.totalsRowCount;
22208
22305
  const headerEndRow = table.start.row + headerRowCount - 1;
22209
22306
  const totalsStartRow = table.end.row - totalsRowCount + 1;
22210
22307
  const bodyStartRow = headerEndRow + 1;
@@ -26528,7 +26625,10 @@ function XlsxGrid({
26528
26625
  });
26529
26626
  const targetTable = getTableAtCell(effectiveTables, targetRow, targetCol);
26530
26627
  const targetTableStyle = resolveTableCellStyle(targetTable, targetRow, targetCol, activeSheet);
26531
- const resolvedStyle = mergeResolvedCellStyle(targetRawStyle, targetTableStyle);
26628
+ const resolvedStyle = targetTableStyle ? mergeResolvedCellStyle(
26629
+ mergeResolvedCellStyle(targetRawStyle, targetTableStyle),
26630
+ resolveCellTableOverrides(targetRawStyle, activeSheet, targetRow, targetCol)
26631
+ ) : targetRawStyle;
26532
26632
  cellBaseRawStyleCacheRef.current.set(targetCacheKey, resolvedStyle);
26533
26633
  return resolvedStyle;
26534
26634
  };
@@ -28899,6 +28999,14 @@ function XlsxGrid({
28899
28999
  hasPendingGridlinePath = false;
28900
29000
  };
28901
29001
  const drawnMergedAnchorKeys = /* @__PURE__ */ new Set();
29002
+ const deferredDoubleBorders = [];
29003
+ const drawCellBorder = (side, rect, border) => {
29004
+ if (border.style === "double") {
29005
+ deferredDoubleBorders.push({ side, rect, border });
29006
+ } else {
29007
+ strokeCanvasBorderSide(paneContext, side, rect, border);
29008
+ }
29009
+ };
28902
29010
  for (const rowItem of paneAxisItems.rows) {
28903
29011
  for (const colItem of paneAxisItems.cols) {
28904
29012
  const cell = { row: rowItem.actualRow, col: colItem.actualCol };
@@ -29045,16 +29153,24 @@ function XlsxGrid({
29045
29153
  flushPendingGridlines();
29046
29154
  }
29047
29155
  if (topBorder && drawRowIndex === 0) {
29048
- strokeCanvasBorderSide(paneContext, "top", localRect, topBorder);
29156
+ drawCellBorder("top", localRect, topBorder);
29049
29157
  }
29050
29158
  if (resolvedRightBorder) {
29051
- strokeCanvasBorderSide(paneContext, "right", localRect, resolvedRightBorder);
29159
+ if (resolvedRightBorder.style === "double" && resolvedRightBorder === rightNeighborLeftBorder) {
29160
+ drawCellBorder("left", { ...localRect, left: localRect.left + localRect.width }, resolvedRightBorder);
29161
+ } else {
29162
+ drawCellBorder("right", localRect, resolvedRightBorder);
29163
+ }
29052
29164
  }
29053
29165
  if (resolvedBottomBorder) {
29054
- strokeCanvasBorderSide(paneContext, "bottom", localRect, resolvedBottomBorder);
29166
+ if (resolvedBottomBorder.style === "double" && resolvedBottomBorder === bottomNeighborTopBorder) {
29167
+ drawCellBorder("top", { ...localRect, top: localRect.top + localRect.height }, resolvedBottomBorder);
29168
+ } else {
29169
+ drawCellBorder("bottom", localRect, resolvedBottomBorder);
29170
+ }
29055
29171
  }
29056
29172
  if (leftBorder && drawColIndex === 0) {
29057
- strokeCanvasBorderSide(paneContext, "left", localRect, leftBorder);
29173
+ drawCellBorder("left", localRect, leftBorder);
29058
29174
  }
29059
29175
  if (cellData.chartHighlight) {
29060
29176
  const highlightBorder = {
@@ -29327,6 +29443,9 @@ function XlsxGrid({
29327
29443
  }
29328
29444
  }
29329
29445
  flushPendingGridlines();
29446
+ for (const { side, rect, border } of deferredDoubleBorders) {
29447
+ strokeCanvasBorderSide(paneContext, side, rect, border);
29448
+ }
29330
29449
  }
29331
29450
  for (const pane of cellPaneOrder) {
29332
29451
  const paneContext = bodyContexts[pane];