@extend-ai/react-xlsx 0.14.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6193,7 +6193,7 @@ function parseWorkbookStructureAssetsFromArchive(archive, options) {
6193
6193
  workbookSheets
6194
6194
  };
6195
6195
  }
6196
- function parseWorkbookImageAssets(bytes, workbook) {
6196
+ function parseWorkbookImageAssets(bytes, workbookOrFormControls) {
6197
6197
  const archive = unzipSync(bytes);
6198
6198
  const {
6199
6199
  contentTypes,
@@ -6212,7 +6212,7 @@ function parseWorkbookImageAssets(bytes, workbook) {
6212
6212
  const shapesByWorkbookSheetIndex = [];
6213
6213
  const sheetOrigins = [];
6214
6214
  const imageOriginsById = /* @__PURE__ */ new Map();
6215
- const dukeFormControlsByWorkbookSheetIndex = collectWorkbookFormControls(workbook);
6215
+ const dukeFormControlsByWorkbookSheetIndex = Array.isArray(workbookOrFormControls) ? workbookOrFormControls : collectWorkbookFormControls(workbookOrFormControls);
6216
6216
  workbookSheets.forEach((sheet, workbookSheetIndex) => {
6217
6217
  const sheetRelationships = parseRelationships(archive, relsPathForDocument(sheet.path), sheet.path);
6218
6218
  const attachments = [];
@@ -8701,13 +8701,24 @@ function useXlsxViewerController(options) {
8701
8701
  if (!effectiveSkipXmlParsing && hasIncompleteWorkerChartSnapshot(snapshot)) {
8702
8702
  throw new Error("Worker chart payload incomplete");
8703
8703
  }
8704
+ const workerImageAssets = effectiveSkipXmlParsing ? null : parseWorkbookImageAssets(
8705
+ new Uint8Array(buffer),
8706
+ snapshot.formControlsByWorkbookSheetIndex
8707
+ );
8708
+ if (!isCurrent || abortController.signal.aborted) {
8709
+ revokeWorkbookImageAssets(workerImageAssets);
8710
+ return;
8711
+ }
8712
+ setImageAssets(workerImageAssets);
8704
8713
  setWorkbook(null);
8705
8714
  setSheets(snapshot.sheets);
8706
8715
  setChartsByWorkbookSheetIndex(snapshot.chartsByWorkbookSheetIndex);
8707
8716
  setChartsheets(snapshot.chartsheets);
8708
8717
  setTabs(snapshot.tabs);
8709
8718
  chartAssetsRef.current = null;
8710
- setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8719
+ if (!workerImageAssets) {
8720
+ setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8721
+ }
8711
8722
  setWorkerTablesByWorkbookSheetIndex(snapshot.tablesByWorkbookSheetIndex);
8712
8723
  setShouldAutoCalculate(false);
8713
8724
  setIsWorkerBacked(true);
@@ -9205,7 +9216,12 @@ function useXlsxViewerController(options) {
9205
9216
  const getFormControlItems = React.useCallback((controlIndex, sheetIndex = activeSheetIndex) => {
9206
9217
  const target = getFormControlWorksheet(sheetIndex);
9207
9218
  if (!workbook || !target) {
9208
- return [];
9219
+ const targetSheet = sheets[sheetIndex];
9220
+ if (!targetSheet) {
9221
+ return [];
9222
+ }
9223
+ const control2 = (formControlsByWorkbookSheetIndex[targetSheet.workbookSheetIndex] ?? []).find((entry) => entry.controlIndex === controlIndex);
9224
+ return control2?.items?.slice() ?? [];
9209
9225
  }
9210
9226
  const control = target.worksheet.formControls[controlIndex];
9211
9227
  const inputRange = control?.kind.kind === "listBox" || control?.kind.kind === "dropdown" ? control.kind.inputRange : void 0;
@@ -9223,7 +9239,7 @@ function useXlsxViewerController(options) {
9223
9239
  }
9224
9240
  }
9225
9241
  return items;
9226
- }, [activeSheetIndex, getFormControlWorksheet, workbook]);
9242
+ }, [activeSheetIndex, formControlsByWorkbookSheetIndex, getFormControlWorksheet, sheets, workbook]);
9227
9243
  const getSheetShapes = React.useCallback((sheetIndex = activeSheetIndex) => {
9228
9244
  const targetSheet = sheets[sheetIndex];
9229
9245
  if (!targetSheet) {
@@ -20775,6 +20791,40 @@ function getCellBooleanValue(worksheet, row, col) {
20775
20791
  }
20776
20792
  return null;
20777
20793
  }
20794
+ function getBatchedCellNumericValue(cell) {
20795
+ if (!cell) {
20796
+ return null;
20797
+ }
20798
+ const text = cell.value.trim();
20799
+ if (text.length === 0) {
20800
+ return null;
20801
+ }
20802
+ if (text.toLowerCase() === "true") {
20803
+ return 1;
20804
+ }
20805
+ if (text.toLowerCase() === "false") {
20806
+ return 0;
20807
+ }
20808
+ const value = Number(text.replace(/,/g, ""));
20809
+ return Number.isFinite(value) ? value : null;
20810
+ }
20811
+ function getBatchedCellBooleanValue(cell) {
20812
+ if (!cell) {
20813
+ return null;
20814
+ }
20815
+ const text = cell.value.trim().toLowerCase();
20816
+ if (text.length === 0) {
20817
+ return null;
20818
+ }
20819
+ if (text === "true") {
20820
+ return true;
20821
+ }
20822
+ if (text === "false") {
20823
+ return false;
20824
+ }
20825
+ const value = Number(text);
20826
+ return Number.isFinite(value) ? value !== 0 : null;
20827
+ }
20778
20828
  function parseA1RangeReference3(reference) {
20779
20829
  const [startRef, endRef = startRef] = reference.split(":");
20780
20830
  const start = parseA1CellReference3(startRef ?? "");
@@ -23028,6 +23078,7 @@ function XlsxGrid({
23028
23078
  onFormControlAction,
23029
23079
  onFormControlChange,
23030
23080
  renderChartLoading,
23081
+ renderFormControl,
23031
23082
  palette,
23032
23083
  renderImage,
23033
23084
  renderImageSelection,
@@ -25592,14 +25643,7 @@ function XlsxGrid({
25592
25643
  () => shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => shape.hyperlink) : shapeRects,
25593
25644
  [shapeRects, shouldBakeCanvasStaticDrawings]
25594
25645
  );
25595
- const bakedFormControlRects = React4.useMemo(
25596
- () => shouldBakeCanvasStaticDrawings ? formControlRects : [],
25597
- [formControlRects, shouldBakeCanvasStaticDrawings]
25598
- );
25599
- const domFormControlRects = React4.useMemo(
25600
- () => shouldBakeCanvasStaticDrawings ? [] : formControlRects,
25601
- [formControlRects, shouldBakeCanvasStaticDrawings]
25602
- );
25646
+ const domFormControlRects = formControlRects;
25603
25647
  const bakedImageRects = React4.useMemo(
25604
25648
  () => shouldBakeCanvasImages ? imageRects.filter(({ image }) => !image.hyperlink && selectedImageId !== image.id) : [],
25605
25649
  [imageRects, selectedImageId, shouldBakeCanvasImages]
@@ -25616,11 +25660,9 @@ function XlsxGrid({
25616
25660
  return "";
25617
25661
  }
25618
25662
  const shapeSignature = bakedShapeRects.map(({ rect, shape }) => `s:${shape.id}:${shape.zIndex}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
25619
- const controlSignature = bakedFormControlRects.map(({ control, rect }) => `f:${control.id}:${control.zIndex}:${control.state ?? Boolean(control.checked)}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
25620
25663
  const imageSignature = bakedImageRects.map(({ image, rect }) => `i:${image.id}:${image.src}:${image.zIndex}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
25621
- return `${revision}:${canvasImageLoadVersion}:${shapeSignature}:${controlSignature}:${imageSignature}`;
25664
+ return `${revision}:${canvasImageLoadVersion}:${shapeSignature}:${imageSignature}`;
25622
25665
  }, [
25623
- bakedFormControlRects,
25624
25666
  bakedImageRects,
25625
25667
  bakedShapeRects,
25626
25668
  canvasImageLoadVersion,
@@ -27155,12 +27197,6 @@ function XlsxGrid({
27155
27197
  shape,
27156
27198
  zIndex: shape.zIndex
27157
27199
  })),
27158
- ...bakedFormControlRects.map(({ control, rect }) => ({
27159
- control,
27160
- kind: "formControl",
27161
- rect,
27162
- zIndex: control.zIndex
27163
- })),
27164
27200
  ...bakedImageRects.map(({ image, rect }) => ({
27165
27201
  image,
27166
27202
  kind: "image",
@@ -27311,97 +27347,6 @@ function XlsxGrid({
27311
27347
  drawBakedShapeText(context, shape, localLeft, localTop, rect.width, rect.height);
27312
27348
  context.restore();
27313
27349
  };
27314
- const drawBakedFormControl = (context, control, rect) => {
27315
- const label = resolveFormControlLabel(control);
27316
- const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
27317
- const textColor = control.textColor ?? "#000000";
27318
- const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
27319
- const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
27320
- const controlChecked = isFormControlChecked(control);
27321
- const controlMixed = isFormControlMixed(control);
27322
- context.save();
27323
- context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
27324
- context.textBaseline = "middle";
27325
- context.fillStyle = textColor;
27326
- context.strokeStyle = stroke;
27327
- context.lineWidth = Math.max(1, zoomFactor);
27328
- if (control.kind === "group-box") {
27329
- const labelInset = label ? Math.max(7, fontSizePx * 0.5) : 0;
27330
- drawCanvasRoundedRect(context, rect.left, rect.top + labelInset, rect.width, Math.max(0, rect.height - labelInset), 2 * zoomFactor);
27331
- context.stroke();
27332
- if (label) {
27333
- context.fillStyle = SHEET_SURFACE;
27334
- const labelWidth = Math.min(rect.width - 16 * zoomFactor, measureCanvasTextWidth(context, label) + 8 * zoomFactor);
27335
- context.fillRect(rect.left + 8 * zoomFactor, rect.top, labelWidth, fontSizePx * 1.2);
27336
- context.fillStyle = textColor;
27337
- context.textAlign = "left";
27338
- context.fillText(label, rect.left + 12 * zoomFactor, rect.top + fontSizePx * 0.55);
27339
- }
27340
- context.restore();
27341
- return;
27342
- }
27343
- if (control.kind === "button" || control.kind === "dropdown" || control.kind === "editbox" || control.kind === "listbox" || control.kind === "scrollbar" || control.kind === "spinner" || control.kind === "unknown") {
27344
- if (control.kind === "button") {
27345
- const gradient = context.createLinearGradient(rect.left, rect.top, rect.left, rect.top + rect.height);
27346
- gradient.addColorStop(0, "#f8fafc");
27347
- gradient.addColorStop(1, "#e2e8f0");
27348
- context.fillStyle = gradient;
27349
- } else {
27350
- context.fillStyle = "transparent";
27351
- }
27352
- drawCanvasRoundedRect(context, rect.left, rect.top, rect.width, rect.height, control.kind === "button" ? 4 * zoomFactor : 2 * zoomFactor);
27353
- if (control.kind === "button") {
27354
- context.fill();
27355
- }
27356
- context.stroke();
27357
- }
27358
- let textLeft = rect.left + 2 * zoomFactor;
27359
- const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
27360
- if (control.kind === "checkbox") {
27361
- context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
27362
- if (controlChecked || controlMixed) {
27363
- context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
27364
- context.fillRect(rect.left + 2 * zoomFactor + 1.5, rect.top + (rect.height - iconSize) / 2 + 1.5, Math.max(0, iconSize - 3), Math.max(0, iconSize - 3));
27365
- context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
27366
- context.beginPath();
27367
- if (controlMixed) {
27368
- context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.25, rect.top + rect.height / 2);
27369
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.75, rect.top + rect.height / 2);
27370
- } else {
27371
- context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
27372
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
27373
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
27374
- }
27375
- context.stroke();
27376
- }
27377
- textLeft += iconSize + 4 * zoomFactor;
27378
- } else if (control.kind === "radio") {
27379
- context.beginPath();
27380
- context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
27381
- context.stroke();
27382
- if (controlChecked) {
27383
- context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
27384
- context.beginPath();
27385
- context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
27386
- context.fill();
27387
- }
27388
- textLeft += iconSize + 4 * zoomFactor;
27389
- }
27390
- if (label) {
27391
- const maxTextWidth = Math.max(0, rect.left + rect.width - textLeft - textRightInset);
27392
- const text = truncateCanvasText(context, label, maxTextWidth);
27393
- context.fillStyle = textColor;
27394
- context.textAlign = control.textAlign === "right" ? "right" : control.textAlign === "center" ? "center" : "left";
27395
- const textX = context.textAlign === "right" ? rect.left + rect.width - textRightInset : context.textAlign === "center" ? textLeft + maxTextWidth / 2 : textLeft;
27396
- context.fillText(text, textX, rect.top + rect.height / 2);
27397
- }
27398
- if (control.kind === "dropdown") {
27399
- context.fillStyle = textColor;
27400
- context.textAlign = "center";
27401
- context.fillText("\u25BC", rect.left + rect.width - 10 * zoomFactor, rect.top + rect.height / 2);
27402
- }
27403
- context.restore();
27404
- };
27405
27350
  const drawBakedCanvasDrawings = (pane, context, dirtyRects) => {
27406
27351
  if (bakedCanvasDrawingEntries.length === 0 || dirtyRects.length === 0) {
27407
27352
  return;
@@ -27416,8 +27361,6 @@ function XlsxGrid({
27416
27361
  }
27417
27362
  if (entry.kind === "shape") {
27418
27363
  drawBakedShape(context, entry.shape, localRect);
27419
- } else if (entry.kind === "formControl") {
27420
- drawBakedFormControl(context, entry.control, localRect);
27421
27364
  } else {
27422
27365
  const imageElement = getCanvasImage(entry.image);
27423
27366
  if (imageElement) {
@@ -28111,7 +28054,6 @@ function XlsxGrid({
28111
28054
  activeSheet,
28112
28055
  applyCanvasViewportCompensation,
28113
28056
  bakedCanvasDrawingSignature,
28114
- bakedFormControlRects,
28115
28057
  bakedImageRects,
28116
28058
  bakedShapeRects,
28117
28059
  canvasColumnHeaderCells,
@@ -28730,8 +28672,8 @@ function XlsxGrid({
28730
28672
  event.stopPropagation();
28731
28673
  };
28732
28674
  const commitControlKind = (kind, nextControl) => {
28733
- if (control.controlIndex === void 0) {
28734
- return;
28675
+ if (readOnly || control.controlIndex === void 0) {
28676
+ return false;
28735
28677
  }
28736
28678
  const didUpdate = updateFormControl(control.controlIndex, { kind }, control.sheetIndex);
28737
28679
  if (didUpdate) {
@@ -28741,7 +28683,77 @@ function XlsxGrid({
28741
28683
  type: "change"
28742
28684
  });
28743
28685
  }
28686
+ return didUpdate;
28744
28687
  };
28688
+ const setControlState = (state = "unchecked") => {
28689
+ const kind = buildFormControlKindInput(control);
28690
+ if (kind?.kind === "checkbox") {
28691
+ return commitControlKind(
28692
+ { ...kind, state },
28693
+ { checked: state === "checked", state }
28694
+ );
28695
+ }
28696
+ if (kind?.kind === "optionButton" && state !== "mixed") {
28697
+ return commitControlKind(
28698
+ { ...kind, state },
28699
+ { checked: state === "checked", state }
28700
+ );
28701
+ }
28702
+ return false;
28703
+ };
28704
+ const setControlSelected = (selected) => {
28705
+ const kind = buildFormControlKindInput(control);
28706
+ if (kind?.kind === "dropdown") {
28707
+ const nextSelected = typeof selected === "number" ? selected : void 0;
28708
+ return commitControlKind(
28709
+ { ...kind, selected: nextSelected },
28710
+ { selected: nextSelected }
28711
+ );
28712
+ }
28713
+ if (kind?.kind === "listBox") {
28714
+ const nextSelected = Array.isArray(selected) ? selected : typeof selected === "number" ? [selected] : [];
28715
+ return commitControlKind(
28716
+ { ...kind, selected: nextSelected },
28717
+ { selected: nextSelected }
28718
+ );
28719
+ }
28720
+ return false;
28721
+ };
28722
+ const setControlValue = (value) => {
28723
+ if (!Number.isFinite(value)) {
28724
+ return false;
28725
+ }
28726
+ const kind = buildFormControlKindInput(control);
28727
+ if (kind?.kind !== "scrollbar" && kind?.kind !== "spinner") {
28728
+ return false;
28729
+ }
28730
+ const nextValue = Math.max(kind.min, Math.min(kind.max, value));
28731
+ return commitControlKind({ ...kind, value: nextValue }, { value: nextValue });
28732
+ };
28733
+ const activateControl = () => {
28734
+ if (!readOnly && control.kind === "button") {
28735
+ onFormControlAction?.({ control, type: "activate" });
28736
+ }
28737
+ };
28738
+ if (renderFormControl) {
28739
+ return /* @__PURE__ */ jsx3(React4.Fragment, { children: renderFormControl({
28740
+ activate: activateControl,
28741
+ checked: isFormControlChecked(control),
28742
+ control,
28743
+ disabled: !isInteractiveControl,
28744
+ items: controlItems,
28745
+ label: controlLabel,
28746
+ mixed: isFormControlMixed(control),
28747
+ readOnly,
28748
+ rect,
28749
+ setSelected: setControlSelected,
28750
+ setState: setControlState,
28751
+ setValue: setControlValue,
28752
+ stopPropagation: stopControlPointerEvent,
28753
+ style: commonStyle,
28754
+ zoomFactor
28755
+ }) }, `${pane}-${control.id}`);
28756
+ }
28745
28757
  if (control.kind === "group-box") {
28746
28758
  const hasLabel = controlLabel.length > 0;
28747
28759
  return /* @__PURE__ */ jsx3(
@@ -28785,19 +28797,16 @@ function XlsxGrid({
28785
28797
  }
28786
28798
  if (control.kind === "radio") {
28787
28799
  const checked = isFormControlChecked(control);
28788
- const kind = buildFormControlKindInput(control);
28789
28800
  return /* @__PURE__ */ jsxs3(
28790
28801
  "button",
28791
28802
  {
28803
+ "aria-disabled": readOnly || control.controlIndex === void 0,
28792
28804
  "aria-checked": checked,
28793
- disabled: readOnly || control.controlIndex === void 0,
28805
+ disabled: !readOnly && control.controlIndex === void 0,
28794
28806
  onClick: (event) => {
28795
28807
  event.stopPropagation();
28796
- if (!checked && kind?.kind === "optionButton") {
28797
- commitControlKind(
28798
- { ...kind, state: "checked" },
28799
- { checked: true, state: "checked" }
28800
- );
28808
+ if (!checked) {
28809
+ setControlState("checked");
28801
28810
  }
28802
28811
  },
28803
28812
  onPointerDown: stopControlPointerEvent,
@@ -28809,6 +28818,7 @@ function XlsxGrid({
28809
28818
  boxSizing: "border-box",
28810
28819
  padding: `0 ${Math.max(1, zoomFactor)}px`
28811
28820
  },
28821
+ tabIndex: readOnly ? -1 : void 0,
28812
28822
  type: "button",
28813
28823
  children: [
28814
28824
  renderRadioControl(checked, palette, zoomFactor),
@@ -28821,21 +28831,16 @@ function XlsxGrid({
28821
28831
  if (control.kind === "checkbox") {
28822
28832
  const checked = isFormControlChecked(control);
28823
28833
  const mixed = isFormControlMixed(control);
28824
- const kind = buildFormControlKindInput(control);
28825
28834
  return /* @__PURE__ */ jsxs3(
28826
28835
  "button",
28827
28836
  {
28837
+ "aria-disabled": readOnly || control.controlIndex === void 0,
28828
28838
  "aria-checked": mixed ? "mixed" : checked,
28829
- disabled: readOnly || control.controlIndex === void 0,
28839
+ disabled: !readOnly && control.controlIndex === void 0,
28830
28840
  onClick: (event) => {
28831
28841
  event.stopPropagation();
28832
- if (kind?.kind === "checkbox") {
28833
- const nextState = checked && !mixed ? "unchecked" : "checked";
28834
- commitControlKind(
28835
- { ...kind, state: nextState },
28836
- { checked: nextState === "checked", state: nextState }
28837
- );
28838
- }
28842
+ const nextState = checked && !mixed ? "unchecked" : "checked";
28843
+ setControlState(nextState);
28839
28844
  },
28840
28845
  onPointerDown: stopControlPointerEvent,
28841
28846
  role: "checkbox",
@@ -28846,6 +28851,7 @@ function XlsxGrid({
28846
28851
  boxSizing: "border-box",
28847
28852
  padding: `0 ${Math.max(1, zoomFactor)}px`
28848
28853
  },
28854
+ tabIndex: readOnly ? -1 : void 0,
28849
28855
  type: "button",
28850
28856
  children: [
28851
28857
  renderCheckboxControl(checked, palette, zoomFactor, mixed),
@@ -28859,10 +28865,11 @@ function XlsxGrid({
28859
28865
  return /* @__PURE__ */ jsx3(
28860
28866
  "button",
28861
28867
  {
28862
- disabled: readOnly || !onFormControlAction,
28868
+ "aria-disabled": readOnly || !onFormControlAction,
28869
+ disabled: !readOnly && !onFormControlAction,
28863
28870
  onClick: (event) => {
28864
28871
  event.stopPropagation();
28865
- onFormControlAction?.({ control, type: "activate" });
28872
+ activateControl();
28866
28873
  },
28867
28874
  onPointerDown: stopControlPointerEvent,
28868
28875
  style: {
@@ -28875,6 +28882,7 @@ function XlsxGrid({
28875
28882
  padding: `0 ${6 * zoomFactor}px`,
28876
28883
  textAlign: "center"
28877
28884
  },
28885
+ tabIndex: readOnly ? -1 : void 0,
28878
28886
  type: "button",
28879
28887
  children: controlLabel
28880
28888
  },
@@ -28882,22 +28890,17 @@ function XlsxGrid({
28882
28890
  );
28883
28891
  }
28884
28892
  if (control.kind === "dropdown") {
28885
- const kind = buildFormControlKindInput(control);
28886
28893
  const selected = typeof control.selected === "number" ? control.selected : void 0;
28887
28894
  return /* @__PURE__ */ jsxs3(
28888
28895
  "select",
28889
28896
  {
28897
+ "aria-disabled": readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28890
28898
  "aria-label": controlLabel || control.name || "Dropdown form control",
28891
- disabled: readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28899
+ disabled: !readOnly && (control.controlIndex === void 0 || controlItems.length === 0),
28892
28900
  onChange: (event) => {
28893
28901
  event.stopPropagation();
28894
- if (kind?.kind === "dropdown") {
28895
- const nextSelected = event.currentTarget.value === "" ? void 0 : Number(event.currentTarget.value);
28896
- commitControlKind(
28897
- { ...kind, selected: nextSelected },
28898
- { selected: nextSelected }
28899
- );
28900
- }
28902
+ const nextSelected = event.currentTarget.value === "" ? void 0 : Number(event.currentTarget.value);
28903
+ setControlSelected(nextSelected);
28901
28904
  },
28902
28905
  onClick: stopControlPointerEvent,
28903
28906
  onPointerDown: stopControlPointerEvent,
@@ -28909,6 +28912,7 @@ function XlsxGrid({
28909
28912
  boxSizing: "border-box",
28910
28913
  padding: `0 ${4 * zoomFactor}px`
28911
28914
  },
28915
+ tabIndex: readOnly ? -1 : void 0,
28912
28916
  value: selected === void 0 ? "" : String(selected),
28913
28917
  children: [
28914
28918
  selected === void 0 ? /* @__PURE__ */ jsx3("option", { value: "", children: controlLabel }) : null,
@@ -28919,24 +28923,19 @@ function XlsxGrid({
28919
28923
  );
28920
28924
  }
28921
28925
  if (control.kind === "listbox") {
28922
- const kind = buildFormControlKindInput(control);
28923
28926
  const selected = Array.isArray(control.selected) ? control.selected : control.selected === void 0 ? [] : [control.selected];
28924
28927
  const allowsMultipleSelection = (control.selection ?? "single") !== "single";
28925
28928
  return /* @__PURE__ */ jsx3(
28926
28929
  "select",
28927
28930
  {
28931
+ "aria-disabled": readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28928
28932
  "aria-label": controlLabel || control.name || "List box form control",
28929
- disabled: readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28933
+ disabled: !readOnly && (control.controlIndex === void 0 || controlItems.length === 0),
28930
28934
  multiple: allowsMultipleSelection,
28931
28935
  onChange: (event) => {
28932
28936
  event.stopPropagation();
28933
- if (kind?.kind === "listBox") {
28934
- const nextSelected = Array.from(event.currentTarget.selectedOptions, (option) => Number(option.value));
28935
- commitControlKind(
28936
- { ...kind, selected: nextSelected },
28937
- { selected: nextSelected }
28938
- );
28939
- }
28937
+ const nextSelected = Array.from(event.currentTarget.selectedOptions, (option) => Number(option.value));
28938
+ setControlSelected(nextSelected);
28940
28939
  },
28941
28940
  onClick: stopControlPointerEvent,
28942
28941
  onPointerDown: stopControlPointerEvent,
@@ -28951,6 +28950,7 @@ function XlsxGrid({
28951
28950
  display: "block",
28952
28951
  padding: 0
28953
28952
  },
28953
+ tabIndex: readOnly ? -1 : void 0,
28954
28954
  value: allowsMultipleSelection ? selected.map(String) : selected[0] === void 0 ? "" : String(selected[0]),
28955
28955
  children: controlItems.map((item, index) => /* @__PURE__ */ jsx3("option", { value: index, children: item }, index))
28956
28956
  },
@@ -28958,22 +28958,19 @@ function XlsxGrid({
28958
28958
  );
28959
28959
  }
28960
28960
  if (control.kind === "scrollbar") {
28961
- const kind = buildFormControlKindInput(control);
28962
28961
  const min = control.min ?? 0;
28963
28962
  const max = Math.max(min, control.max ?? 100);
28964
28963
  return /* @__PURE__ */ jsx3(
28965
28964
  "input",
28966
28965
  {
28966
+ "aria-disabled": readOnly || control.controlIndex === void 0,
28967
28967
  "aria-label": controlLabel || control.name || "Scrollbar form control",
28968
- disabled: readOnly || control.controlIndex === void 0,
28968
+ disabled: !readOnly && control.controlIndex === void 0,
28969
28969
  max,
28970
28970
  min,
28971
28971
  onChange: (event) => {
28972
28972
  event.stopPropagation();
28973
- if (kind?.kind === "scrollbar") {
28974
- const value = event.currentTarget.valueAsNumber;
28975
- commitControlKind({ ...kind, value }, { value });
28976
- }
28973
+ setControlValue(event.currentTarget.valueAsNumber);
28977
28974
  },
28978
28975
  onClick: stopControlPointerEvent,
28979
28976
  onPointerDown: stopControlPointerEvent,
@@ -28985,6 +28982,7 @@ function XlsxGrid({
28985
28982
  padding: 0,
28986
28983
  writingMode: control.horizontal === false ? "vertical-lr" : void 0
28987
28984
  },
28985
+ tabIndex: readOnly ? -1 : void 0,
28988
28986
  type: "range",
28989
28987
  value: Math.max(min, Math.min(max, control.value ?? min))
28990
28988
  },
@@ -28992,22 +28990,19 @@ function XlsxGrid({
28992
28990
  );
28993
28991
  }
28994
28992
  if (control.kind === "spinner") {
28995
- const kind = buildFormControlKindInput(control);
28996
28993
  const min = control.min ?? 0;
28997
28994
  const max = Math.max(min, control.max ?? 100);
28998
28995
  return /* @__PURE__ */ jsx3(
28999
28996
  "input",
29000
28997
  {
28998
+ "aria-disabled": readOnly || control.controlIndex === void 0,
29001
28999
  "aria-label": controlLabel || control.name || "Spinner form control",
29002
- disabled: readOnly || control.controlIndex === void 0,
29000
+ disabled: !readOnly && control.controlIndex === void 0,
29003
29001
  max,
29004
29002
  min,
29005
29003
  onChange: (event) => {
29006
29004
  event.stopPropagation();
29007
- if (kind?.kind === "spinner" && Number.isFinite(event.currentTarget.valueAsNumber)) {
29008
- const value = Math.max(min, Math.min(max, event.currentTarget.valueAsNumber));
29009
- commitControlKind({ ...kind, value }, { value });
29010
- }
29005
+ setControlValue(event.currentTarget.valueAsNumber);
29011
29006
  },
29012
29007
  onClick: stopControlPointerEvent,
29013
29008
  onPointerDown: stopControlPointerEvent,
@@ -29020,6 +29015,7 @@ function XlsxGrid({
29020
29015
  boxSizing: "border-box",
29021
29016
  padding: `0 ${4 * zoomFactor}px`
29022
29017
  },
29018
+ tabIndex: readOnly ? -1 : void 0,
29023
29019
  type: "number",
29024
29020
  value: Math.max(min, Math.min(max, control.value ?? min))
29025
29021
  },
@@ -29323,7 +29319,7 @@ function XlsxGrid({
29323
29319
  };
29324
29320
  const drawingViewportCacheSignature = `${Math.floor(drawingViewport.left / CANVAS_VIEWPORT_OVERSCAN_PX)}:${Math.floor(drawingViewport.top / CANVAS_VIEWPORT_OVERSCAN_PX)}:${drawingViewport.width}:${drawingViewport.height}`;
29325
29321
  const previousPaneDrawingNodes = paneDrawingNodesCacheRef.current;
29326
- const canReusePaneDrawingNodes = previousPaneDrawingNodes !== null && previousPaneDrawingNodes.showImages === showImages && previousPaneDrawingNodes.chartRects === chartRects && previousPaneDrawingNodes.formControlRects === domFormControlRects && previousPaneDrawingNodes.shapeRects === domShapeRects && previousPaneDrawingNodes.imageRects === domImageRects && previousPaneDrawingNodes.selectedChartId === selectedChartId && previousPaneDrawingNodes.selectedImageId === selectedImageId && previousPaneDrawingNodes.readOnly === readOnly && previousPaneDrawingNodes.selectionStroke === selectionStroke && previousPaneDrawingNodes.renderChartLoading === renderChartLoading && previousPaneDrawingNodes.renderImage === renderImage && previousPaneDrawingNodes.renderImageSelection === renderImageSelection && previousPaneDrawingNodes.isChartsLoading === isChartsLoading && previousPaneDrawingNodes.selectedChartElement === selectedChartElement && previousPaneDrawingNodes.palette === palette && previousPaneDrawingNodes.drawingViewportSignature === drawingViewportCacheSignature;
29322
+ const canReusePaneDrawingNodes = previousPaneDrawingNodes !== null && previousPaneDrawingNodes.showImages === showImages && previousPaneDrawingNodes.updateFormControl === updateFormControl && previousPaneDrawingNodes.chartRects === chartRects && previousPaneDrawingNodes.formControlRects === domFormControlRects && previousPaneDrawingNodes.formControlItemsById === formControlItemsById && previousPaneDrawingNodes.shapeRects === domShapeRects && previousPaneDrawingNodes.imageRects === domImageRects && previousPaneDrawingNodes.selectedChartId === selectedChartId && previousPaneDrawingNodes.selectedImageId === selectedImageId && previousPaneDrawingNodes.readOnly === readOnly && previousPaneDrawingNodes.onFormControlAction === onFormControlAction && previousPaneDrawingNodes.onFormControlChange === onFormControlChange && previousPaneDrawingNodes.selectionStroke === selectionStroke && previousPaneDrawingNodes.renderChartLoading === renderChartLoading && previousPaneDrawingNodes.renderFormControl === renderFormControl && previousPaneDrawingNodes.renderImage === renderImage && previousPaneDrawingNodes.renderImageSelection === renderImageSelection && previousPaneDrawingNodes.isChartsLoading === isChartsLoading && previousPaneDrawingNodes.selectedChartElement === selectedChartElement && previousPaneDrawingNodes.palette === palette && previousPaneDrawingNodes.drawingViewportSignature === drawingViewportCacheSignature;
29327
29323
  const paneDrawingNodes = canReusePaneDrawingNodes ? previousPaneDrawingNodes.value : !showImages ? {
29328
29324
  corner: null,
29329
29325
  left: null,
@@ -29359,12 +29355,16 @@ function XlsxGrid({
29359
29355
  paneDrawingNodesCacheRef.current = {
29360
29356
  chartRects,
29361
29357
  drawingViewportSignature: drawingViewportCacheSignature,
29358
+ formControlItemsById,
29362
29359
  formControlRects: domFormControlRects,
29363
29360
  imageRects: domImageRects,
29364
29361
  isChartsLoading,
29362
+ onFormControlAction,
29363
+ onFormControlChange,
29365
29364
  palette,
29366
29365
  readOnly,
29367
29366
  renderChartLoading,
29367
+ renderFormControl,
29368
29368
  renderImage,
29369
29369
  renderImageSelection,
29370
29370
  selectedChartElement,
@@ -29373,6 +29373,7 @@ function XlsxGrid({
29373
29373
  selectionStroke,
29374
29374
  shapeRects: domShapeRects,
29375
29375
  showImages,
29376
+ updateFormControl,
29376
29377
  value: paneDrawingNodes
29377
29378
  };
29378
29379
  }
@@ -30602,6 +30603,7 @@ function XlsxViewerInner({
30602
30603
  onFormControlAction,
30603
30604
  onFormControlChange,
30604
30605
  renderChartLoading,
30606
+ renderFormControl,
30605
30607
  renderImage,
30606
30608
  renderImageSelection,
30607
30609
  renderScroller,
@@ -30667,6 +30669,7 @@ function XlsxViewerInner({
30667
30669
  onFormControlChange,
30668
30670
  palette,
30669
30671
  renderChartLoading,
30672
+ renderFormControl,
30670
30673
  renderImage,
30671
30674
  renderImageSelection,
30672
30675
  renderScroller,
@@ -31087,20 +31090,87 @@ function useXlsxViewerCharts() {
31087
31090
  );
31088
31091
  }
31089
31092
  function useXlsxViewerThumbnails(options = {}) {
31090
- const { getSheetFormControls, getSheetImages, getSheetShapes, workbook, sheets } = useXlsxViewer();
31093
+ const {
31094
+ getRowsBatchAsync,
31095
+ getSheetFormControls,
31096
+ getSheetImages,
31097
+ getSheetShapes,
31098
+ workbook,
31099
+ sheets
31100
+ } = useXlsxViewer();
31091
31101
  const { isDark } = React4.useContext(ViewerAppearanceContext);
31092
31102
  const palette = useViewerPalette(isDark);
31093
31103
  const includeHeaders = options.includeHeaders ?? true;
31094
31104
  const resolution = options.resolution;
31095
31105
  const thumbnailImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
31106
+ const workerThumbnailRowBatchCacheRef = React4.useRef(/* @__PURE__ */ new Map());
31107
+ const workerThumbnailRowBatchRequestRef = React4.useRef(
31108
+ /* @__PURE__ */ new Map()
31109
+ );
31096
31110
  const isMountedRef = React4.useRef(false);
31097
31111
  const [thumbnailImageLoadVersion, setThumbnailImageLoadVersion] = React4.useState(0);
31112
+ const [workerThumbnailRowBatchVersion, setWorkerThumbnailRowBatchVersion] = React4.useState(0);
31098
31113
  React4.useEffect(() => {
31099
31114
  isMountedRef.current = true;
31100
31115
  return () => {
31101
31116
  isMountedRef.current = false;
31102
31117
  };
31103
31118
  }, []);
31119
+ React4.useEffect(() => {
31120
+ const currentSheets = new Set(sheets);
31121
+ for (const sheet of workerThumbnailRowBatchCacheRef.current.keys()) {
31122
+ if (!currentSheets.has(sheet)) {
31123
+ workerThumbnailRowBatchCacheRef.current.delete(sheet);
31124
+ }
31125
+ }
31126
+ for (const sheet of workerThumbnailRowBatchRequestRef.current.keys()) {
31127
+ if (!currentSheets.has(sheet)) {
31128
+ workerThumbnailRowBatchRequestRef.current.delete(sheet);
31129
+ }
31130
+ }
31131
+ }, [sheets]);
31132
+ const getWorkerThumbnailRowBatch = React4.useCallback((sheet, startRow, endRow) => {
31133
+ const cachedBatch = workerThumbnailRowBatchCacheRef.current.get(sheet);
31134
+ if (cachedBatch && startRow >= cachedBatch.startRow && endRow <= cachedBatch.endRow) {
31135
+ return cachedBatch;
31136
+ }
31137
+ if (!getRowsBatchAsync) {
31138
+ return null;
31139
+ }
31140
+ const pendingRequest = workerThumbnailRowBatchRequestRef.current.get(sheet);
31141
+ if (!pendingRequest) {
31142
+ const request = {
31143
+ endRow: Math.max(endRow, cachedBatch?.endRow ?? endRow),
31144
+ startRow: Math.min(startRow, cachedBatch?.startRow ?? startRow)
31145
+ };
31146
+ workerThumbnailRowBatchRequestRef.current.set(sheet, request);
31147
+ void getRowsBatchAsync(
31148
+ sheet.workbookSheetIndex,
31149
+ request.startRow,
31150
+ request.endRow - request.startRow + 1
31151
+ ).then((rows) => {
31152
+ if (workerThumbnailRowBatchRequestRef.current.get(sheet) !== request) {
31153
+ return;
31154
+ }
31155
+ workerThumbnailRowBatchRequestRef.current.delete(sheet);
31156
+ if (!rows) {
31157
+ return;
31158
+ }
31159
+ workerThumbnailRowBatchCacheRef.current.set(
31160
+ sheet,
31161
+ buildWorksheetBatchWindow(rows, sheet, request.startRow, request.endRow)
31162
+ );
31163
+ if (isMountedRef.current) {
31164
+ setWorkerThumbnailRowBatchVersion((version) => version + 1);
31165
+ }
31166
+ }).catch(() => {
31167
+ if (workerThumbnailRowBatchRequestRef.current.get(sheet) === request) {
31168
+ workerThumbnailRowBatchRequestRef.current.delete(sheet);
31169
+ }
31170
+ });
31171
+ }
31172
+ return null;
31173
+ }, [getRowsBatchAsync]);
31104
31174
  const getThumbnailImage = React4.useCallback((image) => {
31105
31175
  if (typeof Image === "undefined") {
31106
31176
  return null;
@@ -31326,6 +31396,10 @@ function useXlsxViewerThumbnails(options = {}) {
31326
31396
  if (!canvas) {
31327
31397
  return false;
31328
31398
  }
31399
+ const workerRowBatch = worksheet ? null : getWorkerThumbnailRowBatch(sheet, sourceRange.start.row, sourceRange.end.row);
31400
+ if (!worksheet && !workerRowBatch) {
31401
+ return false;
31402
+ }
31329
31403
  const context = canvas.getContext("2d", { alpha: false });
31330
31404
  if (!context) {
31331
31405
  return false;
@@ -31362,9 +31436,6 @@ function useXlsxViewerThumbnails(options = {}) {
31362
31436
  context.fillRect(0, headerHeight, rowHeaderWidth, Math.max(1, rowAxis.totalSize));
31363
31437
  context.fillRect(0, 0, rowHeaderWidth, headerHeight);
31364
31438
  }
31365
- if (!worksheet) {
31366
- return true;
31367
- }
31368
31439
  const conditionalFormatMetricsCache = /* @__PURE__ */ new Map();
31369
31440
  const cellRenderCache = /* @__PURE__ */ new Map();
31370
31441
  const getCellData = (row, col) => {
@@ -31373,7 +31444,9 @@ function useXlsxViewerThumbnails(options = {}) {
31373
31444
  if (cached) {
31374
31445
  return cached;
31375
31446
  }
31376
- if (worksheet.isMergedSecondary(row, col)) {
31447
+ const batchCoversRow = workerRowBatch ? row >= workerRowBatch.startRow && row <= workerRowBatch.endRow : false;
31448
+ const batchedCell = batchCoversRow ? workerRowBatch?.cells.get(cacheKey) : void 0;
31449
+ if (batchedCell?.isMergedSecondary || worksheet?.isMergedSecondary(row, col)) {
31377
31450
  const mergedSecondaryData = {
31378
31451
  isMergedSecondary: true,
31379
31452
  style: {},
@@ -31382,53 +31455,34 @@ function useXlsxViewerThumbnails(options = {}) {
31382
31455
  cellRenderCache.set(cacheKey, mergedSecondaryData);
31383
31456
  return mergedSecondaryData;
31384
31457
  }
31385
- const merge = worksheet.getMergeSpan(row, col);
31458
+ const merge = worksheet ? worksheet.getMergeSpan(row, col) : batchedCell?.mergeSpan;
31386
31459
  const inheritedStyle = resolveInheritedCellStyle2(sheet, row, col);
31387
- const worksheetStyle = worksheet.getCellStyleAt(row, col) ?? null;
31460
+ const worksheetStyle = worksheet ? worksheet.getCellStyleAt(row, col) ?? null : batchedCell?.style ?? null;
31388
31461
  const mergedStyle = mergeResolvedCellStyle(inheritedStyle, worksheetStyle, { replaceXfSubtrees: true });
31389
31462
  const alignment = mergedStyle?.alignment;
31390
31463
  const sparkline = sparklineByCell.get(cacheKey) ?? null;
31464
+ const getNumericValue = (targetRow, targetCol) => worksheet ? getCellNumericValue(worksheet, targetRow, targetCol) : getBatchedCellNumericValue(workerRowBatch?.cells.get(`${targetRow}:${targetCol}`));
31391
31465
  const sparklineValues = sparkline ? sparkline.range.start.row === sparkline.range.end.row ? Array.from(
31392
31466
  { length: Math.abs(sparkline.range.end.col - sparkline.range.start.col) + 1 },
31393
- (_, index) => getCellNumericValue(
31394
- worksheet,
31467
+ (_, index) => getNumericValue(
31395
31468
  sparkline.range.start.row,
31396
31469
  Math.min(sparkline.range.start.col, sparkline.range.end.col) + index
31397
31470
  )
31398
31471
  ) : Array.from(
31399
31472
  { length: Math.abs(sparkline.range.end.row - sparkline.range.start.row) + 1 },
31400
- (_, index) => getCellNumericValue(
31401
- worksheet,
31473
+ (_, index) => getNumericValue(
31402
31474
  Math.min(sparkline.range.start.row, sparkline.range.end.row) + index,
31403
31475
  sparkline.range.start.col
31404
31476
  )
31405
31477
  ) : null;
31406
- const checkboxState = mergedStyle?.cellControl ? getCellBooleanValue(worksheet, row, col) : null;
31478
+ const checkboxState = mergedStyle?.cellControl ? worksheet ? getCellBooleanValue(worksheet, row, col) : getBatchedCellBooleanValue(batchedCell) : null;
31407
31479
  const nextData = {
31408
31480
  canvas: void 0,
31409
31481
  checkboxState,
31410
31482
  colSpan: merge?.colSpan,
31411
- conditionalColorScale: resolveConditionalColorScaleForCell(
31412
- row,
31413
- col,
31414
- worksheet,
31415
- sheet,
31416
- conditionalFormatMetricsCache
31417
- ),
31418
- conditionalDataBar: resolveConditionalDataBarForCell(
31419
- row,
31420
- col,
31421
- worksheet,
31422
- sheet,
31423
- conditionalFormatMetricsCache
31424
- ),
31425
- conditionalIcon: resolveConditionalIconForCell(
31426
- row,
31427
- col,
31428
- worksheet,
31429
- sheet,
31430
- conditionalFormatMetricsCache
31431
- ),
31483
+ conditionalColorScale: worksheet ? resolveConditionalColorScaleForCell(row, col, worksheet, sheet, conditionalFormatMetricsCache) : null,
31484
+ conditionalDataBar: worksheet ? resolveConditionalDataBarForCell(row, col, worksheet, sheet, conditionalFormatMetricsCache) : null,
31485
+ conditionalIcon: worksheet ? resolveConditionalIconForCell(row, col, worksheet, sheet, conditionalFormatMetricsCache) : null,
31432
31486
  isMergedSecondary: false,
31433
31487
  rowSpan: merge?.rowSpan,
31434
31488
  sparkline: sparkline && sparklineValues ? { config: sparkline, values: sparklineValues } : null,
@@ -31436,7 +31490,7 @@ function useXlsxViewerThumbnails(options = {}) {
31436
31490
  showGridLines
31437
31491
  }),
31438
31492
  textRotationDeg: resolveSpreadsheetTextRotation(alignment?.textRotation),
31439
- value: sparkline ? "" : checkboxState !== null ? "" : getCellDisplayValue(worksheet, row, col, sheet)
31493
+ value: sparkline ? "" : checkboxState !== null ? "" : worksheet ? getCellDisplayValue(worksheet, row, col, sheet) : batchedCell?.value ?? ""
31440
31494
  };
31441
31495
  nextData.canvas = buildCanvasCellStyleCache(nextData.style);
31442
31496
  cellRenderCache.set(cacheKey, nextData);
@@ -31710,12 +31764,14 @@ function useXlsxViewerThumbnails(options = {}) {
31710
31764
  getSheetFormControls,
31711
31765
  getSheetImages,
31712
31766
  getSheetShapes,
31767
+ getWorkerThumbnailRowBatch,
31713
31768
  getThumbnailImage,
31714
31769
  includeHeaders,
31715
31770
  palette,
31716
31771
  resolution,
31717
31772
  sheets,
31718
31773
  thumbnailImageLoadVersion,
31774
+ workerThumbnailRowBatchVersion,
31719
31775
  workbook
31720
31776
  ]);
31721
31777
  const paintThumbnail = React4.useCallback(