@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.js CHANGED
@@ -3592,7 +3592,6 @@ var SPREADSHEET_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
3592
3592
  var CONTENT_TYPES_NS = "http://schemas.openxmlformats.org/package/2006/content-types";
3593
3593
  var DRAWING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing";
3594
3594
  var VML_DRAWING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing";
3595
- var CTRL_PROP_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp";
3596
3595
  var IMAGE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
3597
3596
  var DRAWING_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.drawing+xml";
3598
3597
  var EMU_PER_PIXEL2 = 9525;
@@ -5035,6 +5034,124 @@ function parseSpreadsheetBooleanNode(node) {
5035
5034
  }
5036
5035
  return parseSpreadsheetBooleanValue(node.getAttribute("val") ?? node.textContent);
5037
5036
  }
5037
+ function mapDukeFormControlAnchor(anchor) {
5038
+ return {
5039
+ from: {
5040
+ col: anchor.fromCol,
5041
+ colOffsetEmu: anchor.fromColOffset,
5042
+ row: anchor.fromRow,
5043
+ rowOffsetEmu: anchor.fromRowOffset
5044
+ },
5045
+ kind: "two-cell",
5046
+ to: {
5047
+ col: anchor.toCol,
5048
+ colOffsetEmu: anchor.toColOffset,
5049
+ row: anchor.toRow,
5050
+ rowOffsetEmu: anchor.toRowOffset
5051
+ }
5052
+ };
5053
+ }
5054
+ function mapDukeFormControlKind(kind) {
5055
+ switch (kind.kind) {
5056
+ case "button":
5057
+ return { caption: kind.caption, kind: "button", label: normalizeControlLabel(kind.caption) };
5058
+ case "checkbox":
5059
+ return {
5060
+ caption: kind.caption,
5061
+ checked: kind.state === "checked",
5062
+ kind: "checkbox",
5063
+ label: normalizeControlLabel(kind.caption),
5064
+ linkedCell: kind.cellLink,
5065
+ no3D: kind.no3D,
5066
+ state: kind.state
5067
+ };
5068
+ case "optionButton":
5069
+ return {
5070
+ caption: kind.caption,
5071
+ checked: kind.state === "checked",
5072
+ firstInGroup: kind.firstInGroup,
5073
+ kind: "radio",
5074
+ label: normalizeControlLabel(kind.caption),
5075
+ linkedCell: kind.cellLink,
5076
+ no3D: kind.no3D,
5077
+ state: kind.state
5078
+ };
5079
+ case "label":
5080
+ return { caption: kind.caption, kind: "label", label: normalizeControlLabel(kind.caption) };
5081
+ case "groupBox":
5082
+ return {
5083
+ caption: kind.caption,
5084
+ kind: "group-box",
5085
+ label: normalizeControlLabel(kind.caption),
5086
+ no3D: kind.no3D
5087
+ };
5088
+ case "listBox":
5089
+ return {
5090
+ inputRange: kind.inputRange,
5091
+ kind: "listbox",
5092
+ linkedCell: kind.cellLink,
5093
+ no3D: kind.no3D,
5094
+ selected: [...kind.selected],
5095
+ selection: kind.selection
5096
+ };
5097
+ case "dropdown":
5098
+ return {
5099
+ inputRange: kind.inputRange,
5100
+ kind: "dropdown",
5101
+ lines: kind.lines,
5102
+ linkedCell: kind.cellLink,
5103
+ no3D: kind.no3D,
5104
+ selected: kind.selected
5105
+ };
5106
+ case "scrollbar":
5107
+ return {
5108
+ horizontal: kind.horizontal,
5109
+ increment: kind.increment,
5110
+ kind: "scrollbar",
5111
+ linkedCell: kind.cellLink,
5112
+ max: kind.max,
5113
+ min: kind.min,
5114
+ page: kind.page,
5115
+ value: kind.value
5116
+ };
5117
+ case "spinner":
5118
+ return {
5119
+ increment: kind.increment,
5120
+ kind: "spinner",
5121
+ linkedCell: kind.cellLink,
5122
+ max: kind.max,
5123
+ min: kind.min,
5124
+ value: kind.value
5125
+ };
5126
+ }
5127
+ }
5128
+ function mapDukeFormControl(control, controlIndex, workbookSheetIndex) {
5129
+ return {
5130
+ anchor: mapDukeFormControlAnchor(control.anchor),
5131
+ controlIndex,
5132
+ dukeAnchor: { ...control.anchor },
5133
+ editAs: control.anchor.editAs,
5134
+ hidden: false,
5135
+ id: `form-control-${workbookSheetIndex}-${controlIndex}`,
5136
+ locked: control.locked,
5137
+ name: control.name,
5138
+ printable: control.printable,
5139
+ sheetIndex: workbookSheetIndex,
5140
+ workbookSheetIndex,
5141
+ zIndex: controlIndex + 1,
5142
+ ...mapDukeFormControlKind(control.kind)
5143
+ };
5144
+ }
5145
+ function collectWorkbookFormControls(workbook) {
5146
+ return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {
5147
+ try {
5148
+ const controls = workbook.getSheet(workbookSheetIndex).formControls;
5149
+ return Array.isArray(controls) ? controls.map((control, controlIndex) => mapDukeFormControl(control, controlIndex, workbookSheetIndex)) : [];
5150
+ } catch {
5151
+ return [];
5152
+ }
5153
+ });
5154
+ }
5038
5155
  function parseFormControlKind(rawType) {
5039
5156
  const normalized = (rawType ?? "").trim().toLowerCase();
5040
5157
  switch (normalized) {
@@ -5800,27 +5917,10 @@ function parseSheetFormControlNodes(archive, sheetPath) {
5800
5917
  }
5801
5918
  return getLocalElements(sheetDocument, "control").map((controlNode) => ({
5802
5919
  anchor: parseAnchor(getFirstDescendant(controlNode, "anchor") ?? controlNode),
5803
- controlRelationshipId: getRelationshipId(controlNode),
5804
5920
  name: controlNode.getAttribute("name") ?? void 0,
5805
5921
  shapeId: parseFormControlShapeId(controlNode.getAttribute("shapeId"))
5806
5922
  }));
5807
5923
  }
5808
- function parseCtrlPropDocument(archive, ctrlPropPath) {
5809
- const xml = readArchiveText2(archive, ctrlPropPath);
5810
- if (!xml) {
5811
- return null;
5812
- }
5813
- const document2 = parseXml2(xml);
5814
- const root = document2?.documentElement;
5815
- if (!root) {
5816
- return null;
5817
- }
5818
- return {
5819
- checked: parseSpreadsheetBooleanValue(root.getAttribute("checked")),
5820
- linkedCell: root.getAttribute("fmlaLink") ?? void 0,
5821
- objectType: root.getAttribute("objectType") ?? void 0
5822
- };
5823
- }
5824
5924
  function parseVmlFormControls(archive, vmlDrawingPath) {
5825
5925
  const xml = readArchiveText2(archive, vmlDrawingPath);
5826
5926
  if (!xml) {
@@ -5878,21 +5978,18 @@ function parseSheetFormControls(archive, sheetPath, sheetRelationships, workbook
5878
5978
  if (!controlNode.anchor) {
5879
5979
  return;
5880
5980
  }
5881
- const ctrlPropRelationship = controlNode.controlRelationshipId ? sheetRelationships.get(controlNode.controlRelationshipId) ?? null : null;
5882
- const ctrlPropPath = ctrlPropRelationship?.type === CTRL_PROP_REL_TYPE ? ctrlPropRelationship.target : null;
5883
- const ctrlProp = ctrlPropPath ? parseCtrlPropDocument(archive, ctrlPropPath) : null;
5884
5981
  const vmlControl = controlNode.shapeId !== null ? vmlControlsByShapeId.get(controlNode.shapeId) ?? null : null;
5885
- const kind = parseFormControlKind(ctrlProp?.objectType ?? vmlControl?.objectType);
5982
+ const kind = parseFormControlKind(vmlControl?.objectType);
5886
5983
  parsedControls.push({
5887
5984
  anchor: controlNode.anchor,
5888
- checked: ctrlProp?.checked ?? vmlControl?.checked,
5985
+ checked: vmlControl?.checked,
5889
5986
  fontFamily: vmlControl?.fontFamily,
5890
5987
  fontSizePt: vmlControl?.fontSizePt,
5891
5988
  hidden: vmlControl?.hidden ?? false,
5892
5989
  id: `form-control-${workbookSheetIndex}-${index}`,
5893
5990
  kind,
5894
5991
  label: vmlControl?.label,
5895
- linkedCell: ctrlProp?.linkedCell ?? vmlControl?.linkedCell,
5992
+ linkedCell: vmlControl?.linkedCell,
5896
5993
  name: controlNode.name,
5897
5994
  sheetIndex: workbookSheetIndex,
5898
5995
  textAlign: vmlControl?.textAlign,
@@ -5978,6 +6075,92 @@ function enrichFormControlsWithHiddenShapes(formControls, shapes, sheetState) {
5978
6075
  };
5979
6076
  });
5980
6077
  }
6078
+ function getFormControlAnchorStart(anchor) {
6079
+ return anchor.kind === "absolute" ? null : { col: anchor.from.col, row: anchor.from.row };
6080
+ }
6081
+ function scoreParsedFormControlMatch(dukeControl, parsedControl, parsedIndex) {
6082
+ if (dukeControl.kind !== parsedControl.kind && parsedControl.kind !== "unknown") {
6083
+ return Number.NEGATIVE_INFINITY;
6084
+ }
6085
+ let score = 10;
6086
+ const dukeName = normalizeControlLabel(dukeControl.name)?.toLowerCase();
6087
+ const parsedName = normalizeControlLabel(parsedControl.name)?.toLowerCase();
6088
+ if (dukeName && parsedName) {
6089
+ if (dukeName !== parsedName) {
6090
+ return Number.NEGATIVE_INFINITY;
6091
+ }
6092
+ score += 100;
6093
+ }
6094
+ const dukeStart = getFormControlAnchorStart(dukeControl.anchor);
6095
+ const parsedStart = getFormControlAnchorStart(parsedControl.anchor);
6096
+ if (dukeStart && parsedStart && dukeStart.col === parsedStart.col && dukeStart.row === parsedStart.row) {
6097
+ score += 40;
6098
+ }
6099
+ if (dukeControl.controlIndex === parsedIndex) {
6100
+ score += 5;
6101
+ }
6102
+ return score;
6103
+ }
6104
+ function areDukeFormControlAnchorsEqual(left, right) {
6105
+ if (!left || !right) {
6106
+ return true;
6107
+ }
6108
+ 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;
6109
+ }
6110
+ function mergeDukeAndParsedFormControls(dukeControls, parsedControls, keepUnmatchedParsedControl = () => true) {
6111
+ const usedParsedIndexes = /* @__PURE__ */ new Set();
6112
+ const mergedControls = dukeControls.map((dukeControl) => {
6113
+ let bestIndex = -1;
6114
+ let bestScore = Number.NEGATIVE_INFINITY;
6115
+ parsedControls.forEach((parsedControl2, parsedIndex) => {
6116
+ if (usedParsedIndexes.has(parsedIndex)) {
6117
+ return;
6118
+ }
6119
+ const score = scoreParsedFormControlMatch(dukeControl, parsedControl2, parsedIndex);
6120
+ if (score > bestScore) {
6121
+ bestIndex = parsedIndex;
6122
+ bestScore = score;
6123
+ }
6124
+ });
6125
+ const parsedControl = bestIndex >= 0 && bestScore >= 10 ? parsedControls[bestIndex] : null;
6126
+ if (!parsedControl) {
6127
+ return dukeControl;
6128
+ }
6129
+ usedParsedIndexes.add(bestIndex);
6130
+ const dukeAnchorChanged = !areDukeFormControlAnchorsEqual(
6131
+ parsedControl.dukeAnchor,
6132
+ dukeControl.dukeAnchor
6133
+ );
6134
+ return {
6135
+ ...parsedControl,
6136
+ ...dukeControl,
6137
+ anchor: dukeAnchorChanged ? dukeControl.anchor : parsedControl.anchor,
6138
+ fontFamily: parsedControl.fontFamily,
6139
+ fontSizePt: parsedControl.fontSizePt,
6140
+ hidden: parsedControl.hidden,
6141
+ id: parsedControl.id,
6142
+ label: dukeControl.label ?? parsedControl.label,
6143
+ name: dukeControl.name ?? parsedControl.name,
6144
+ textAlign: parsedControl.textAlign,
6145
+ textColor: parsedControl.textColor,
6146
+ zIndex: parsedControl.zIndex
6147
+ };
6148
+ });
6149
+ parsedControls.forEach((parsedControl, parsedIndex) => {
6150
+ if (!usedParsedIndexes.has(parsedIndex) && keepUnmatchedParsedControl(parsedControl)) {
6151
+ mergedControls.push(parsedControl);
6152
+ }
6153
+ });
6154
+ return mergedControls.sort((left, right) => left.zIndex - right.zIndex);
6155
+ }
6156
+ function refreshWorkbookFormControls(workbook, currentControlsByWorkbookSheetIndex) {
6157
+ const dukeControlsByWorkbookSheetIndex = collectWorkbookFormControls(workbook);
6158
+ return dukeControlsByWorkbookSheetIndex.map((dukeControls, workbookSheetIndex) => mergeDukeAndParsedFormControls(
6159
+ dukeControls,
6160
+ currentControlsByWorkbookSheetIndex[workbookSheetIndex] ?? [],
6161
+ (control) => control.controlIndex === void 0
6162
+ ));
6163
+ }
5981
6164
  function revokeWorkbookImageAssets(assets) {
5982
6165
  if (!assets) {
5983
6166
  return;
@@ -6010,7 +6193,7 @@ function parseWorkbookStructureAssetsFromArchive(archive, options) {
6010
6193
  workbookSheets
6011
6194
  };
6012
6195
  }
6013
- function parseWorkbookImageAssets(bytes) {
6196
+ function parseWorkbookImageAssets(bytes, workbook) {
6014
6197
  const archive = unzipSync(bytes);
6015
6198
  const {
6016
6199
  contentTypes,
@@ -6029,6 +6212,7 @@ function parseWorkbookImageAssets(bytes) {
6029
6212
  const shapesByWorkbookSheetIndex = [];
6030
6213
  const sheetOrigins = [];
6031
6214
  const imageOriginsById = /* @__PURE__ */ new Map();
6215
+ const dukeFormControlsByWorkbookSheetIndex = collectWorkbookFormControls(workbook);
6032
6216
  workbookSheets.forEach((sheet, workbookSheetIndex) => {
6033
6217
  const sheetRelationships = parseRelationships(archive, relsPathForDocument(sheet.path), sheet.path);
6034
6218
  const attachments = [];
@@ -6074,7 +6258,10 @@ function parseWorkbookImageAssets(bytes) {
6074
6258
  shapeList,
6075
6259
  sheetStatesByWorkbookSheetIndex[workbookSheetIndex] ?? null
6076
6260
  );
6077
- formControlsByWorkbookSheetIndex[workbookSheetIndex] = enrichedFormControlList;
6261
+ formControlsByWorkbookSheetIndex[workbookSheetIndex] = mergeDukeAndParsedFormControls(
6262
+ dukeFormControlsByWorkbookSheetIndex[workbookSheetIndex] ?? [],
6263
+ enrichedFormControlList
6264
+ );
6078
6265
  imagesByWorkbookSheetIndex[workbookSheetIndex] = imageList;
6079
6266
  shapesByWorkbookSheetIndex[workbookSheetIndex] = visibleShapeList;
6080
6267
  sheetOrigins[workbookSheetIndex] = attachments.length > 0 ? {
@@ -7148,6 +7335,144 @@ function parseA1RangeReference2(reference) {
7148
7335
  }
7149
7336
  return normalizeRange({ start, end });
7150
7337
  }
7338
+ function resolveWorkbookReference(workbook, defaultWorkbookSheetIndex, rawReference, resolvingNamedRange = false) {
7339
+ const reference = rawReference.trim().replace(/^=/, "");
7340
+ if (!reference) {
7341
+ return null;
7342
+ }
7343
+ let workbookSheetIndex = defaultWorkbookSheetIndex;
7344
+ let rangeReference = reference;
7345
+ const bangIndex = reference.lastIndexOf("!");
7346
+ if (bangIndex >= 0) {
7347
+ let sheetName = reference.slice(0, bangIndex).trim();
7348
+ rangeReference = reference.slice(bangIndex + 1).trim();
7349
+ if (sheetName.startsWith("'") && sheetName.endsWith("'")) {
7350
+ sheetName = sheetName.slice(1, -1).replace(/''/g, "'");
7351
+ }
7352
+ const resolvedSheetIndex = workbook.sheetIndex(sheetName);
7353
+ if (resolvedSheetIndex === void 0) {
7354
+ return null;
7355
+ }
7356
+ workbookSheetIndex = resolvedSheetIndex;
7357
+ } else if (!resolvingNamedRange) {
7358
+ const namedRange = workbook.getNamedRange(reference);
7359
+ if (namedRange) {
7360
+ return resolveWorkbookReference(workbook, defaultWorkbookSheetIndex, namedRange, true);
7361
+ }
7362
+ }
7363
+ const range = parseA1RangeReference2(rangeReference.replace(/\$/g, ""));
7364
+ if (!range || workbookSheetIndex < 0 || workbookSheetIndex >= workbook.sheetCount) {
7365
+ return null;
7366
+ }
7367
+ try {
7368
+ return {
7369
+ range,
7370
+ workbookSheetIndex,
7371
+ worksheet: workbook.getSheet(workbookSheetIndex)
7372
+ };
7373
+ } catch {
7374
+ return null;
7375
+ }
7376
+ }
7377
+ function formControlInputFromDukeControl(control) {
7378
+ return {
7379
+ anchor: { ...control.anchor },
7380
+ kind: { ...control.kind },
7381
+ locked: control.locked,
7382
+ name: control.name,
7383
+ printable: control.printable
7384
+ };
7385
+ }
7386
+ function getFormControlCellLink(kind) {
7387
+ return "cellLink" in kind ? kind.cellLink : void 0;
7388
+ }
7389
+ function anchorContainsControl(group, control) {
7390
+ const groupAnchor = group.anchor;
7391
+ const controlAnchor = control.anchor;
7392
+ return controlAnchor.fromCol >= groupAnchor.fromCol && controlAnchor.fromCol <= groupAnchor.toCol && controlAnchor.fromRow >= groupAnchor.fromRow && controlAnchor.fromRow <= groupAnchor.toRow;
7393
+ }
7394
+ function resolveOptionButtonGroupIndexes(controls, targetIndex) {
7395
+ const target = controls[targetIndex];
7396
+ if (!target || target.kind.kind !== "optionButton") {
7397
+ return [];
7398
+ }
7399
+ if (target.kind.cellLink) {
7400
+ const targetCellLink = target.kind.cellLink;
7401
+ return controls.flatMap((control, index) => control.kind.kind === "optionButton" && control.kind.cellLink === targetCellLink ? [index] : []);
7402
+ }
7403
+ const containingGroups = controls.map((control, index) => ({ control, index })).filter(({ control }) => control.kind.kind === "groupBox" && anchorContainsControl(control, target)).sort((left, right) => {
7404
+ const leftArea = (left.control.anchor.toCol - left.control.anchor.fromCol) * (left.control.anchor.toRow - left.control.anchor.fromRow);
7405
+ const rightArea = (right.control.anchor.toCol - right.control.anchor.fromCol) * (right.control.anchor.toRow - right.control.anchor.fromRow);
7406
+ return leftArea - rightArea;
7407
+ });
7408
+ const containingGroup = containingGroups[0]?.control;
7409
+ if (containingGroup) {
7410
+ return controls.flatMap((control, index) => control.kind.kind === "optionButton" && anchorContainsControl(containingGroup, control) ? [index] : []);
7411
+ }
7412
+ const optionIndexes = controls.flatMap((control, index) => control.kind.kind === "optionButton" ? [index] : []);
7413
+ const targetPosition = optionIndexes.indexOf(targetIndex);
7414
+ if (targetPosition < 0) {
7415
+ return [];
7416
+ }
7417
+ let startPosition = targetPosition;
7418
+ while (startPosition > 0) {
7419
+ const control = controls[optionIndexes[startPosition] ?? -1];
7420
+ if (control?.kind.kind === "optionButton" && control.kind.firstInGroup) {
7421
+ break;
7422
+ }
7423
+ startPosition -= 1;
7424
+ }
7425
+ let endPosition = targetPosition + 1;
7426
+ while (endPosition < optionIndexes.length) {
7427
+ const control = controls[optionIndexes[endPosition] ?? -1];
7428
+ if (control?.kind.kind === "optionButton" && control.kind.firstInGroup) {
7429
+ break;
7430
+ }
7431
+ endPosition += 1;
7432
+ }
7433
+ return optionIndexes.slice(startPosition, endPosition);
7434
+ }
7435
+ function resolveLinkedCellValue(controls, controlIndex) {
7436
+ const control = controls[controlIndex];
7437
+ if (!control) {
7438
+ return void 0;
7439
+ }
7440
+ switch (control.kind.kind) {
7441
+ case "checkbox":
7442
+ return control.kind.state === "mixed" ? "#N/A" : control.kind.state === "checked";
7443
+ case "optionButton": {
7444
+ const groupIndexes = resolveOptionButtonGroupIndexes(controls, controlIndex);
7445
+ const checkedIndex = groupIndexes.find((index) => controls[index]?.kind.kind === "optionButton" && controls[index]?.kind.state === "checked");
7446
+ const groupPosition = checkedIndex === void 0 ? -1 : groupIndexes.indexOf(checkedIndex);
7447
+ return groupPosition >= 0 ? groupPosition + 1 : "";
7448
+ }
7449
+ case "dropdown":
7450
+ return control.kind.selected === void 0 ? "" : control.kind.selected + 1;
7451
+ case "listBox":
7452
+ return control.kind.selection === "single" && control.kind.selected.length === 1 ? (control.kind.selected[0] ?? -1) + 1 : "";
7453
+ case "scrollbar":
7454
+ case "spinner":
7455
+ return control.kind.value;
7456
+ default:
7457
+ return void 0;
7458
+ }
7459
+ }
7460
+ function syncFormControlLinkedCell(workbook, workbookSheetIndex, controls, controlIndex) {
7461
+ const control = controls[controlIndex];
7462
+ if (!control) {
7463
+ return;
7464
+ }
7465
+ const cellLink = getFormControlCellLink(control.kind);
7466
+ const value = resolveLinkedCellValue(controls, controlIndex);
7467
+ if (!cellLink || value === void 0) {
7468
+ return;
7469
+ }
7470
+ const target = resolveWorkbookReference(workbook, workbookSheetIndex, cellLink);
7471
+ if (!target || target.range.start.row !== target.range.end.row || target.range.start.col !== target.range.end.col) {
7472
+ return;
7473
+ }
7474
+ target.worksheet.setCell(cellAddressToA1(target.range.start), value);
7475
+ }
7151
7476
  function parseWorksheetFreezePanes(worksheet) {
7152
7477
  const rawFreezePanes = worksheet.freezePanes;
7153
7478
  const row = typeof rawFreezePanes?.row === "number" && rawFreezePanes.row >= 0 ? rawFreezePanes.row : null;
@@ -7914,7 +8239,7 @@ function createBasicWorkbookAssets(workbook) {
7914
8239
  const objectUrls = [];
7915
8240
  return {
7916
8241
  archive: {},
7917
- formControlsByWorkbookSheetIndex: Array.from({ length: workbook.sheetCount }, () => []),
8242
+ formControlsByWorkbookSheetIndex: collectWorkbookFormControls(workbook),
7918
8243
  imageOriginsById: /* @__PURE__ */ new Map(),
7919
8244
  imagesByWorkbookSheetIndex: collectWorksheetApiImages(workbook, objectUrls),
7920
8245
  namedCellStyleByName: {},
@@ -7932,7 +8257,7 @@ function loadWorkbookImageAssets(bytes, workbook, skipXmlParsing = false) {
7932
8257
  if (shouldSkipXmlParsingForWorkbook(bytes, skipXmlParsing) || !isZipWorkbook(bytes)) {
7933
8258
  return createBasicWorkbookAssets(workbook);
7934
8259
  }
7935
- const parsedAssets = parseWorkbookImageAssets(bytes);
8260
+ const parsedAssets = parseWorkbookImageAssets(bytes, workbook);
7936
8261
  const apiImagesByWorkbookSheetIndex = collectWorksheetApiImages(workbook, parsedAssets.objectUrls);
7937
8262
  const imagesByWorkbookSheetIndex = Array.from(
7938
8263
  { length: Math.max(workbook.sheetCount, parsedAssets.imagesByWorkbookSheetIndex.length, apiImagesByWorkbookSheetIndex.length) },
@@ -8249,6 +8574,12 @@ function useXlsxViewerController(options) {
8249
8574
  };
8250
8575
  }, [skipXmlParsing]);
8251
8576
  const refreshWorkbookState = React.useCallback((targetWorkbook) => {
8577
+ const currentFormControls = imageAssetsRef.current?.formControlsByWorkbookSheetIndex ?? [];
8578
+ const nextFormControls = refreshWorkbookFormControls(targetWorkbook, currentFormControls);
8579
+ if (imageAssetsRef.current) {
8580
+ imageAssetsRef.current.formControlsByWorkbookSheetIndex = nextFormControls;
8581
+ }
8582
+ setFormControlsByWorkbookSheetIndex(nextFormControls);
8252
8583
  const nextSheets = buildSheetList(
8253
8584
  targetWorkbook,
8254
8585
  imageAssetsRef.current?.sheetStatesByWorkbookSheetIndex,
@@ -8376,6 +8707,7 @@ function useXlsxViewerController(options) {
8376
8707
  setChartsheets(snapshot.chartsheets);
8377
8708
  setTabs(snapshot.tabs);
8378
8709
  chartAssetsRef.current = null;
8710
+ setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8379
8711
  setWorkerTablesByWorkbookSheetIndex(snapshot.tablesByWorkbookSheetIndex);
8380
8712
  setShouldAutoCalculate(false);
8381
8713
  setIsWorkerBacked(true);
@@ -8595,6 +8927,7 @@ function useXlsxViewerController(options) {
8595
8927
  setChartsheets(snapshot.chartsheets);
8596
8928
  setTabs(snapshot.tabs);
8597
8929
  chartAssetsRef.current = null;
8930
+ setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8598
8931
  setWorkerTablesByWorkbookSheetIndex(snapshot.tablesByWorkbookSheetIndex);
8599
8932
  setShouldAutoCalculate(false);
8600
8933
  setIsWorkerBacked(true);
@@ -8716,6 +9049,20 @@ function useXlsxViewerController(options) {
8716
9049
  }
8717
9050
  return workbook.getSheet(activeSheet.workbookSheetIndex);
8718
9051
  }, [activeSheet, workbook]);
9052
+ const getFormControlWorksheet = React.useCallback((sheetIndex = activeSheetIndex) => {
9053
+ const targetSheet = sheets[sheetIndex];
9054
+ if (!workbook || !targetSheet) {
9055
+ return null;
9056
+ }
9057
+ try {
9058
+ return {
9059
+ workbookSheetIndex: targetSheet.workbookSheetIndex,
9060
+ worksheet: workbook.getSheet(targetSheet.workbookSheetIndex)
9061
+ };
9062
+ } catch {
9063
+ return null;
9064
+ }
9065
+ }, [activeSheetIndex, sheets, workbook]);
8719
9066
  const tables = React.useMemo(
8720
9067
  () => isWorkerBacked ? workerTablesByWorkbookSheetIndex[activeSheet?.workbookSheetIndex ?? -1] ?? [] : mapWorksheetTables(getActiveWorksheet()),
8721
9068
  [activeSheet?.workbookSheetIndex, getActiveWorksheet, isWorkerBacked, revision, workerTablesByWorkbookSheetIndex]
@@ -8855,6 +9202,28 @@ function useXlsxViewerController(options) {
8855
9202
  return (formControlsByWorkbookSheetIndex[targetSheet.workbookSheetIndex] ?? []).map(mapPublicFormControl);
8856
9203
  }, [activeSheetIndex, formControlsByWorkbookSheetIndex, mapPublicFormControl, sheets]);
8857
9204
  const formControls = React.useMemo(() => getSheetFormControls(activeSheetIndex), [activeSheetIndex, getSheetFormControls]);
9205
+ const getFormControlItems = React.useCallback((controlIndex, sheetIndex = activeSheetIndex) => {
9206
+ const target = getFormControlWorksheet(sheetIndex);
9207
+ if (!workbook || !target) {
9208
+ return [];
9209
+ }
9210
+ const control = target.worksheet.formControls[controlIndex];
9211
+ const inputRange = control?.kind.kind === "listBox" || control?.kind.kind === "dropdown" ? control.kind.inputRange : void 0;
9212
+ if (!inputRange) {
9213
+ return [];
9214
+ }
9215
+ const source = resolveWorkbookReference(workbook, target.workbookSheetIndex, inputRange);
9216
+ if (!source) {
9217
+ return [];
9218
+ }
9219
+ const items = [];
9220
+ for (let row = source.range.start.row; row <= source.range.end.row; row += 1) {
9221
+ for (let col = source.range.start.col; col <= source.range.end.col; col += 1) {
9222
+ items.push(source.worksheet.getFormattedValueAt(row, col));
9223
+ }
9224
+ }
9225
+ return items;
9226
+ }, [activeSheetIndex, getFormControlWorksheet, workbook]);
8858
9227
  const getSheetShapes = React.useCallback((sheetIndex = activeSheetIndex) => {
8859
9228
  const targetSheet = sheets[sheetIndex];
8860
9229
  if (!targetSheet) {
@@ -9252,6 +9621,89 @@ function useXlsxViewerController(options) {
9252
9621
  redoStackRef.current = [];
9253
9622
  setHistoryRevision((current) => current + 1);
9254
9623
  }, [createHistoryEntry]);
9624
+ const addFormControl = React.useCallback((input, sheetIndex = activeSheetIndex) => {
9625
+ const target = getFormControlWorksheet(sheetIndex);
9626
+ if (readOnly || !workbook || !target) {
9627
+ return null;
9628
+ }
9629
+ recordHistoryBeforeMutation();
9630
+ const controlIndex = target.worksheet.addFormControl(input);
9631
+ const controls = target.worksheet.formControls;
9632
+ const addedControl = controls[controlIndex];
9633
+ if (addedControl?.kind.kind === "optionButton" && addedControl.kind.state === "checked") {
9634
+ const groupIndexes = resolveOptionButtonGroupIndexes(controls, controlIndex);
9635
+ for (const siblingIndex of groupIndexes) {
9636
+ if (siblingIndex === controlIndex) {
9637
+ continue;
9638
+ }
9639
+ const sibling = target.worksheet.formControls[siblingIndex];
9640
+ if (sibling?.kind.kind === "optionButton" && sibling.kind.state !== "unchecked") {
9641
+ target.worksheet.setFormControl(siblingIndex, {
9642
+ ...formControlInputFromDukeControl(sibling),
9643
+ kind: { ...sibling.kind, state: "unchecked" }
9644
+ });
9645
+ }
9646
+ }
9647
+ }
9648
+ syncFormControlLinkedCell(
9649
+ workbook,
9650
+ target.workbookSheetIndex,
9651
+ target.worksheet.formControls,
9652
+ controlIndex
9653
+ );
9654
+ maybeRecalculateWorkbook(workbook);
9655
+ refreshWorkbookState(workbook);
9656
+ return controlIndex;
9657
+ }, [activeSheetIndex, getFormControlWorksheet, maybeRecalculateWorkbook, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9658
+ const updateFormControl = React.useCallback((controlIndex, patch, sheetIndex = activeSheetIndex) => {
9659
+ const target = getFormControlWorksheet(sheetIndex);
9660
+ const currentControl = target?.worksheet.formControls[controlIndex];
9661
+ if (readOnly || !workbook || !target || !currentControl) {
9662
+ return false;
9663
+ }
9664
+ recordHistoryBeforeMutation();
9665
+ const nextInput = {
9666
+ ...formControlInputFromDukeControl(currentControl),
9667
+ ...patch,
9668
+ anchor: patch.anchor ? { ...patch.anchor } : { ...currentControl.anchor },
9669
+ kind: patch.kind ? { ...patch.kind } : { ...currentControl.kind }
9670
+ };
9671
+ if (nextInput.kind.kind === "optionButton" && nextInput.kind.state === "checked") {
9672
+ const groupIndexes = resolveOptionButtonGroupIndexes(target.worksheet.formControls, controlIndex);
9673
+ for (const siblingIndex of groupIndexes) {
9674
+ if (siblingIndex === controlIndex) {
9675
+ continue;
9676
+ }
9677
+ const sibling = target.worksheet.formControls[siblingIndex];
9678
+ if (sibling?.kind.kind === "optionButton" && sibling.kind.state !== "unchecked") {
9679
+ target.worksheet.setFormControl(siblingIndex, {
9680
+ ...formControlInputFromDukeControl(sibling),
9681
+ kind: { ...sibling.kind, state: "unchecked" }
9682
+ });
9683
+ }
9684
+ }
9685
+ }
9686
+ target.worksheet.setFormControl(controlIndex, nextInput);
9687
+ syncFormControlLinkedCell(
9688
+ workbook,
9689
+ target.workbookSheetIndex,
9690
+ target.worksheet.formControls,
9691
+ controlIndex
9692
+ );
9693
+ maybeRecalculateWorkbook(workbook);
9694
+ refreshWorkbookState(workbook);
9695
+ return true;
9696
+ }, [activeSheetIndex, getFormControlWorksheet, maybeRecalculateWorkbook, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9697
+ const removeFormControl = React.useCallback((controlIndex, sheetIndex = activeSheetIndex) => {
9698
+ const target = getFormControlWorksheet(sheetIndex);
9699
+ if (readOnly || !workbook || !target || !target.worksheet.formControls[controlIndex]) {
9700
+ return false;
9701
+ }
9702
+ recordHistoryBeforeMutation();
9703
+ target.worksheet.removeFormControl(controlIndex);
9704
+ refreshWorkbookState(workbook);
9705
+ return true;
9706
+ }, [activeSheetIndex, getFormControlWorksheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9255
9707
  const recordCellEditHistory = React.useCallback((cell, before, after) => {
9256
9708
  if (!activeSheet || isApplyingHistoryRef.current) {
9257
9709
  return;
@@ -10060,8 +10512,15 @@ function useXlsxViewerController(options) {
10060
10512
  }
10061
10513
  workbook.addSheet(candidate);
10062
10514
  sheetOriginsRef.current = [...sheetOriginsRef.current, null];
10515
+ setFormControlsByWorkbookSheetIndex((current) => [...current, []]);
10063
10516
  setImagesByWorkbookSheetIndex((current) => [...current, []]);
10064
10517
  setShapesByWorkbookSheetIndex((current) => [...current, []]);
10518
+ if (imageAssetsRef.current) {
10519
+ imageAssetsRef.current.formControlsByWorkbookSheetIndex = [
10520
+ ...imageAssetsRef.current.formControlsByWorkbookSheetIndex,
10521
+ []
10522
+ ];
10523
+ }
10065
10524
  const nextSheets = buildSheetList(
10066
10525
  workbook,
10067
10526
  imageAssetsRef.current?.sheetStatesByWorkbookSheetIndex,
@@ -10091,9 +10550,13 @@ function useXlsxViewerController(options) {
10091
10550
  recordHistoryBeforeMutation();
10092
10551
  workbook.removeSheet(activeSheet.workbookSheetIndex);
10093
10552
  sheetOriginsRef.current = sheetOriginsRef.current.filter((_, index) => index !== activeSheet.workbookSheetIndex);
10553
+ setFormControlsByWorkbookSheetIndex((current) => current.filter((_, index) => index !== activeSheet.workbookSheetIndex));
10094
10554
  setImagesByWorkbookSheetIndex((current) => current.filter((_, index) => index !== activeSheet.workbookSheetIndex));
10095
10555
  setShapesByWorkbookSheetIndex((current) => current.filter((_, index) => index !== activeSheet.workbookSheetIndex));
10096
10556
  if (imageAssetsRef.current) {
10557
+ imageAssetsRef.current.formControlsByWorkbookSheetIndex = imageAssetsRef.current.formControlsByWorkbookSheetIndex.filter(
10558
+ (_, index) => index !== activeSheet.workbookSheetIndex
10559
+ );
10097
10560
  imageAssetsRef.current.sheetStatesByWorkbookSheetIndex = imageAssetsRef.current.sheetStatesByWorkbookSheetIndex.filter(
10098
10561
  (_, index) => index !== activeSheet.workbookSheetIndex
10099
10562
  );
@@ -10394,6 +10857,7 @@ function useXlsxViewerController(options) {
10394
10857
  activeSheetIndex,
10395
10858
  activeTab,
10396
10859
  activeTabIndex,
10860
+ addFormControl,
10397
10861
  addSheet,
10398
10862
  canRedo,
10399
10863
  canDownload: Boolean(file ?? src),
@@ -10433,6 +10897,7 @@ function useXlsxViewerController(options) {
10433
10897
  getClipboardData,
10434
10898
  getCellDisplayValue: getCellDisplayValue2,
10435
10899
  getCellFormula,
10900
+ getFormControlItems,
10436
10901
  getCellSnapshotAsync: isWorkerBacked ? getCellSnapshotAsync : void 0,
10437
10902
  getActiveWorksheet,
10438
10903
  getRowsBatchAsync: isWorkerBacked ? getRowsBatchAsync : void 0,
@@ -10450,6 +10915,7 @@ function useXlsxViewerController(options) {
10450
10915
  pasteStructuredClipboardData,
10451
10916
  pasteText,
10452
10917
  removeActiveSheet,
10918
+ removeFormControl,
10453
10919
  readOnly,
10454
10920
  recalculate,
10455
10921
  redo,
@@ -10500,6 +10966,7 @@ function useXlsxViewerController(options) {
10500
10966
  undo,
10501
10967
  unmergeSelection,
10502
10968
  updateChart,
10969
+ updateFormControl,
10503
10970
  workbook,
10504
10971
  zoomIn,
10505
10972
  zoomOut,
@@ -10512,6 +10979,7 @@ function useXlsxViewerController(options) {
10512
10979
  activeSheetIndex,
10513
10980
  activeTab,
10514
10981
  activeTabIndex,
10982
+ addFormControl,
10515
10983
  addSheet,
10516
10984
  canLoadDeferred,
10517
10985
  canRedo,
@@ -10548,6 +11016,7 @@ function useXlsxViewerController(options) {
10548
11016
  getClipboardData,
10549
11017
  getCellDisplayValue2,
10550
11018
  getCellFormula,
11019
+ getFormControlItems,
10551
11020
  getCellSnapshotAsync,
10552
11021
  getActiveWorksheet,
10553
11022
  historyRevision,
@@ -10565,6 +11034,7 @@ function useXlsxViewerController(options) {
10565
11034
  pasteStructuredClipboardData,
10566
11035
  pasteText,
10567
11036
  removeActiveSheet,
11037
+ removeFormControl,
10568
11038
  readOnly,
10569
11039
  recalculate,
10570
11040
  redo,
@@ -10615,6 +11085,7 @@ function useXlsxViewerController(options) {
10615
11085
  undo,
10616
11086
  unmergeSelection,
10617
11087
  updateChart,
11088
+ updateFormControl,
10618
11089
  workbook,
10619
11090
  zoomIn,
10620
11091
  zoomOut,
@@ -21603,13 +22074,90 @@ function renderConditionalIcon(icon, scale = 1) {
21603
22074
  }
21604
22075
  );
21605
22076
  }
21606
- function renderCheckboxControl(checked, palette, scale = 1) {
22077
+ function isFormControlChecked(control) {
22078
+ return control.state ? control.state === "checked" : Boolean(control.checked);
22079
+ }
22080
+ function isFormControlMixed(control) {
22081
+ return control.state === "mixed";
22082
+ }
22083
+ function buildFormControlKindInput(control) {
22084
+ switch (control.kind) {
22085
+ case "button":
22086
+ return { caption: control.caption ?? control.label ?? "", kind: "button" };
22087
+ case "checkbox":
22088
+ return {
22089
+ caption: control.caption ?? control.label ?? "",
22090
+ cellLink: control.linkedCell,
22091
+ kind: "checkbox",
22092
+ no3D: control.no3D ?? false,
22093
+ state: control.state ?? (control.checked ? "checked" : "unchecked")
22094
+ };
22095
+ case "radio":
22096
+ return {
22097
+ caption: control.caption ?? control.label ?? "",
22098
+ cellLink: control.linkedCell,
22099
+ firstInGroup: control.firstInGroup,
22100
+ kind: "optionButton",
22101
+ no3D: control.no3D ?? false,
22102
+ state: control.state === "checked" ? "checked" : "unchecked"
22103
+ };
22104
+ case "label":
22105
+ return { caption: control.caption ?? control.label ?? "", kind: "label" };
22106
+ case "group-box":
22107
+ return {
22108
+ caption: control.caption ?? control.label ?? "",
22109
+ kind: "groupBox",
22110
+ no3D: control.no3D ?? false
22111
+ };
22112
+ case "listbox":
22113
+ return {
22114
+ cellLink: control.linkedCell,
22115
+ inputRange: control.inputRange,
22116
+ kind: "listBox",
22117
+ no3D: control.no3D ?? false,
22118
+ selected: Array.isArray(control.selected) ? control.selected : control.selected === void 0 ? [] : [control.selected],
22119
+ selection: control.selection ?? "single"
22120
+ };
22121
+ case "dropdown":
22122
+ return {
22123
+ cellLink: control.linkedCell,
22124
+ inputRange: control.inputRange,
22125
+ kind: "dropdown",
22126
+ lines: control.lines ?? 8,
22127
+ no3D: control.no3D ?? false,
22128
+ selected: typeof control.selected === "number" ? control.selected : void 0
22129
+ };
22130
+ case "scrollbar":
22131
+ return {
22132
+ cellLink: control.linkedCell,
22133
+ horizontal: control.horizontal ?? true,
22134
+ increment: control.increment ?? 1,
22135
+ kind: "scrollbar",
22136
+ max: control.max ?? 100,
22137
+ min: control.min ?? 0,
22138
+ page: control.page ?? 10,
22139
+ value: control.value ?? control.min ?? 0
22140
+ };
22141
+ case "spinner":
22142
+ return {
22143
+ cellLink: control.linkedCell,
22144
+ increment: control.increment ?? 1,
22145
+ kind: "spinner",
22146
+ max: control.max ?? 100,
22147
+ min: control.min ?? 0,
22148
+ value: control.value ?? control.min ?? 0
22149
+ };
22150
+ default:
22151
+ return null;
22152
+ }
22153
+ }
22154
+ function renderCheckboxControl(checked, palette, scale = 1, mixed = false) {
21607
22155
  const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
21608
- const fill = checked ? paletteIsDark(palette) ? "#60a5fa" : "#2563eb" : "transparent";
22156
+ const fill = checked || mixed ? paletteIsDark(palette) ? "#60a5fa" : "#2563eb" : "transparent";
21609
22157
  const check = paletteIsDark(palette) ? "#020617" : "#ffffff";
21610
22158
  return /* @__PURE__ */ jsxs3("svg", { "aria-hidden": "true", height: 14 * scale, style: { display: "block" }, viewBox: "0 0 16 16", width: 14 * scale, children: [
21611
22159
  /* @__PURE__ */ jsx3("rect", { fill, height: 11, rx: 2, ry: 2, stroke, strokeWidth: 1.2, width: 11, x: 2.5, y: 2.5 }),
21612
- checked ? /* @__PURE__ */ jsx3(
22160
+ mixed ? /* @__PURE__ */ jsx3("path", { d: "M5 8h6", fill: "none", stroke: check, strokeLinecap: "round", strokeWidth: 1.8 }) : checked ? /* @__PURE__ */ jsx3(
21613
22161
  "path",
21614
22162
  {
21615
22163
  d: "M5 8.1 7.1 10.2 11.3 5.8",
@@ -21779,6 +22327,8 @@ function drawStaticFormControl(context, control, rect, palette, zoomFactor, shee
21779
22327
  const textColor = control.textColor ?? "#000000";
21780
22328
  const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
21781
22329
  const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
22330
+ const controlChecked = isFormControlChecked(control);
22331
+ const controlMixed = isFormControlMixed(control);
21782
22332
  context.save();
21783
22333
  context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
21784
22334
  context.textBaseline = "middle";
@@ -21819,14 +22369,19 @@ function drawStaticFormControl(context, control, rect, palette, zoomFactor, shee
21819
22369
  const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
21820
22370
  if (control.kind === "checkbox") {
21821
22371
  context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
21822
- if (control.checked) {
22372
+ if (controlChecked || controlMixed) {
21823
22373
  context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
21824
22374
  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));
21825
22375
  context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
21826
22376
  context.beginPath();
21827
- context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
21828
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
21829
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
22377
+ if (controlMixed) {
22378
+ context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.25, rect.top + rect.height / 2);
22379
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.75, rect.top + rect.height / 2);
22380
+ } else {
22381
+ context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
22382
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
22383
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
22384
+ }
21830
22385
  context.stroke();
21831
22386
  }
21832
22387
  textLeft += iconSize + 4 * zoomFactor;
@@ -21834,7 +22389,7 @@ function drawStaticFormControl(context, control, rect, palette, zoomFactor, shee
21834
22389
  context.beginPath();
21835
22390
  context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
21836
22391
  context.stroke();
21837
- if (control.checked) {
22392
+ if (controlChecked) {
21838
22393
  context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
21839
22394
  context.beginPath();
21840
22395
  context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
@@ -22470,6 +23025,8 @@ function XlsxGrid({
22470
23025
  getCellStyle,
22471
23026
  loadingComponent,
22472
23027
  loadingState,
23028
+ onFormControlAction,
23029
+ onFormControlChange,
22473
23030
  renderChartLoading,
22474
23031
  palette,
22475
23032
  renderImage,
@@ -22507,6 +23064,7 @@ function XlsxGrid({
22507
23064
  getRowsBatchAsync,
22508
23065
  getClipboardData,
22509
23066
  getCellDisplayValue: getControllerCellDisplayValue,
23067
+ getFormControlItems,
22510
23068
  images,
22511
23069
  shapes,
22512
23070
  isLoadDeferred,
@@ -22544,9 +23102,13 @@ function XlsxGrid({
22544
23102
  sortTable,
22545
23103
  tables,
22546
23104
  undo,
23105
+ updateFormControl,
22547
23106
  workbook,
22548
23107
  zoomScale
22549
23108
  } = controller;
23109
+ const formControlItemsById = React4.useMemo(() => new Map(
23110
+ formControls.flatMap((control) => control.controlIndex !== void 0 && (control.kind === "dropdown" || control.kind === "listbox") ? [[control.id, getFormControlItems(control.controlIndex, control.sheetIndex)]] : [])
23111
+ ), [formControls, getFormControlItems]);
22550
23112
  const canResizeHeaders = !readOnly || allowResizeInReadOnly;
22551
23113
  const scrollRef = React4.useRef(null);
22552
23114
  const wrapperRef = React4.useRef(null);
@@ -25054,7 +25616,7 @@ function XlsxGrid({
25054
25616
  return "";
25055
25617
  }
25056
25618
  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("|");
25057
- 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("|");
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("|");
25058
25620
  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("|");
25059
25621
  return `${revision}:${canvasImageLoadVersion}:${shapeSignature}:${controlSignature}:${imageSignature}`;
25060
25622
  }, [
@@ -26755,6 +27317,8 @@ function XlsxGrid({
26755
27317
  const textColor = control.textColor ?? "#000000";
26756
27318
  const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
26757
27319
  const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
27320
+ const controlChecked = isFormControlChecked(control);
27321
+ const controlMixed = isFormControlMixed(control);
26758
27322
  context.save();
26759
27323
  context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
26760
27324
  context.textBaseline = "middle";
@@ -26795,14 +27359,19 @@ function XlsxGrid({
26795
27359
  const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
26796
27360
  if (control.kind === "checkbox") {
26797
27361
  context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
26798
- if (control.checked) {
27362
+ if (controlChecked || controlMixed) {
26799
27363
  context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
26800
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));
26801
27365
  context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
26802
27366
  context.beginPath();
26803
- context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
26804
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
26805
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
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
+ }
26806
27375
  context.stroke();
26807
27376
  }
26808
27377
  textLeft += iconSize + 4 * zoomFactor;
@@ -26810,7 +27379,7 @@ function XlsxGrid({
26810
27379
  context.beginPath();
26811
27380
  context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
26812
27381
  context.stroke();
26813
- if (control.checked) {
27382
+ if (controlChecked) {
26814
27383
  context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
26815
27384
  context.beginPath();
26816
27385
  context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
@@ -28125,10 +28694,22 @@ function XlsxGrid({
28125
28694
  const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
28126
28695
  const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
28127
28696
  const textColor = control.textColor ?? "#000000";
28697
+ const canMutateControl = !readOnly && control.controlIndex !== void 0 && [
28698
+ "checkbox",
28699
+ "dropdown",
28700
+ "listbox",
28701
+ "radio",
28702
+ "scrollbar",
28703
+ "spinner"
28704
+ ].includes(control.kind);
28705
+ const canActivateButton = !readOnly && control.kind === "button" && Boolean(onFormControlAction);
28706
+ const isInteractiveControl = canMutateControl || canActivateButton;
28707
+ const controlItems = formControlItemsById.get(control.id) ?? [];
28128
28708
  const commonStyle = {
28129
28709
  alignItems: control.kind === "group-box" ? "stretch" : "center",
28130
28710
  color: textColor,
28131
28711
  contain: "layout paint",
28712
+ cursor: isInteractiveControl ? "pointer" : void 0,
28132
28713
  display: "flex",
28133
28714
  fontFamily: control.fontFamily ?? "Calibri, sans-serif",
28134
28715
  fontSize: fontSizePx,
@@ -28139,12 +28720,28 @@ function XlsxGrid({
28139
28720
  left: rect.left,
28140
28721
  lineHeight: 1.2,
28141
28722
  overflow: "hidden",
28142
- pointerEvents: "none",
28723
+ pointerEvents: isInteractiveControl ? "auto" : "none",
28143
28724
  position: "absolute",
28144
28725
  top: rect.top,
28145
28726
  width: rect.width,
28146
28727
  zIndex: isFrozenDrawing ? control.zIndex + 20 : control.zIndex
28147
28728
  };
28729
+ const stopControlPointerEvent = (event) => {
28730
+ event.stopPropagation();
28731
+ };
28732
+ const commitControlKind = (kind, nextControl) => {
28733
+ if (control.controlIndex === void 0) {
28734
+ return;
28735
+ }
28736
+ const didUpdate = updateFormControl(control.controlIndex, { kind }, control.sheetIndex);
28737
+ if (didUpdate) {
28738
+ onFormControlChange?.({
28739
+ control: { ...control, ...nextControl },
28740
+ previousControl: control,
28741
+ type: "change"
28742
+ });
28743
+ }
28744
+ };
28148
28745
  if (control.kind === "group-box") {
28149
28746
  const hasLabel = controlLabel.length > 0;
28150
28747
  return /* @__PURE__ */ jsx3(
@@ -28186,15 +28783,88 @@ function XlsxGrid({
28186
28783
  `${pane}-${control.id}`
28187
28784
  );
28188
28785
  }
28189
- let content;
28190
28786
  if (control.kind === "radio") {
28191
- content = renderRadioControl(Boolean(control.checked), palette, zoomFactor);
28192
- } else if (control.kind === "checkbox") {
28193
- content = renderCheckboxControl(Boolean(control.checked), palette, zoomFactor);
28194
- } else if (control.kind === "button") {
28787
+ const checked = isFormControlChecked(control);
28788
+ const kind = buildFormControlKindInput(control);
28789
+ return /* @__PURE__ */ jsxs3(
28790
+ "button",
28791
+ {
28792
+ "aria-checked": checked,
28793
+ disabled: readOnly || control.controlIndex === void 0,
28794
+ onClick: (event) => {
28795
+ event.stopPropagation();
28796
+ if (!checked && kind?.kind === "optionButton") {
28797
+ commitControlKind(
28798
+ { ...kind, state: "checked" },
28799
+ { checked: true, state: "checked" }
28800
+ );
28801
+ }
28802
+ },
28803
+ onPointerDown: stopControlPointerEvent,
28804
+ role: "radio",
28805
+ style: {
28806
+ ...commonStyle,
28807
+ background: "transparent",
28808
+ border: 0,
28809
+ boxSizing: "border-box",
28810
+ padding: `0 ${Math.max(1, zoomFactor)}px`
28811
+ },
28812
+ type: "button",
28813
+ children: [
28814
+ renderRadioControl(checked, palette, zoomFactor),
28815
+ controlLabel ? /* @__PURE__ */ jsx3("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: controlLabel }) : null
28816
+ ]
28817
+ },
28818
+ `${pane}-${control.id}`
28819
+ );
28820
+ }
28821
+ if (control.kind === "checkbox") {
28822
+ const checked = isFormControlChecked(control);
28823
+ const mixed = isFormControlMixed(control);
28824
+ const kind = buildFormControlKindInput(control);
28825
+ return /* @__PURE__ */ jsxs3(
28826
+ "button",
28827
+ {
28828
+ "aria-checked": mixed ? "mixed" : checked,
28829
+ disabled: readOnly || control.controlIndex === void 0,
28830
+ onClick: (event) => {
28831
+ 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
+ }
28839
+ },
28840
+ onPointerDown: stopControlPointerEvent,
28841
+ role: "checkbox",
28842
+ style: {
28843
+ ...commonStyle,
28844
+ background: "transparent",
28845
+ border: 0,
28846
+ boxSizing: "border-box",
28847
+ padding: `0 ${Math.max(1, zoomFactor)}px`
28848
+ },
28849
+ type: "button",
28850
+ children: [
28851
+ renderCheckboxControl(checked, palette, zoomFactor, mixed),
28852
+ controlLabel ? /* @__PURE__ */ jsx3("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: controlLabel }) : null
28853
+ ]
28854
+ },
28855
+ `${pane}-${control.id}`
28856
+ );
28857
+ }
28858
+ if (control.kind === "button") {
28195
28859
  return /* @__PURE__ */ jsx3(
28196
- "div",
28860
+ "button",
28197
28861
  {
28862
+ disabled: readOnly || !onFormControlAction,
28863
+ onClick: (event) => {
28864
+ event.stopPropagation();
28865
+ onFormControlAction?.({ control, type: "activate" });
28866
+ },
28867
+ onPointerDown: stopControlPointerEvent,
28198
28868
  style: {
28199
28869
  ...commonStyle,
28200
28870
  background: "linear-gradient(180deg, #f8fafc 0%, #e2e8f0 100%)",
@@ -28205,68 +28875,168 @@ function XlsxGrid({
28205
28875
  padding: `0 ${6 * zoomFactor}px`,
28206
28876
  textAlign: "center"
28207
28877
  },
28878
+ type: "button",
28208
28879
  children: controlLabel
28209
28880
  },
28210
28881
  `${pane}-${control.id}`
28211
28882
  );
28212
- } else if (control.kind === "dropdown") {
28883
+ }
28884
+ if (control.kind === "dropdown") {
28885
+ const kind = buildFormControlKindInput(control);
28886
+ const selected = typeof control.selected === "number" ? control.selected : void 0;
28213
28887
  return /* @__PURE__ */ jsxs3(
28214
- "div",
28888
+ "select",
28215
28889
  {
28890
+ "aria-label": controlLabel || control.name || "Dropdown form control",
28891
+ disabled: readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28892
+ onChange: (event) => {
28893
+ 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
+ }
28901
+ },
28902
+ onClick: stopControlPointerEvent,
28903
+ onPointerDown: stopControlPointerEvent,
28216
28904
  style: {
28217
28905
  ...commonStyle,
28906
+ backgroundColor: SHEET_SURFACE,
28218
28907
  border: `${Math.max(1, zoomFactor)}px solid ${stroke}`,
28219
28908
  borderRadius: 2 * zoomFactor,
28220
28909
  boxSizing: "border-box",
28221
- justifyContent: "space-between",
28222
- padding: `0 ${6 * zoomFactor}px`
28910
+ padding: `0 ${4 * zoomFactor}px`
28223
28911
  },
28912
+ value: selected === void 0 ? "" : String(selected),
28224
28913
  children: [
28225
- /* @__PURE__ */ jsx3("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: controlLabel }),
28226
- /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", style: { fontSize: fontSizePx * 0.85 }, children: "\u25BC" })
28914
+ selected === void 0 ? /* @__PURE__ */ jsx3("option", { value: "", children: controlLabel }) : null,
28915
+ controlItems.map((item, index) => /* @__PURE__ */ jsx3("option", { value: index, children: item }, index))
28227
28916
  ]
28228
28917
  },
28229
28918
  `${pane}-${control.id}`
28230
28919
  );
28231
- } else if (control.kind === "editbox" || control.kind === "listbox" || control.kind === "scrollbar" || control.kind === "spinner" || control.kind === "unknown") {
28920
+ }
28921
+ if (control.kind === "listbox") {
28922
+ const kind = buildFormControlKindInput(control);
28923
+ const selected = Array.isArray(control.selected) ? control.selected : control.selected === void 0 ? [] : [control.selected];
28924
+ const allowsMultipleSelection = (control.selection ?? "single") !== "single";
28232
28925
  return /* @__PURE__ */ jsx3(
28233
- "div",
28926
+ "select",
28234
28927
  {
28928
+ "aria-label": controlLabel || control.name || "List box form control",
28929
+ disabled: readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28930
+ multiple: allowsMultipleSelection,
28931
+ onChange: (event) => {
28932
+ 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
+ }
28940
+ },
28941
+ onClick: stopControlPointerEvent,
28942
+ onPointerDown: stopControlPointerEvent,
28943
+ size: Math.max(2, Math.min(controlItems.length || 2, Math.floor(rect.height / Math.max(1, fontSizePx * 1.45)))),
28235
28944
  style: {
28236
28945
  ...commonStyle,
28946
+ alignItems: void 0,
28947
+ backgroundColor: SHEET_SURFACE,
28237
28948
  border: `${Math.max(1, zoomFactor)}px solid ${stroke}`,
28238
28949
  borderRadius: 2 * zoomFactor,
28239
28950
  boxSizing: "border-box",
28240
- padding: `0 ${6 * zoomFactor}px`
28951
+ display: "block",
28952
+ padding: 0
28241
28953
  },
28242
- children: /* @__PURE__ */ jsx3("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: controlLabel })
28954
+ value: allowsMultipleSelection ? selected.map(String) : selected[0] === void 0 ? "" : String(selected[0]),
28955
+ children: controlItems.map((item, index) => /* @__PURE__ */ jsx3("option", { value: index, children: item }, index))
28243
28956
  },
28244
28957
  `${pane}-${control.id}`
28245
28958
  );
28246
- } else {
28247
- content = null;
28248
28959
  }
28249
- return /* @__PURE__ */ jsxs3(
28960
+ if (control.kind === "scrollbar") {
28961
+ const kind = buildFormControlKindInput(control);
28962
+ const min = control.min ?? 0;
28963
+ const max = Math.max(min, control.max ?? 100);
28964
+ return /* @__PURE__ */ jsx3(
28965
+ "input",
28966
+ {
28967
+ "aria-label": controlLabel || control.name || "Scrollbar form control",
28968
+ disabled: readOnly || control.controlIndex === void 0,
28969
+ max,
28970
+ min,
28971
+ onChange: (event) => {
28972
+ event.stopPropagation();
28973
+ if (kind?.kind === "scrollbar") {
28974
+ const value = event.currentTarget.valueAsNumber;
28975
+ commitControlKind({ ...kind, value }, { value });
28976
+ }
28977
+ },
28978
+ onClick: stopControlPointerEvent,
28979
+ onPointerDown: stopControlPointerEvent,
28980
+ step: Math.max(1, control.increment ?? 1),
28981
+ style: {
28982
+ ...commonStyle,
28983
+ accentColor: paletteIsDark(palette) ? "#60a5fa" : "#2563eb",
28984
+ boxSizing: "border-box",
28985
+ padding: 0,
28986
+ writingMode: control.horizontal === false ? "vertical-lr" : void 0
28987
+ },
28988
+ type: "range",
28989
+ value: Math.max(min, Math.min(max, control.value ?? min))
28990
+ },
28991
+ `${pane}-${control.id}`
28992
+ );
28993
+ }
28994
+ if (control.kind === "spinner") {
28995
+ const kind = buildFormControlKindInput(control);
28996
+ const min = control.min ?? 0;
28997
+ const max = Math.max(min, control.max ?? 100);
28998
+ return /* @__PURE__ */ jsx3(
28999
+ "input",
29000
+ {
29001
+ "aria-label": controlLabel || control.name || "Spinner form control",
29002
+ disabled: readOnly || control.controlIndex === void 0,
29003
+ max,
29004
+ min,
29005
+ onChange: (event) => {
29006
+ 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
+ }
29011
+ },
29012
+ onClick: stopControlPointerEvent,
29013
+ onPointerDown: stopControlPointerEvent,
29014
+ step: Math.max(1, control.increment ?? 1),
29015
+ style: {
29016
+ ...commonStyle,
29017
+ backgroundColor: SHEET_SURFACE,
29018
+ border: `${Math.max(1, zoomFactor)}px solid ${stroke}`,
29019
+ borderRadius: 2 * zoomFactor,
29020
+ boxSizing: "border-box",
29021
+ padding: `0 ${4 * zoomFactor}px`
29022
+ },
29023
+ type: "number",
29024
+ value: Math.max(min, Math.min(max, control.value ?? min))
29025
+ },
29026
+ `${pane}-${control.id}`
29027
+ );
29028
+ }
29029
+ return /* @__PURE__ */ jsx3(
28250
29030
  "div",
28251
29031
  {
28252
29032
  style: {
28253
29033
  ...commonStyle,
29034
+ border: control.kind === "editbox" || control.kind === "unknown" ? `${Math.max(1, zoomFactor)}px solid ${stroke}` : void 0,
29035
+ borderRadius: 2 * zoomFactor,
29036
+ boxSizing: "border-box",
28254
29037
  padding: `0 ${Math.max(1, zoomFactor)}px`
28255
29038
  },
28256
- children: [
28257
- content,
28258
- controlLabel ? /* @__PURE__ */ jsx3(
28259
- "span",
28260
- {
28261
- style: {
28262
- overflow: "hidden",
28263
- textOverflow: "ellipsis",
28264
- whiteSpace: "nowrap"
28265
- },
28266
- children: controlLabel
28267
- }
28268
- ) : null
28269
- ]
29039
+ children: controlLabel ? /* @__PURE__ */ jsx3("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: controlLabel }) : null
28270
29040
  },
28271
29041
  `${pane}-${control.id}`
28272
29042
  );
@@ -29829,6 +30599,8 @@ function XlsxViewerInner({
29829
30599
  isDark = false,
29830
30600
  loadingComponent,
29831
30601
  loadingState,
30602
+ onFormControlAction,
30603
+ onFormControlChange,
29832
30604
  renderChartLoading,
29833
30605
  renderImage,
29834
30606
  renderImageSelection,
@@ -29891,6 +30663,8 @@ function XlsxViewerInner({
29891
30663
  getCellStyle,
29892
30664
  loadingComponent,
29893
30665
  loadingState,
30666
+ onFormControlAction,
30667
+ onFormControlChange,
29894
30668
  palette,
29895
30669
  renderChartLoading,
29896
30670
  renderImage,
@@ -29997,6 +30771,7 @@ function useXlsxViewerZoom() {
29997
30771
  }
29998
30772
  function useXlsxViewerEditing() {
29999
30773
  const {
30774
+ addFormControl,
30000
30775
  addSheet,
30001
30776
  canRedo,
30002
30777
  canUndo,
@@ -30004,14 +30779,18 @@ function useXlsxViewerEditing() {
30004
30779
  copySelectionToClipboard,
30005
30780
  defineNamedRange,
30006
30781
  fillSelection,
30782
+ formControls,
30007
30783
  getClipboardData,
30008
30784
  getCellDisplayValue: getCellDisplayValue2,
30009
30785
  getCellFormula,
30786
+ getFormControlItems,
30787
+ getSheetFormControls,
30010
30788
  mergeSelection,
30011
30789
  pasteFromClipboard,
30012
30790
  pasteStructuredClipboardData,
30013
30791
  pasteText,
30014
30792
  removeActiveSheet,
30793
+ removeFormControl,
30015
30794
  readOnly,
30016
30795
  redo,
30017
30796
  selectedCellFormula,
@@ -30028,10 +30807,12 @@ function useXlsxViewerEditing() {
30028
30807
  setSelectedCellStyle,
30029
30808
  setSelectedCellValue,
30030
30809
  undo,
30031
- unmergeSelection
30810
+ unmergeSelection,
30811
+ updateFormControl
30032
30812
  } = useXlsxViewer();
30033
30813
  return React4.useMemo(
30034
30814
  () => ({
30815
+ addFormControl,
30035
30816
  addSheet,
30036
30817
  canRedo,
30037
30818
  canUndo,
@@ -30039,14 +30820,18 @@ function useXlsxViewerEditing() {
30039
30820
  copySelectionToClipboard,
30040
30821
  defineNamedRange,
30041
30822
  fillSelection,
30823
+ formControls,
30042
30824
  getClipboardData,
30043
30825
  getCellDisplayValue: getCellDisplayValue2,
30044
30826
  getCellFormula,
30827
+ getFormControlItems,
30828
+ getSheetFormControls,
30045
30829
  mergeSelection,
30046
30830
  pasteFromClipboard,
30047
30831
  pasteStructuredClipboardData,
30048
30832
  pasteText,
30049
30833
  removeActiveSheet,
30834
+ removeFormControl,
30050
30835
  readOnly,
30051
30836
  redo,
30052
30837
  selectedCellFormula,
@@ -30063,9 +30848,11 @@ function useXlsxViewerEditing() {
30063
30848
  setSelectedCellStyle,
30064
30849
  setSelectedCellValue,
30065
30850
  undo,
30066
- unmergeSelection
30851
+ unmergeSelection,
30852
+ updateFormControl
30067
30853
  }),
30068
30854
  [
30855
+ addFormControl,
30069
30856
  addSheet,
30070
30857
  canRedo,
30071
30858
  canUndo,
@@ -30073,14 +30860,18 @@ function useXlsxViewerEditing() {
30073
30860
  copySelectionToClipboard,
30074
30861
  defineNamedRange,
30075
30862
  fillSelection,
30863
+ formControls,
30076
30864
  getClipboardData,
30077
30865
  getCellDisplayValue2,
30078
30866
  getCellFormula,
30867
+ getFormControlItems,
30868
+ getSheetFormControls,
30079
30869
  mergeSelection,
30080
30870
  pasteFromClipboard,
30081
30871
  pasteStructuredClipboardData,
30082
30872
  pasteText,
30083
30873
  removeActiveSheet,
30874
+ removeFormControl,
30084
30875
  readOnly,
30085
30876
  redo,
30086
30877
  selectedCellFormula,
@@ -30097,7 +30888,8 @@ function useXlsxViewerEditing() {
30097
30888
  setSelectedCellStyle,
30098
30889
  setSelectedCellValue,
30099
30890
  undo,
30100
- unmergeSelection
30891
+ unmergeSelection,
30892
+ updateFormControl
30101
30893
  ]
30102
30894
  );
30103
30895
  }