@extend-ai/react-xlsx 0.13.4 → 0.14.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
@@ -3642,7 +3642,6 @@ var SPREADSHEET_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
3642
3642
  var CONTENT_TYPES_NS = "http://schemas.openxmlformats.org/package/2006/content-types";
3643
3643
  var DRAWING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing";
3644
3644
  var VML_DRAWING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing";
3645
- var CTRL_PROP_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp";
3646
3645
  var IMAGE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
3647
3646
  var DRAWING_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.drawing+xml";
3648
3647
  var EMU_PER_PIXEL2 = 9525;
@@ -5085,6 +5084,124 @@ function parseSpreadsheetBooleanNode(node) {
5085
5084
  }
5086
5085
  return parseSpreadsheetBooleanValue(node.getAttribute("val") ?? node.textContent);
5087
5086
  }
5087
+ function mapDukeFormControlAnchor(anchor) {
5088
+ return {
5089
+ from: {
5090
+ col: anchor.fromCol,
5091
+ colOffsetEmu: anchor.fromColOffset,
5092
+ row: anchor.fromRow,
5093
+ rowOffsetEmu: anchor.fromRowOffset
5094
+ },
5095
+ kind: "two-cell",
5096
+ to: {
5097
+ col: anchor.toCol,
5098
+ colOffsetEmu: anchor.toColOffset,
5099
+ row: anchor.toRow,
5100
+ rowOffsetEmu: anchor.toRowOffset
5101
+ }
5102
+ };
5103
+ }
5104
+ function mapDukeFormControlKind(kind) {
5105
+ switch (kind.kind) {
5106
+ case "button":
5107
+ return { caption: kind.caption, kind: "button", label: normalizeControlLabel(kind.caption) };
5108
+ case "checkbox":
5109
+ return {
5110
+ caption: kind.caption,
5111
+ checked: kind.state === "checked",
5112
+ kind: "checkbox",
5113
+ label: normalizeControlLabel(kind.caption),
5114
+ linkedCell: kind.cellLink,
5115
+ no3D: kind.no3D,
5116
+ state: kind.state
5117
+ };
5118
+ case "optionButton":
5119
+ return {
5120
+ caption: kind.caption,
5121
+ checked: kind.state === "checked",
5122
+ firstInGroup: kind.firstInGroup,
5123
+ kind: "radio",
5124
+ label: normalizeControlLabel(kind.caption),
5125
+ linkedCell: kind.cellLink,
5126
+ no3D: kind.no3D,
5127
+ state: kind.state
5128
+ };
5129
+ case "label":
5130
+ return { caption: kind.caption, kind: "label", label: normalizeControlLabel(kind.caption) };
5131
+ case "groupBox":
5132
+ return {
5133
+ caption: kind.caption,
5134
+ kind: "group-box",
5135
+ label: normalizeControlLabel(kind.caption),
5136
+ no3D: kind.no3D
5137
+ };
5138
+ case "listBox":
5139
+ return {
5140
+ inputRange: kind.inputRange,
5141
+ kind: "listbox",
5142
+ linkedCell: kind.cellLink,
5143
+ no3D: kind.no3D,
5144
+ selected: [...kind.selected],
5145
+ selection: kind.selection
5146
+ };
5147
+ case "dropdown":
5148
+ return {
5149
+ inputRange: kind.inputRange,
5150
+ kind: "dropdown",
5151
+ lines: kind.lines,
5152
+ linkedCell: kind.cellLink,
5153
+ no3D: kind.no3D,
5154
+ selected: kind.selected
5155
+ };
5156
+ case "scrollbar":
5157
+ return {
5158
+ horizontal: kind.horizontal,
5159
+ increment: kind.increment,
5160
+ kind: "scrollbar",
5161
+ linkedCell: kind.cellLink,
5162
+ max: kind.max,
5163
+ min: kind.min,
5164
+ page: kind.page,
5165
+ value: kind.value
5166
+ };
5167
+ case "spinner":
5168
+ return {
5169
+ increment: kind.increment,
5170
+ kind: "spinner",
5171
+ linkedCell: kind.cellLink,
5172
+ max: kind.max,
5173
+ min: kind.min,
5174
+ value: kind.value
5175
+ };
5176
+ }
5177
+ }
5178
+ function mapDukeFormControl(control, controlIndex, workbookSheetIndex) {
5179
+ return {
5180
+ anchor: mapDukeFormControlAnchor(control.anchor),
5181
+ controlIndex,
5182
+ dukeAnchor: { ...control.anchor },
5183
+ editAs: control.anchor.editAs,
5184
+ hidden: false,
5185
+ id: `form-control-${workbookSheetIndex}-${controlIndex}`,
5186
+ locked: control.locked,
5187
+ name: control.name,
5188
+ printable: control.printable,
5189
+ sheetIndex: workbookSheetIndex,
5190
+ workbookSheetIndex,
5191
+ zIndex: controlIndex + 1,
5192
+ ...mapDukeFormControlKind(control.kind)
5193
+ };
5194
+ }
5195
+ function collectWorkbookFormControls(workbook) {
5196
+ return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {
5197
+ try {
5198
+ const controls = workbook.getSheet(workbookSheetIndex).formControls;
5199
+ return Array.isArray(controls) ? controls.map((control, controlIndex) => mapDukeFormControl(control, controlIndex, workbookSheetIndex)) : [];
5200
+ } catch {
5201
+ return [];
5202
+ }
5203
+ });
5204
+ }
5088
5205
  function parseFormControlKind(rawType) {
5089
5206
  const normalized = (rawType ?? "").trim().toLowerCase();
5090
5207
  switch (normalized) {
@@ -5850,27 +5967,10 @@ function parseSheetFormControlNodes(archive, sheetPath) {
5850
5967
  }
5851
5968
  return getLocalElements(sheetDocument, "control").map((controlNode) => ({
5852
5969
  anchor: parseAnchor(getFirstDescendant(controlNode, "anchor") ?? controlNode),
5853
- controlRelationshipId: getRelationshipId(controlNode),
5854
5970
  name: controlNode.getAttribute("name") ?? void 0,
5855
5971
  shapeId: parseFormControlShapeId(controlNode.getAttribute("shapeId"))
5856
5972
  }));
5857
5973
  }
5858
- function parseCtrlPropDocument(archive, ctrlPropPath) {
5859
- const xml = readArchiveText2(archive, ctrlPropPath);
5860
- if (!xml) {
5861
- return null;
5862
- }
5863
- const document2 = parseXml2(xml);
5864
- const root = document2?.documentElement;
5865
- if (!root) {
5866
- return null;
5867
- }
5868
- return {
5869
- checked: parseSpreadsheetBooleanValue(root.getAttribute("checked")),
5870
- linkedCell: root.getAttribute("fmlaLink") ?? void 0,
5871
- objectType: root.getAttribute("objectType") ?? void 0
5872
- };
5873
- }
5874
5974
  function parseVmlFormControls(archive, vmlDrawingPath) {
5875
5975
  const xml = readArchiveText2(archive, vmlDrawingPath);
5876
5976
  if (!xml) {
@@ -5928,21 +6028,18 @@ function parseSheetFormControls(archive, sheetPath, sheetRelationships, workbook
5928
6028
  if (!controlNode.anchor) {
5929
6029
  return;
5930
6030
  }
5931
- const ctrlPropRelationship = controlNode.controlRelationshipId ? sheetRelationships.get(controlNode.controlRelationshipId) ?? null : null;
5932
- const ctrlPropPath = ctrlPropRelationship?.type === CTRL_PROP_REL_TYPE ? ctrlPropRelationship.target : null;
5933
- const ctrlProp = ctrlPropPath ? parseCtrlPropDocument(archive, ctrlPropPath) : null;
5934
6031
  const vmlControl = controlNode.shapeId !== null ? vmlControlsByShapeId.get(controlNode.shapeId) ?? null : null;
5935
- const kind = parseFormControlKind(ctrlProp?.objectType ?? vmlControl?.objectType);
6032
+ const kind = parseFormControlKind(vmlControl?.objectType);
5936
6033
  parsedControls.push({
5937
6034
  anchor: controlNode.anchor,
5938
- checked: ctrlProp?.checked ?? vmlControl?.checked,
6035
+ checked: vmlControl?.checked,
5939
6036
  fontFamily: vmlControl?.fontFamily,
5940
6037
  fontSizePt: vmlControl?.fontSizePt,
5941
6038
  hidden: vmlControl?.hidden ?? false,
5942
6039
  id: `form-control-${workbookSheetIndex}-${index}`,
5943
6040
  kind,
5944
6041
  label: vmlControl?.label,
5945
- linkedCell: ctrlProp?.linkedCell ?? vmlControl?.linkedCell,
6042
+ linkedCell: vmlControl?.linkedCell,
5946
6043
  name: controlNode.name,
5947
6044
  sheetIndex: workbookSheetIndex,
5948
6045
  textAlign: vmlControl?.textAlign,
@@ -6028,6 +6125,92 @@ function enrichFormControlsWithHiddenShapes(formControls, shapes, sheetState) {
6028
6125
  };
6029
6126
  });
6030
6127
  }
6128
+ function getFormControlAnchorStart(anchor) {
6129
+ return anchor.kind === "absolute" ? null : { col: anchor.from.col, row: anchor.from.row };
6130
+ }
6131
+ function scoreParsedFormControlMatch(dukeControl, parsedControl, parsedIndex) {
6132
+ if (dukeControl.kind !== parsedControl.kind && parsedControl.kind !== "unknown") {
6133
+ return Number.NEGATIVE_INFINITY;
6134
+ }
6135
+ let score = 10;
6136
+ const dukeName = normalizeControlLabel(dukeControl.name)?.toLowerCase();
6137
+ const parsedName = normalizeControlLabel(parsedControl.name)?.toLowerCase();
6138
+ if (dukeName && parsedName) {
6139
+ if (dukeName !== parsedName) {
6140
+ return Number.NEGATIVE_INFINITY;
6141
+ }
6142
+ score += 100;
6143
+ }
6144
+ const dukeStart = getFormControlAnchorStart(dukeControl.anchor);
6145
+ const parsedStart = getFormControlAnchorStart(parsedControl.anchor);
6146
+ if (dukeStart && parsedStart && dukeStart.col === parsedStart.col && dukeStart.row === parsedStart.row) {
6147
+ score += 40;
6148
+ }
6149
+ if (dukeControl.controlIndex === parsedIndex) {
6150
+ score += 5;
6151
+ }
6152
+ return score;
6153
+ }
6154
+ function areDukeFormControlAnchorsEqual(left, right) {
6155
+ if (!left || !right) {
6156
+ return true;
6157
+ }
6158
+ return left.editAs === right.editAs && left.fromCol === right.fromCol && left.fromColOffset === right.fromColOffset && left.fromRow === right.fromRow && left.fromRowOffset === right.fromRowOffset && left.toCol === right.toCol && left.toColOffset === right.toColOffset && left.toRow === right.toRow && left.toRowOffset === right.toRowOffset;
6159
+ }
6160
+ function mergeDukeAndParsedFormControls(dukeControls, parsedControls, keepUnmatchedParsedControl = () => true) {
6161
+ const usedParsedIndexes = /* @__PURE__ */ new Set();
6162
+ const mergedControls = dukeControls.map((dukeControl) => {
6163
+ let bestIndex = -1;
6164
+ let bestScore = Number.NEGATIVE_INFINITY;
6165
+ parsedControls.forEach((parsedControl2, parsedIndex) => {
6166
+ if (usedParsedIndexes.has(parsedIndex)) {
6167
+ return;
6168
+ }
6169
+ const score = scoreParsedFormControlMatch(dukeControl, parsedControl2, parsedIndex);
6170
+ if (score > bestScore) {
6171
+ bestIndex = parsedIndex;
6172
+ bestScore = score;
6173
+ }
6174
+ });
6175
+ const parsedControl = bestIndex >= 0 && bestScore >= 10 ? parsedControls[bestIndex] : null;
6176
+ if (!parsedControl) {
6177
+ return dukeControl;
6178
+ }
6179
+ usedParsedIndexes.add(bestIndex);
6180
+ const dukeAnchorChanged = !areDukeFormControlAnchorsEqual(
6181
+ parsedControl.dukeAnchor,
6182
+ dukeControl.dukeAnchor
6183
+ );
6184
+ return {
6185
+ ...parsedControl,
6186
+ ...dukeControl,
6187
+ anchor: dukeAnchorChanged ? dukeControl.anchor : parsedControl.anchor,
6188
+ fontFamily: parsedControl.fontFamily,
6189
+ fontSizePt: parsedControl.fontSizePt,
6190
+ hidden: parsedControl.hidden,
6191
+ id: parsedControl.id,
6192
+ label: dukeControl.label ?? parsedControl.label,
6193
+ name: dukeControl.name ?? parsedControl.name,
6194
+ textAlign: parsedControl.textAlign,
6195
+ textColor: parsedControl.textColor,
6196
+ zIndex: parsedControl.zIndex
6197
+ };
6198
+ });
6199
+ parsedControls.forEach((parsedControl, parsedIndex) => {
6200
+ if (!usedParsedIndexes.has(parsedIndex) && keepUnmatchedParsedControl(parsedControl)) {
6201
+ mergedControls.push(parsedControl);
6202
+ }
6203
+ });
6204
+ return mergedControls.sort((left, right) => left.zIndex - right.zIndex);
6205
+ }
6206
+ function refreshWorkbookFormControls(workbook, currentControlsByWorkbookSheetIndex) {
6207
+ const dukeControlsByWorkbookSheetIndex = collectWorkbookFormControls(workbook);
6208
+ return dukeControlsByWorkbookSheetIndex.map((dukeControls, workbookSheetIndex) => mergeDukeAndParsedFormControls(
6209
+ dukeControls,
6210
+ currentControlsByWorkbookSheetIndex[workbookSheetIndex] ?? [],
6211
+ (control) => control.controlIndex === void 0
6212
+ ));
6213
+ }
6031
6214
  function revokeWorkbookImageAssets(assets) {
6032
6215
  if (!assets) {
6033
6216
  return;
@@ -6060,7 +6243,7 @@ function parseWorkbookStructureAssetsFromArchive(archive, options) {
6060
6243
  workbookSheets
6061
6244
  };
6062
6245
  }
6063
- function parseWorkbookImageAssets(bytes) {
6246
+ function parseWorkbookImageAssets(bytes, workbook) {
6064
6247
  const archive = (0, import_fflate2.unzipSync)(bytes);
6065
6248
  const {
6066
6249
  contentTypes,
@@ -6079,6 +6262,7 @@ function parseWorkbookImageAssets(bytes) {
6079
6262
  const shapesByWorkbookSheetIndex = [];
6080
6263
  const sheetOrigins = [];
6081
6264
  const imageOriginsById = /* @__PURE__ */ new Map();
6265
+ const dukeFormControlsByWorkbookSheetIndex = collectWorkbookFormControls(workbook);
6082
6266
  workbookSheets.forEach((sheet, workbookSheetIndex) => {
6083
6267
  const sheetRelationships = parseRelationships(archive, relsPathForDocument(sheet.path), sheet.path);
6084
6268
  const attachments = [];
@@ -6124,7 +6308,10 @@ function parseWorkbookImageAssets(bytes) {
6124
6308
  shapeList,
6125
6309
  sheetStatesByWorkbookSheetIndex[workbookSheetIndex] ?? null
6126
6310
  );
6127
- formControlsByWorkbookSheetIndex[workbookSheetIndex] = enrichedFormControlList;
6311
+ formControlsByWorkbookSheetIndex[workbookSheetIndex] = mergeDukeAndParsedFormControls(
6312
+ dukeFormControlsByWorkbookSheetIndex[workbookSheetIndex] ?? [],
6313
+ enrichedFormControlList
6314
+ );
6128
6315
  imagesByWorkbookSheetIndex[workbookSheetIndex] = imageList;
6129
6316
  shapesByWorkbookSheetIndex[workbookSheetIndex] = visibleShapeList;
6130
6317
  sheetOrigins[workbookSheetIndex] = attachments.length > 0 ? {
@@ -7199,6 +7386,144 @@ function parseA1RangeReference2(reference) {
7199
7386
  }
7200
7387
  return normalizeRange({ start, end });
7201
7388
  }
7389
+ function resolveWorkbookReference(workbook, defaultWorkbookSheetIndex, rawReference, resolvingNamedRange = false) {
7390
+ const reference = rawReference.trim().replace(/^=/, "");
7391
+ if (!reference) {
7392
+ return null;
7393
+ }
7394
+ let workbookSheetIndex = defaultWorkbookSheetIndex;
7395
+ let rangeReference = reference;
7396
+ const bangIndex = reference.lastIndexOf("!");
7397
+ if (bangIndex >= 0) {
7398
+ let sheetName = reference.slice(0, bangIndex).trim();
7399
+ rangeReference = reference.slice(bangIndex + 1).trim();
7400
+ if (sheetName.startsWith("'") && sheetName.endsWith("'")) {
7401
+ sheetName = sheetName.slice(1, -1).replace(/''/g, "'");
7402
+ }
7403
+ const resolvedSheetIndex = workbook.sheetIndex(sheetName);
7404
+ if (resolvedSheetIndex === void 0) {
7405
+ return null;
7406
+ }
7407
+ workbookSheetIndex = resolvedSheetIndex;
7408
+ } else if (!resolvingNamedRange) {
7409
+ const namedRange = workbook.getNamedRange(reference);
7410
+ if (namedRange) {
7411
+ return resolveWorkbookReference(workbook, defaultWorkbookSheetIndex, namedRange, true);
7412
+ }
7413
+ }
7414
+ const range = parseA1RangeReference2(rangeReference.replace(/\$/g, ""));
7415
+ if (!range || workbookSheetIndex < 0 || workbookSheetIndex >= workbook.sheetCount) {
7416
+ return null;
7417
+ }
7418
+ try {
7419
+ return {
7420
+ range,
7421
+ workbookSheetIndex,
7422
+ worksheet: workbook.getSheet(workbookSheetIndex)
7423
+ };
7424
+ } catch {
7425
+ return null;
7426
+ }
7427
+ }
7428
+ function formControlInputFromDukeControl(control) {
7429
+ return {
7430
+ anchor: { ...control.anchor },
7431
+ kind: { ...control.kind },
7432
+ locked: control.locked,
7433
+ name: control.name,
7434
+ printable: control.printable
7435
+ };
7436
+ }
7437
+ function getFormControlCellLink(kind) {
7438
+ return "cellLink" in kind ? kind.cellLink : void 0;
7439
+ }
7440
+ function anchorContainsControl(group, control) {
7441
+ const groupAnchor = group.anchor;
7442
+ const controlAnchor = control.anchor;
7443
+ return controlAnchor.fromCol >= groupAnchor.fromCol && controlAnchor.fromCol <= groupAnchor.toCol && controlAnchor.fromRow >= groupAnchor.fromRow && controlAnchor.fromRow <= groupAnchor.toRow;
7444
+ }
7445
+ function resolveOptionButtonGroupIndexes(controls, targetIndex) {
7446
+ const target = controls[targetIndex];
7447
+ if (!target || target.kind.kind !== "optionButton") {
7448
+ return [];
7449
+ }
7450
+ if (target.kind.cellLink) {
7451
+ const targetCellLink = target.kind.cellLink;
7452
+ return controls.flatMap((control, index) => control.kind.kind === "optionButton" && control.kind.cellLink === targetCellLink ? [index] : []);
7453
+ }
7454
+ const containingGroups = controls.map((control, index) => ({ control, index })).filter(({ control }) => control.kind.kind === "groupBox" && anchorContainsControl(control, target)).sort((left, right) => {
7455
+ const leftArea = (left.control.anchor.toCol - left.control.anchor.fromCol) * (left.control.anchor.toRow - left.control.anchor.fromRow);
7456
+ const rightArea = (right.control.anchor.toCol - right.control.anchor.fromCol) * (right.control.anchor.toRow - right.control.anchor.fromRow);
7457
+ return leftArea - rightArea;
7458
+ });
7459
+ const containingGroup = containingGroups[0]?.control;
7460
+ if (containingGroup) {
7461
+ return controls.flatMap((control, index) => control.kind.kind === "optionButton" && anchorContainsControl(containingGroup, control) ? [index] : []);
7462
+ }
7463
+ const optionIndexes = controls.flatMap((control, index) => control.kind.kind === "optionButton" ? [index] : []);
7464
+ const targetPosition = optionIndexes.indexOf(targetIndex);
7465
+ if (targetPosition < 0) {
7466
+ return [];
7467
+ }
7468
+ let startPosition = targetPosition;
7469
+ while (startPosition > 0) {
7470
+ const control = controls[optionIndexes[startPosition] ?? -1];
7471
+ if (control?.kind.kind === "optionButton" && control.kind.firstInGroup) {
7472
+ break;
7473
+ }
7474
+ startPosition -= 1;
7475
+ }
7476
+ let endPosition = targetPosition + 1;
7477
+ while (endPosition < optionIndexes.length) {
7478
+ const control = controls[optionIndexes[endPosition] ?? -1];
7479
+ if (control?.kind.kind === "optionButton" && control.kind.firstInGroup) {
7480
+ break;
7481
+ }
7482
+ endPosition += 1;
7483
+ }
7484
+ return optionIndexes.slice(startPosition, endPosition);
7485
+ }
7486
+ function resolveLinkedCellValue(controls, controlIndex) {
7487
+ const control = controls[controlIndex];
7488
+ if (!control) {
7489
+ return void 0;
7490
+ }
7491
+ switch (control.kind.kind) {
7492
+ case "checkbox":
7493
+ return control.kind.state === "mixed" ? "#N/A" : control.kind.state === "checked";
7494
+ case "optionButton": {
7495
+ const groupIndexes = resolveOptionButtonGroupIndexes(controls, controlIndex);
7496
+ const checkedIndex = groupIndexes.find((index) => controls[index]?.kind.kind === "optionButton" && controls[index]?.kind.state === "checked");
7497
+ const groupPosition = checkedIndex === void 0 ? -1 : groupIndexes.indexOf(checkedIndex);
7498
+ return groupPosition >= 0 ? groupPosition + 1 : "";
7499
+ }
7500
+ case "dropdown":
7501
+ return control.kind.selected === void 0 ? "" : control.kind.selected + 1;
7502
+ case "listBox":
7503
+ return control.kind.selection === "single" && control.kind.selected.length === 1 ? (control.kind.selected[0] ?? -1) + 1 : "";
7504
+ case "scrollbar":
7505
+ case "spinner":
7506
+ return control.kind.value;
7507
+ default:
7508
+ return void 0;
7509
+ }
7510
+ }
7511
+ function syncFormControlLinkedCell(workbook, workbookSheetIndex, controls, controlIndex) {
7512
+ const control = controls[controlIndex];
7513
+ if (!control) {
7514
+ return;
7515
+ }
7516
+ const cellLink = getFormControlCellLink(control.kind);
7517
+ const value = resolveLinkedCellValue(controls, controlIndex);
7518
+ if (!cellLink || value === void 0) {
7519
+ return;
7520
+ }
7521
+ const target = resolveWorkbookReference(workbook, workbookSheetIndex, cellLink);
7522
+ if (!target || target.range.start.row !== target.range.end.row || target.range.start.col !== target.range.end.col) {
7523
+ return;
7524
+ }
7525
+ target.worksheet.setCell(cellAddressToA1(target.range.start), value);
7526
+ }
7202
7527
  function parseWorksheetFreezePanes(worksheet) {
7203
7528
  const rawFreezePanes = worksheet.freezePanes;
7204
7529
  const row = typeof rawFreezePanes?.row === "number" && rawFreezePanes.row >= 0 ? rawFreezePanes.row : null;
@@ -7965,7 +8290,7 @@ function createBasicWorkbookAssets(workbook) {
7965
8290
  const objectUrls = [];
7966
8291
  return {
7967
8292
  archive: {},
7968
- formControlsByWorkbookSheetIndex: Array.from({ length: workbook.sheetCount }, () => []),
8293
+ formControlsByWorkbookSheetIndex: collectWorkbookFormControls(workbook),
7969
8294
  imageOriginsById: /* @__PURE__ */ new Map(),
7970
8295
  imagesByWorkbookSheetIndex: collectWorksheetApiImages(workbook, objectUrls),
7971
8296
  namedCellStyleByName: {},
@@ -7983,7 +8308,7 @@ function loadWorkbookImageAssets(bytes, workbook, skipXmlParsing = false) {
7983
8308
  if (shouldSkipXmlParsingForWorkbook(bytes, skipXmlParsing) || !isZipWorkbook(bytes)) {
7984
8309
  return createBasicWorkbookAssets(workbook);
7985
8310
  }
7986
- const parsedAssets = parseWorkbookImageAssets(bytes);
8311
+ const parsedAssets = parseWorkbookImageAssets(bytes, workbook);
7987
8312
  const apiImagesByWorkbookSheetIndex = collectWorksheetApiImages(workbook, parsedAssets.objectUrls);
7988
8313
  const imagesByWorkbookSheetIndex = Array.from(
7989
8314
  { length: Math.max(workbook.sheetCount, parsedAssets.imagesByWorkbookSheetIndex.length, apiImagesByWorkbookSheetIndex.length) },
@@ -8300,6 +8625,12 @@ function useXlsxViewerController(options) {
8300
8625
  };
8301
8626
  }, [skipXmlParsing]);
8302
8627
  const refreshWorkbookState = React.useCallback((targetWorkbook) => {
8628
+ const currentFormControls = imageAssetsRef.current?.formControlsByWorkbookSheetIndex ?? [];
8629
+ const nextFormControls = refreshWorkbookFormControls(targetWorkbook, currentFormControls);
8630
+ if (imageAssetsRef.current) {
8631
+ imageAssetsRef.current.formControlsByWorkbookSheetIndex = nextFormControls;
8632
+ }
8633
+ setFormControlsByWorkbookSheetIndex(nextFormControls);
8303
8634
  const nextSheets = buildSheetList(
8304
8635
  targetWorkbook,
8305
8636
  imageAssetsRef.current?.sheetStatesByWorkbookSheetIndex,
@@ -8427,6 +8758,7 @@ function useXlsxViewerController(options) {
8427
8758
  setChartsheets(snapshot.chartsheets);
8428
8759
  setTabs(snapshot.tabs);
8429
8760
  chartAssetsRef.current = null;
8761
+ setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8430
8762
  setWorkerTablesByWorkbookSheetIndex(snapshot.tablesByWorkbookSheetIndex);
8431
8763
  setShouldAutoCalculate(false);
8432
8764
  setIsWorkerBacked(true);
@@ -8646,6 +8978,7 @@ function useXlsxViewerController(options) {
8646
8978
  setChartsheets(snapshot.chartsheets);
8647
8979
  setTabs(snapshot.tabs);
8648
8980
  chartAssetsRef.current = null;
8981
+ setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8649
8982
  setWorkerTablesByWorkbookSheetIndex(snapshot.tablesByWorkbookSheetIndex);
8650
8983
  setShouldAutoCalculate(false);
8651
8984
  setIsWorkerBacked(true);
@@ -8767,6 +9100,20 @@ function useXlsxViewerController(options) {
8767
9100
  }
8768
9101
  return workbook.getSheet(activeSheet.workbookSheetIndex);
8769
9102
  }, [activeSheet, workbook]);
9103
+ const getFormControlWorksheet = React.useCallback((sheetIndex = activeSheetIndex) => {
9104
+ const targetSheet = sheets[sheetIndex];
9105
+ if (!workbook || !targetSheet) {
9106
+ return null;
9107
+ }
9108
+ try {
9109
+ return {
9110
+ workbookSheetIndex: targetSheet.workbookSheetIndex,
9111
+ worksheet: workbook.getSheet(targetSheet.workbookSheetIndex)
9112
+ };
9113
+ } catch {
9114
+ return null;
9115
+ }
9116
+ }, [activeSheetIndex, sheets, workbook]);
8770
9117
  const tables = React.useMemo(
8771
9118
  () => isWorkerBacked ? workerTablesByWorkbookSheetIndex[activeSheet?.workbookSheetIndex ?? -1] ?? [] : mapWorksheetTables(getActiveWorksheet()),
8772
9119
  [activeSheet?.workbookSheetIndex, getActiveWorksheet, isWorkerBacked, revision, workerTablesByWorkbookSheetIndex]
@@ -8906,6 +9253,28 @@ function useXlsxViewerController(options) {
8906
9253
  return (formControlsByWorkbookSheetIndex[targetSheet.workbookSheetIndex] ?? []).map(mapPublicFormControl);
8907
9254
  }, [activeSheetIndex, formControlsByWorkbookSheetIndex, mapPublicFormControl, sheets]);
8908
9255
  const formControls = React.useMemo(() => getSheetFormControls(activeSheetIndex), [activeSheetIndex, getSheetFormControls]);
9256
+ const getFormControlItems = React.useCallback((controlIndex, sheetIndex = activeSheetIndex) => {
9257
+ const target = getFormControlWorksheet(sheetIndex);
9258
+ if (!workbook || !target) {
9259
+ return [];
9260
+ }
9261
+ const control = target.worksheet.formControls[controlIndex];
9262
+ const inputRange = control?.kind.kind === "listBox" || control?.kind.kind === "dropdown" ? control.kind.inputRange : void 0;
9263
+ if (!inputRange) {
9264
+ return [];
9265
+ }
9266
+ const source = resolveWorkbookReference(workbook, target.workbookSheetIndex, inputRange);
9267
+ if (!source) {
9268
+ return [];
9269
+ }
9270
+ const items = [];
9271
+ for (let row = source.range.start.row; row <= source.range.end.row; row += 1) {
9272
+ for (let col = source.range.start.col; col <= source.range.end.col; col += 1) {
9273
+ items.push(source.worksheet.getFormattedValueAt(row, col));
9274
+ }
9275
+ }
9276
+ return items;
9277
+ }, [activeSheetIndex, getFormControlWorksheet, workbook]);
8909
9278
  const getSheetShapes = React.useCallback((sheetIndex = activeSheetIndex) => {
8910
9279
  const targetSheet = sheets[sheetIndex];
8911
9280
  if (!targetSheet) {
@@ -9303,6 +9672,89 @@ function useXlsxViewerController(options) {
9303
9672
  redoStackRef.current = [];
9304
9673
  setHistoryRevision((current) => current + 1);
9305
9674
  }, [createHistoryEntry]);
9675
+ const addFormControl = React.useCallback((input, sheetIndex = activeSheetIndex) => {
9676
+ const target = getFormControlWorksheet(sheetIndex);
9677
+ if (readOnly || !workbook || !target) {
9678
+ return null;
9679
+ }
9680
+ recordHistoryBeforeMutation();
9681
+ const controlIndex = target.worksheet.addFormControl(input);
9682
+ const controls = target.worksheet.formControls;
9683
+ const addedControl = controls[controlIndex];
9684
+ if (addedControl?.kind.kind === "optionButton" && addedControl.kind.state === "checked") {
9685
+ const groupIndexes = resolveOptionButtonGroupIndexes(controls, controlIndex);
9686
+ for (const siblingIndex of groupIndexes) {
9687
+ if (siblingIndex === controlIndex) {
9688
+ continue;
9689
+ }
9690
+ const sibling = target.worksheet.formControls[siblingIndex];
9691
+ if (sibling?.kind.kind === "optionButton" && sibling.kind.state !== "unchecked") {
9692
+ target.worksheet.setFormControl(siblingIndex, {
9693
+ ...formControlInputFromDukeControl(sibling),
9694
+ kind: { ...sibling.kind, state: "unchecked" }
9695
+ });
9696
+ }
9697
+ }
9698
+ }
9699
+ syncFormControlLinkedCell(
9700
+ workbook,
9701
+ target.workbookSheetIndex,
9702
+ target.worksheet.formControls,
9703
+ controlIndex
9704
+ );
9705
+ maybeRecalculateWorkbook(workbook);
9706
+ refreshWorkbookState(workbook);
9707
+ return controlIndex;
9708
+ }, [activeSheetIndex, getFormControlWorksheet, maybeRecalculateWorkbook, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9709
+ const updateFormControl = React.useCallback((controlIndex, patch, sheetIndex = activeSheetIndex) => {
9710
+ const target = getFormControlWorksheet(sheetIndex);
9711
+ const currentControl = target?.worksheet.formControls[controlIndex];
9712
+ if (readOnly || !workbook || !target || !currentControl) {
9713
+ return false;
9714
+ }
9715
+ recordHistoryBeforeMutation();
9716
+ const nextInput = {
9717
+ ...formControlInputFromDukeControl(currentControl),
9718
+ ...patch,
9719
+ anchor: patch.anchor ? { ...patch.anchor } : { ...currentControl.anchor },
9720
+ kind: patch.kind ? { ...patch.kind } : { ...currentControl.kind }
9721
+ };
9722
+ if (nextInput.kind.kind === "optionButton" && nextInput.kind.state === "checked") {
9723
+ const groupIndexes = resolveOptionButtonGroupIndexes(target.worksheet.formControls, controlIndex);
9724
+ for (const siblingIndex of groupIndexes) {
9725
+ if (siblingIndex === controlIndex) {
9726
+ continue;
9727
+ }
9728
+ const sibling = target.worksheet.formControls[siblingIndex];
9729
+ if (sibling?.kind.kind === "optionButton" && sibling.kind.state !== "unchecked") {
9730
+ target.worksheet.setFormControl(siblingIndex, {
9731
+ ...formControlInputFromDukeControl(sibling),
9732
+ kind: { ...sibling.kind, state: "unchecked" }
9733
+ });
9734
+ }
9735
+ }
9736
+ }
9737
+ target.worksheet.setFormControl(controlIndex, nextInput);
9738
+ syncFormControlLinkedCell(
9739
+ workbook,
9740
+ target.workbookSheetIndex,
9741
+ target.worksheet.formControls,
9742
+ controlIndex
9743
+ );
9744
+ maybeRecalculateWorkbook(workbook);
9745
+ refreshWorkbookState(workbook);
9746
+ return true;
9747
+ }, [activeSheetIndex, getFormControlWorksheet, maybeRecalculateWorkbook, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9748
+ const removeFormControl = React.useCallback((controlIndex, sheetIndex = activeSheetIndex) => {
9749
+ const target = getFormControlWorksheet(sheetIndex);
9750
+ if (readOnly || !workbook || !target || !target.worksheet.formControls[controlIndex]) {
9751
+ return false;
9752
+ }
9753
+ recordHistoryBeforeMutation();
9754
+ target.worksheet.removeFormControl(controlIndex);
9755
+ refreshWorkbookState(workbook);
9756
+ return true;
9757
+ }, [activeSheetIndex, getFormControlWorksheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9306
9758
  const recordCellEditHistory = React.useCallback((cell, before, after) => {
9307
9759
  if (!activeSheet || isApplyingHistoryRef.current) {
9308
9760
  return;
@@ -10111,8 +10563,15 @@ function useXlsxViewerController(options) {
10111
10563
  }
10112
10564
  workbook.addSheet(candidate);
10113
10565
  sheetOriginsRef.current = [...sheetOriginsRef.current, null];
10566
+ setFormControlsByWorkbookSheetIndex((current) => [...current, []]);
10114
10567
  setImagesByWorkbookSheetIndex((current) => [...current, []]);
10115
10568
  setShapesByWorkbookSheetIndex((current) => [...current, []]);
10569
+ if (imageAssetsRef.current) {
10570
+ imageAssetsRef.current.formControlsByWorkbookSheetIndex = [
10571
+ ...imageAssetsRef.current.formControlsByWorkbookSheetIndex,
10572
+ []
10573
+ ];
10574
+ }
10116
10575
  const nextSheets = buildSheetList(
10117
10576
  workbook,
10118
10577
  imageAssetsRef.current?.sheetStatesByWorkbookSheetIndex,
@@ -10142,9 +10601,13 @@ function useXlsxViewerController(options) {
10142
10601
  recordHistoryBeforeMutation();
10143
10602
  workbook.removeSheet(activeSheet.workbookSheetIndex);
10144
10603
  sheetOriginsRef.current = sheetOriginsRef.current.filter((_, index) => index !== activeSheet.workbookSheetIndex);
10604
+ setFormControlsByWorkbookSheetIndex((current) => current.filter((_, index) => index !== activeSheet.workbookSheetIndex));
10145
10605
  setImagesByWorkbookSheetIndex((current) => current.filter((_, index) => index !== activeSheet.workbookSheetIndex));
10146
10606
  setShapesByWorkbookSheetIndex((current) => current.filter((_, index) => index !== activeSheet.workbookSheetIndex));
10147
10607
  if (imageAssetsRef.current) {
10608
+ imageAssetsRef.current.formControlsByWorkbookSheetIndex = imageAssetsRef.current.formControlsByWorkbookSheetIndex.filter(
10609
+ (_, index) => index !== activeSheet.workbookSheetIndex
10610
+ );
10148
10611
  imageAssetsRef.current.sheetStatesByWorkbookSheetIndex = imageAssetsRef.current.sheetStatesByWorkbookSheetIndex.filter(
10149
10612
  (_, index) => index !== activeSheet.workbookSheetIndex
10150
10613
  );
@@ -10445,6 +10908,7 @@ function useXlsxViewerController(options) {
10445
10908
  activeSheetIndex,
10446
10909
  activeTab,
10447
10910
  activeTabIndex,
10911
+ addFormControl,
10448
10912
  addSheet,
10449
10913
  canRedo,
10450
10914
  canDownload: Boolean(file ?? src),
@@ -10484,6 +10948,7 @@ function useXlsxViewerController(options) {
10484
10948
  getClipboardData,
10485
10949
  getCellDisplayValue: getCellDisplayValue2,
10486
10950
  getCellFormula,
10951
+ getFormControlItems,
10487
10952
  getCellSnapshotAsync: isWorkerBacked ? getCellSnapshotAsync : void 0,
10488
10953
  getActiveWorksheet,
10489
10954
  getRowsBatchAsync: isWorkerBacked ? getRowsBatchAsync : void 0,
@@ -10501,6 +10966,7 @@ function useXlsxViewerController(options) {
10501
10966
  pasteStructuredClipboardData,
10502
10967
  pasteText,
10503
10968
  removeActiveSheet,
10969
+ removeFormControl,
10504
10970
  readOnly,
10505
10971
  recalculate,
10506
10972
  redo,
@@ -10551,6 +11017,7 @@ function useXlsxViewerController(options) {
10551
11017
  undo,
10552
11018
  unmergeSelection,
10553
11019
  updateChart,
11020
+ updateFormControl,
10554
11021
  workbook,
10555
11022
  zoomIn,
10556
11023
  zoomOut,
@@ -10563,6 +11030,7 @@ function useXlsxViewerController(options) {
10563
11030
  activeSheetIndex,
10564
11031
  activeTab,
10565
11032
  activeTabIndex,
11033
+ addFormControl,
10566
11034
  addSheet,
10567
11035
  canLoadDeferred,
10568
11036
  canRedo,
@@ -10599,6 +11067,7 @@ function useXlsxViewerController(options) {
10599
11067
  getClipboardData,
10600
11068
  getCellDisplayValue2,
10601
11069
  getCellFormula,
11070
+ getFormControlItems,
10602
11071
  getCellSnapshotAsync,
10603
11072
  getActiveWorksheet,
10604
11073
  historyRevision,
@@ -10616,6 +11085,7 @@ function useXlsxViewerController(options) {
10616
11085
  pasteStructuredClipboardData,
10617
11086
  pasteText,
10618
11087
  removeActiveSheet,
11088
+ removeFormControl,
10619
11089
  readOnly,
10620
11090
  recalculate,
10621
11091
  redo,
@@ -10666,6 +11136,7 @@ function useXlsxViewerController(options) {
10666
11136
  undo,
10667
11137
  unmergeSelection,
10668
11138
  updateChart,
11139
+ updateFormControl,
10669
11140
  workbook,
10670
11141
  zoomIn,
10671
11142
  zoomOut,
@@ -21637,13 +22108,90 @@ function renderConditionalIcon(icon, scale = 1) {
21637
22108
  }
21638
22109
  );
21639
22110
  }
21640
- function renderCheckboxControl(checked, palette, scale = 1) {
22111
+ function isFormControlChecked(control) {
22112
+ return control.state ? control.state === "checked" : Boolean(control.checked);
22113
+ }
22114
+ function isFormControlMixed(control) {
22115
+ return control.state === "mixed";
22116
+ }
22117
+ function buildFormControlKindInput(control) {
22118
+ switch (control.kind) {
22119
+ case "button":
22120
+ return { caption: control.caption ?? control.label ?? "", kind: "button" };
22121
+ case "checkbox":
22122
+ return {
22123
+ caption: control.caption ?? control.label ?? "",
22124
+ cellLink: control.linkedCell,
22125
+ kind: "checkbox",
22126
+ no3D: control.no3D ?? false,
22127
+ state: control.state ?? (control.checked ? "checked" : "unchecked")
22128
+ };
22129
+ case "radio":
22130
+ return {
22131
+ caption: control.caption ?? control.label ?? "",
22132
+ cellLink: control.linkedCell,
22133
+ firstInGroup: control.firstInGroup,
22134
+ kind: "optionButton",
22135
+ no3D: control.no3D ?? false,
22136
+ state: control.state === "checked" ? "checked" : "unchecked"
22137
+ };
22138
+ case "label":
22139
+ return { caption: control.caption ?? control.label ?? "", kind: "label" };
22140
+ case "group-box":
22141
+ return {
22142
+ caption: control.caption ?? control.label ?? "",
22143
+ kind: "groupBox",
22144
+ no3D: control.no3D ?? false
22145
+ };
22146
+ case "listbox":
22147
+ return {
22148
+ cellLink: control.linkedCell,
22149
+ inputRange: control.inputRange,
22150
+ kind: "listBox",
22151
+ no3D: control.no3D ?? false,
22152
+ selected: Array.isArray(control.selected) ? control.selected : control.selected === void 0 ? [] : [control.selected],
22153
+ selection: control.selection ?? "single"
22154
+ };
22155
+ case "dropdown":
22156
+ return {
22157
+ cellLink: control.linkedCell,
22158
+ inputRange: control.inputRange,
22159
+ kind: "dropdown",
22160
+ lines: control.lines ?? 8,
22161
+ no3D: control.no3D ?? false,
22162
+ selected: typeof control.selected === "number" ? control.selected : void 0
22163
+ };
22164
+ case "scrollbar":
22165
+ return {
22166
+ cellLink: control.linkedCell,
22167
+ horizontal: control.horizontal ?? true,
22168
+ increment: control.increment ?? 1,
22169
+ kind: "scrollbar",
22170
+ max: control.max ?? 100,
22171
+ min: control.min ?? 0,
22172
+ page: control.page ?? 10,
22173
+ value: control.value ?? control.min ?? 0
22174
+ };
22175
+ case "spinner":
22176
+ return {
22177
+ cellLink: control.linkedCell,
22178
+ increment: control.increment ?? 1,
22179
+ kind: "spinner",
22180
+ max: control.max ?? 100,
22181
+ min: control.min ?? 0,
22182
+ value: control.value ?? control.min ?? 0
22183
+ };
22184
+ default:
22185
+ return null;
22186
+ }
22187
+ }
22188
+ function renderCheckboxControl(checked, palette, scale = 1, mixed = false) {
21641
22189
  const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
21642
- const fill = checked ? paletteIsDark(palette) ? "#60a5fa" : "#2563eb" : "transparent";
22190
+ const fill = checked || mixed ? paletteIsDark(palette) ? "#60a5fa" : "#2563eb" : "transparent";
21643
22191
  const check = paletteIsDark(palette) ? "#020617" : "#ffffff";
21644
22192
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { "aria-hidden": "true", height: 14 * scale, style: { display: "block" }, viewBox: "0 0 16 16", width: 14 * scale, children: [
21645
22193
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { fill, height: 11, rx: 2, ry: 2, stroke, strokeWidth: 1.2, width: 11, x: 2.5, y: 2.5 }),
21646
- checked ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
22194
+ mixed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M5 8h6", fill: "none", stroke: check, strokeLinecap: "round", strokeWidth: 1.8 }) : checked ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
21647
22195
  "path",
21648
22196
  {
21649
22197
  d: "M5 8.1 7.1 10.2 11.3 5.8",
@@ -21813,6 +22361,8 @@ function drawStaticFormControl(context, control, rect, palette, zoomFactor, shee
21813
22361
  const textColor = control.textColor ?? "#000000";
21814
22362
  const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
21815
22363
  const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
22364
+ const controlChecked = isFormControlChecked(control);
22365
+ const controlMixed = isFormControlMixed(control);
21816
22366
  context.save();
21817
22367
  context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
21818
22368
  context.textBaseline = "middle";
@@ -21853,14 +22403,19 @@ function drawStaticFormControl(context, control, rect, palette, zoomFactor, shee
21853
22403
  const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
21854
22404
  if (control.kind === "checkbox") {
21855
22405
  context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
21856
- if (control.checked) {
22406
+ if (controlChecked || controlMixed) {
21857
22407
  context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
21858
22408
  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));
21859
22409
  context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
21860
22410
  context.beginPath();
21861
- context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
21862
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
21863
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
22411
+ if (controlMixed) {
22412
+ context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.25, rect.top + rect.height / 2);
22413
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.75, rect.top + rect.height / 2);
22414
+ } else {
22415
+ context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
22416
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
22417
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
22418
+ }
21864
22419
  context.stroke();
21865
22420
  }
21866
22421
  textLeft += iconSize + 4 * zoomFactor;
@@ -21868,7 +22423,7 @@ function drawStaticFormControl(context, control, rect, palette, zoomFactor, shee
21868
22423
  context.beginPath();
21869
22424
  context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
21870
22425
  context.stroke();
21871
- if (control.checked) {
22426
+ if (controlChecked) {
21872
22427
  context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
21873
22428
  context.beginPath();
21874
22429
  context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
@@ -22504,6 +23059,8 @@ function XlsxGrid({
22504
23059
  getCellStyle,
22505
23060
  loadingComponent,
22506
23061
  loadingState,
23062
+ onFormControlAction,
23063
+ onFormControlChange,
22507
23064
  renderChartLoading,
22508
23065
  palette,
22509
23066
  renderImage,
@@ -22541,6 +23098,7 @@ function XlsxGrid({
22541
23098
  getRowsBatchAsync,
22542
23099
  getClipboardData,
22543
23100
  getCellDisplayValue: getControllerCellDisplayValue,
23101
+ getFormControlItems,
22544
23102
  images,
22545
23103
  shapes,
22546
23104
  isLoadDeferred,
@@ -22578,9 +23136,13 @@ function XlsxGrid({
22578
23136
  sortTable,
22579
23137
  tables,
22580
23138
  undo,
23139
+ updateFormControl,
22581
23140
  workbook,
22582
23141
  zoomScale
22583
23142
  } = controller;
23143
+ const formControlItemsById = React4.useMemo(() => new Map(
23144
+ formControls.flatMap((control) => control.controlIndex !== void 0 && (control.kind === "dropdown" || control.kind === "listbox") ? [[control.id, getFormControlItems(control.controlIndex, control.sheetIndex)]] : [])
23145
+ ), [formControls, getFormControlItems]);
22584
23146
  const canResizeHeaders = !readOnly || allowResizeInReadOnly;
22585
23147
  const scrollRef = React4.useRef(null);
22586
23148
  const wrapperRef = React4.useRef(null);
@@ -25088,7 +25650,7 @@ function XlsxGrid({
25088
25650
  return "";
25089
25651
  }
25090
25652
  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("|");
25091
- const controlSignature = bakedFormControlRects.map(({ control, rect }) => `f:${control.id}:${control.zIndex}:${Boolean(control.checked)}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
25653
+ 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("|");
25092
25654
  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("|");
25093
25655
  return `${revision}:${canvasImageLoadVersion}:${shapeSignature}:${controlSignature}:${imageSignature}`;
25094
25656
  }, [
@@ -26789,6 +27351,8 @@ function XlsxGrid({
26789
27351
  const textColor = control.textColor ?? "#000000";
26790
27352
  const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
26791
27353
  const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
27354
+ const controlChecked = isFormControlChecked(control);
27355
+ const controlMixed = isFormControlMixed(control);
26792
27356
  context.save();
26793
27357
  context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
26794
27358
  context.textBaseline = "middle";
@@ -26829,14 +27393,19 @@ function XlsxGrid({
26829
27393
  const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
26830
27394
  if (control.kind === "checkbox") {
26831
27395
  context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
26832
- if (control.checked) {
27396
+ if (controlChecked || controlMixed) {
26833
27397
  context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
26834
27398
  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));
26835
27399
  context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
26836
27400
  context.beginPath();
26837
- context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
26838
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
26839
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
27401
+ if (controlMixed) {
27402
+ context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.25, rect.top + rect.height / 2);
27403
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.75, rect.top + rect.height / 2);
27404
+ } else {
27405
+ context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
27406
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
27407
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
27408
+ }
26840
27409
  context.stroke();
26841
27410
  }
26842
27411
  textLeft += iconSize + 4 * zoomFactor;
@@ -26844,7 +27413,7 @@ function XlsxGrid({
26844
27413
  context.beginPath();
26845
27414
  context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
26846
27415
  context.stroke();
26847
- if (control.checked) {
27416
+ if (controlChecked) {
26848
27417
  context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
26849
27418
  context.beginPath();
26850
27419
  context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
@@ -28159,10 +28728,22 @@ function XlsxGrid({
28159
28728
  const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
28160
28729
  const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
28161
28730
  const textColor = control.textColor ?? "#000000";
28731
+ const canMutateControl = !readOnly && control.controlIndex !== void 0 && [
28732
+ "checkbox",
28733
+ "dropdown",
28734
+ "listbox",
28735
+ "radio",
28736
+ "scrollbar",
28737
+ "spinner"
28738
+ ].includes(control.kind);
28739
+ const canActivateButton = !readOnly && control.kind === "button" && Boolean(onFormControlAction);
28740
+ const isInteractiveControl = canMutateControl || canActivateButton;
28741
+ const controlItems = formControlItemsById.get(control.id) ?? [];
28162
28742
  const commonStyle = {
28163
28743
  alignItems: control.kind === "group-box" ? "stretch" : "center",
28164
28744
  color: textColor,
28165
28745
  contain: "layout paint",
28746
+ cursor: isInteractiveControl ? "pointer" : void 0,
28166
28747
  display: "flex",
28167
28748
  fontFamily: control.fontFamily ?? "Calibri, sans-serif",
28168
28749
  fontSize: fontSizePx,
@@ -28173,12 +28754,28 @@ function XlsxGrid({
28173
28754
  left: rect.left,
28174
28755
  lineHeight: 1.2,
28175
28756
  overflow: "hidden",
28176
- pointerEvents: "none",
28757
+ pointerEvents: isInteractiveControl ? "auto" : "none",
28177
28758
  position: "absolute",
28178
28759
  top: rect.top,
28179
28760
  width: rect.width,
28180
28761
  zIndex: isFrozenDrawing ? control.zIndex + 20 : control.zIndex
28181
28762
  };
28763
+ const stopControlPointerEvent = (event) => {
28764
+ event.stopPropagation();
28765
+ };
28766
+ const commitControlKind = (kind, nextControl) => {
28767
+ if (control.controlIndex === void 0) {
28768
+ return;
28769
+ }
28770
+ const didUpdate = updateFormControl(control.controlIndex, { kind }, control.sheetIndex);
28771
+ if (didUpdate) {
28772
+ onFormControlChange?.({
28773
+ control: { ...control, ...nextControl },
28774
+ previousControl: control,
28775
+ type: "change"
28776
+ });
28777
+ }
28778
+ };
28182
28779
  if (control.kind === "group-box") {
28183
28780
  const hasLabel = controlLabel.length > 0;
28184
28781
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -28220,15 +28817,88 @@ function XlsxGrid({
28220
28817
  `${pane}-${control.id}`
28221
28818
  );
28222
28819
  }
28223
- let content;
28224
28820
  if (control.kind === "radio") {
28225
- content = renderRadioControl(Boolean(control.checked), palette, zoomFactor);
28226
- } else if (control.kind === "checkbox") {
28227
- content = renderCheckboxControl(Boolean(control.checked), palette, zoomFactor);
28228
- } else if (control.kind === "button") {
28821
+ const checked = isFormControlChecked(control);
28822
+ const kind = buildFormControlKindInput(control);
28823
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
28824
+ "button",
28825
+ {
28826
+ "aria-checked": checked,
28827
+ disabled: readOnly || control.controlIndex === void 0,
28828
+ onClick: (event) => {
28829
+ event.stopPropagation();
28830
+ if (!checked && kind?.kind === "optionButton") {
28831
+ commitControlKind(
28832
+ { ...kind, state: "checked" },
28833
+ { checked: true, state: "checked" }
28834
+ );
28835
+ }
28836
+ },
28837
+ onPointerDown: stopControlPointerEvent,
28838
+ role: "radio",
28839
+ style: {
28840
+ ...commonStyle,
28841
+ background: "transparent",
28842
+ border: 0,
28843
+ boxSizing: "border-box",
28844
+ padding: `0 ${Math.max(1, zoomFactor)}px`
28845
+ },
28846
+ type: "button",
28847
+ children: [
28848
+ renderRadioControl(checked, palette, zoomFactor),
28849
+ controlLabel ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: controlLabel }) : null
28850
+ ]
28851
+ },
28852
+ `${pane}-${control.id}`
28853
+ );
28854
+ }
28855
+ if (control.kind === "checkbox") {
28856
+ const checked = isFormControlChecked(control);
28857
+ const mixed = isFormControlMixed(control);
28858
+ const kind = buildFormControlKindInput(control);
28859
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
28860
+ "button",
28861
+ {
28862
+ "aria-checked": mixed ? "mixed" : checked,
28863
+ disabled: readOnly || control.controlIndex === void 0,
28864
+ onClick: (event) => {
28865
+ event.stopPropagation();
28866
+ if (kind?.kind === "checkbox") {
28867
+ const nextState = checked && !mixed ? "unchecked" : "checked";
28868
+ commitControlKind(
28869
+ { ...kind, state: nextState },
28870
+ { checked: nextState === "checked", state: nextState }
28871
+ );
28872
+ }
28873
+ },
28874
+ onPointerDown: stopControlPointerEvent,
28875
+ role: "checkbox",
28876
+ style: {
28877
+ ...commonStyle,
28878
+ background: "transparent",
28879
+ border: 0,
28880
+ boxSizing: "border-box",
28881
+ padding: `0 ${Math.max(1, zoomFactor)}px`
28882
+ },
28883
+ type: "button",
28884
+ children: [
28885
+ renderCheckboxControl(checked, palette, zoomFactor, mixed),
28886
+ controlLabel ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: controlLabel }) : null
28887
+ ]
28888
+ },
28889
+ `${pane}-${control.id}`
28890
+ );
28891
+ }
28892
+ if (control.kind === "button") {
28229
28893
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
28230
- "div",
28894
+ "button",
28231
28895
  {
28896
+ disabled: readOnly || !onFormControlAction,
28897
+ onClick: (event) => {
28898
+ event.stopPropagation();
28899
+ onFormControlAction?.({ control, type: "activate" });
28900
+ },
28901
+ onPointerDown: stopControlPointerEvent,
28232
28902
  style: {
28233
28903
  ...commonStyle,
28234
28904
  background: "linear-gradient(180deg, #f8fafc 0%, #e2e8f0 100%)",
@@ -28239,68 +28909,168 @@ function XlsxGrid({
28239
28909
  padding: `0 ${6 * zoomFactor}px`,
28240
28910
  textAlign: "center"
28241
28911
  },
28912
+ type: "button",
28242
28913
  children: controlLabel
28243
28914
  },
28244
28915
  `${pane}-${control.id}`
28245
28916
  );
28246
- } else if (control.kind === "dropdown") {
28917
+ }
28918
+ if (control.kind === "dropdown") {
28919
+ const kind = buildFormControlKindInput(control);
28920
+ const selected = typeof control.selected === "number" ? control.selected : void 0;
28247
28921
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
28248
- "div",
28922
+ "select",
28249
28923
  {
28924
+ "aria-label": controlLabel || control.name || "Dropdown form control",
28925
+ disabled: readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28926
+ onChange: (event) => {
28927
+ event.stopPropagation();
28928
+ if (kind?.kind === "dropdown") {
28929
+ const nextSelected = event.currentTarget.value === "" ? void 0 : Number(event.currentTarget.value);
28930
+ commitControlKind(
28931
+ { ...kind, selected: nextSelected },
28932
+ { selected: nextSelected }
28933
+ );
28934
+ }
28935
+ },
28936
+ onClick: stopControlPointerEvent,
28937
+ onPointerDown: stopControlPointerEvent,
28250
28938
  style: {
28251
28939
  ...commonStyle,
28940
+ backgroundColor: SHEET_SURFACE,
28252
28941
  border: `${Math.max(1, zoomFactor)}px solid ${stroke}`,
28253
28942
  borderRadius: 2 * zoomFactor,
28254
28943
  boxSizing: "border-box",
28255
- justifyContent: "space-between",
28256
- padding: `0 ${6 * zoomFactor}px`
28944
+ padding: `0 ${4 * zoomFactor}px`
28257
28945
  },
28946
+ value: selected === void 0 ? "" : String(selected),
28258
28947
  children: [
28259
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: controlLabel }),
28260
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": "true", style: { fontSize: fontSizePx * 0.85 }, children: "\u25BC" })
28948
+ selected === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "", children: controlLabel }) : null,
28949
+ controlItems.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: index, children: item }, index))
28261
28950
  ]
28262
28951
  },
28263
28952
  `${pane}-${control.id}`
28264
28953
  );
28265
- } else if (control.kind === "editbox" || control.kind === "listbox" || control.kind === "scrollbar" || control.kind === "spinner" || control.kind === "unknown") {
28954
+ }
28955
+ if (control.kind === "listbox") {
28956
+ const kind = buildFormControlKindInput(control);
28957
+ const selected = Array.isArray(control.selected) ? control.selected : control.selected === void 0 ? [] : [control.selected];
28958
+ const allowsMultipleSelection = (control.selection ?? "single") !== "single";
28266
28959
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
28267
- "div",
28960
+ "select",
28268
28961
  {
28962
+ "aria-label": controlLabel || control.name || "List box form control",
28963
+ disabled: readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28964
+ multiple: allowsMultipleSelection,
28965
+ onChange: (event) => {
28966
+ event.stopPropagation();
28967
+ if (kind?.kind === "listBox") {
28968
+ const nextSelected = Array.from(event.currentTarget.selectedOptions, (option) => Number(option.value));
28969
+ commitControlKind(
28970
+ { ...kind, selected: nextSelected },
28971
+ { selected: nextSelected }
28972
+ );
28973
+ }
28974
+ },
28975
+ onClick: stopControlPointerEvent,
28976
+ onPointerDown: stopControlPointerEvent,
28977
+ size: Math.max(2, Math.min(controlItems.length || 2, Math.floor(rect.height / Math.max(1, fontSizePx * 1.45)))),
28269
28978
  style: {
28270
28979
  ...commonStyle,
28980
+ alignItems: void 0,
28981
+ backgroundColor: SHEET_SURFACE,
28271
28982
  border: `${Math.max(1, zoomFactor)}px solid ${stroke}`,
28272
28983
  borderRadius: 2 * zoomFactor,
28273
28984
  boxSizing: "border-box",
28274
- padding: `0 ${6 * zoomFactor}px`
28985
+ display: "block",
28986
+ padding: 0
28275
28987
  },
28276
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: controlLabel })
28988
+ value: allowsMultipleSelection ? selected.map(String) : selected[0] === void 0 ? "" : String(selected[0]),
28989
+ children: controlItems.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: index, children: item }, index))
28277
28990
  },
28278
28991
  `${pane}-${control.id}`
28279
28992
  );
28280
- } else {
28281
- content = null;
28282
28993
  }
28283
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
28994
+ if (control.kind === "scrollbar") {
28995
+ const kind = buildFormControlKindInput(control);
28996
+ const min = control.min ?? 0;
28997
+ const max = Math.max(min, control.max ?? 100);
28998
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
28999
+ "input",
29000
+ {
29001
+ "aria-label": controlLabel || control.name || "Scrollbar form control",
29002
+ disabled: readOnly || control.controlIndex === void 0,
29003
+ max,
29004
+ min,
29005
+ onChange: (event) => {
29006
+ event.stopPropagation();
29007
+ if (kind?.kind === "scrollbar") {
29008
+ const value = event.currentTarget.valueAsNumber;
29009
+ commitControlKind({ ...kind, value }, { value });
29010
+ }
29011
+ },
29012
+ onClick: stopControlPointerEvent,
29013
+ onPointerDown: stopControlPointerEvent,
29014
+ step: Math.max(1, control.increment ?? 1),
29015
+ style: {
29016
+ ...commonStyle,
29017
+ accentColor: paletteIsDark(palette) ? "#60a5fa" : "#2563eb",
29018
+ boxSizing: "border-box",
29019
+ padding: 0,
29020
+ writingMode: control.horizontal === false ? "vertical-lr" : void 0
29021
+ },
29022
+ type: "range",
29023
+ value: Math.max(min, Math.min(max, control.value ?? min))
29024
+ },
29025
+ `${pane}-${control.id}`
29026
+ );
29027
+ }
29028
+ if (control.kind === "spinner") {
29029
+ const kind = buildFormControlKindInput(control);
29030
+ const min = control.min ?? 0;
29031
+ const max = Math.max(min, control.max ?? 100);
29032
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
29033
+ "input",
29034
+ {
29035
+ "aria-label": controlLabel || control.name || "Spinner form control",
29036
+ disabled: readOnly || control.controlIndex === void 0,
29037
+ max,
29038
+ min,
29039
+ onChange: (event) => {
29040
+ event.stopPropagation();
29041
+ if (kind?.kind === "spinner" && Number.isFinite(event.currentTarget.valueAsNumber)) {
29042
+ const value = Math.max(min, Math.min(max, event.currentTarget.valueAsNumber));
29043
+ commitControlKind({ ...kind, value }, { value });
29044
+ }
29045
+ },
29046
+ onClick: stopControlPointerEvent,
29047
+ onPointerDown: stopControlPointerEvent,
29048
+ step: Math.max(1, control.increment ?? 1),
29049
+ style: {
29050
+ ...commonStyle,
29051
+ backgroundColor: SHEET_SURFACE,
29052
+ border: `${Math.max(1, zoomFactor)}px solid ${stroke}`,
29053
+ borderRadius: 2 * zoomFactor,
29054
+ boxSizing: "border-box",
29055
+ padding: `0 ${4 * zoomFactor}px`
29056
+ },
29057
+ type: "number",
29058
+ value: Math.max(min, Math.min(max, control.value ?? min))
29059
+ },
29060
+ `${pane}-${control.id}`
29061
+ );
29062
+ }
29063
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
28284
29064
  "div",
28285
29065
  {
28286
29066
  style: {
28287
29067
  ...commonStyle,
29068
+ border: control.kind === "editbox" || control.kind === "unknown" ? `${Math.max(1, zoomFactor)}px solid ${stroke}` : void 0,
29069
+ borderRadius: 2 * zoomFactor,
29070
+ boxSizing: "border-box",
28288
29071
  padding: `0 ${Math.max(1, zoomFactor)}px`
28289
29072
  },
28290
- children: [
28291
- content,
28292
- controlLabel ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
28293
- "span",
28294
- {
28295
- style: {
28296
- overflow: "hidden",
28297
- textOverflow: "ellipsis",
28298
- whiteSpace: "nowrap"
28299
- },
28300
- children: controlLabel
28301
- }
28302
- ) : null
28303
- ]
29073
+ children: controlLabel ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: controlLabel }) : null
28304
29074
  },
28305
29075
  `${pane}-${control.id}`
28306
29076
  );
@@ -29863,6 +30633,8 @@ function XlsxViewerInner({
29863
30633
  isDark = false,
29864
30634
  loadingComponent,
29865
30635
  loadingState,
30636
+ onFormControlAction,
30637
+ onFormControlChange,
29866
30638
  renderChartLoading,
29867
30639
  renderImage,
29868
30640
  renderImageSelection,
@@ -29925,6 +30697,8 @@ function XlsxViewerInner({
29925
30697
  getCellStyle,
29926
30698
  loadingComponent,
29927
30699
  loadingState,
30700
+ onFormControlAction,
30701
+ onFormControlChange,
29928
30702
  palette,
29929
30703
  renderChartLoading,
29930
30704
  renderImage,
@@ -30031,6 +30805,7 @@ function useXlsxViewerZoom() {
30031
30805
  }
30032
30806
  function useXlsxViewerEditing() {
30033
30807
  const {
30808
+ addFormControl,
30034
30809
  addSheet,
30035
30810
  canRedo,
30036
30811
  canUndo,
@@ -30038,14 +30813,18 @@ function useXlsxViewerEditing() {
30038
30813
  copySelectionToClipboard,
30039
30814
  defineNamedRange,
30040
30815
  fillSelection,
30816
+ formControls,
30041
30817
  getClipboardData,
30042
30818
  getCellDisplayValue: getCellDisplayValue2,
30043
30819
  getCellFormula,
30820
+ getFormControlItems,
30821
+ getSheetFormControls,
30044
30822
  mergeSelection,
30045
30823
  pasteFromClipboard,
30046
30824
  pasteStructuredClipboardData,
30047
30825
  pasteText,
30048
30826
  removeActiveSheet,
30827
+ removeFormControl,
30049
30828
  readOnly,
30050
30829
  redo,
30051
30830
  selectedCellFormula,
@@ -30062,10 +30841,12 @@ function useXlsxViewerEditing() {
30062
30841
  setSelectedCellStyle,
30063
30842
  setSelectedCellValue,
30064
30843
  undo,
30065
- unmergeSelection
30844
+ unmergeSelection,
30845
+ updateFormControl
30066
30846
  } = useXlsxViewer();
30067
30847
  return React4.useMemo(
30068
30848
  () => ({
30849
+ addFormControl,
30069
30850
  addSheet,
30070
30851
  canRedo,
30071
30852
  canUndo,
@@ -30073,14 +30854,18 @@ function useXlsxViewerEditing() {
30073
30854
  copySelectionToClipboard,
30074
30855
  defineNamedRange,
30075
30856
  fillSelection,
30857
+ formControls,
30076
30858
  getClipboardData,
30077
30859
  getCellDisplayValue: getCellDisplayValue2,
30078
30860
  getCellFormula,
30861
+ getFormControlItems,
30862
+ getSheetFormControls,
30079
30863
  mergeSelection,
30080
30864
  pasteFromClipboard,
30081
30865
  pasteStructuredClipboardData,
30082
30866
  pasteText,
30083
30867
  removeActiveSheet,
30868
+ removeFormControl,
30084
30869
  readOnly,
30085
30870
  redo,
30086
30871
  selectedCellFormula,
@@ -30097,9 +30882,11 @@ function useXlsxViewerEditing() {
30097
30882
  setSelectedCellStyle,
30098
30883
  setSelectedCellValue,
30099
30884
  undo,
30100
- unmergeSelection
30885
+ unmergeSelection,
30886
+ updateFormControl
30101
30887
  }),
30102
30888
  [
30889
+ addFormControl,
30103
30890
  addSheet,
30104
30891
  canRedo,
30105
30892
  canUndo,
@@ -30107,14 +30894,18 @@ function useXlsxViewerEditing() {
30107
30894
  copySelectionToClipboard,
30108
30895
  defineNamedRange,
30109
30896
  fillSelection,
30897
+ formControls,
30110
30898
  getClipboardData,
30111
30899
  getCellDisplayValue2,
30112
30900
  getCellFormula,
30901
+ getFormControlItems,
30902
+ getSheetFormControls,
30113
30903
  mergeSelection,
30114
30904
  pasteFromClipboard,
30115
30905
  pasteStructuredClipboardData,
30116
30906
  pasteText,
30117
30907
  removeActiveSheet,
30908
+ removeFormControl,
30118
30909
  readOnly,
30119
30910
  redo,
30120
30911
  selectedCellFormula,
@@ -30131,7 +30922,8 @@ function useXlsxViewerEditing() {
30131
30922
  setSelectedCellStyle,
30132
30923
  setSelectedCellValue,
30133
30924
  undo,
30134
- unmergeSelection
30925
+ unmergeSelection,
30926
+ updateFormControl
30135
30927
  ]
30136
30928
  );
30137
30929
  }