@underverse-ui/underverse 2.0.30 → 2.0.32

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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getPanelBorderRadiusClass,
3
3
  useUnderverseUIConfig
4
- } from "./chunk-37MHVJMV.js";
4
+ } from "./chunk-4TXHZS3S.js";
5
5
  import {
6
6
  cn,
7
7
  useGlobalI18n
@@ -253,4 +253,4 @@ var Modal_default = Modal;
253
253
  export {
254
254
  Modal_default
255
255
  };
256
- //# sourceMappingURL=chunk-2P7TSXEE.js.map
256
+ //# sourceMappingURL=chunk-RSSSQS2A.js.map
package/dist/index.cjs CHANGED
@@ -6393,10 +6393,15 @@ function isCrossRealmTableRow(value) {
6393
6393
  function isCrossRealmTableCell(value) {
6394
6394
  return isCrossRealmElement(value) && ["TD", "TH"].includes(String(value.tagName).toUpperCase()) && "cellIndex" in value;
6395
6395
  }
6396
+ function isValidProseMirrorPosition(doc, pos) {
6397
+ return Number.isInteger(pos) && pos >= 0 && pos <= doc.content.size;
6398
+ }
6396
6399
  function findTableRowNodeInfo(view, rowElement) {
6400
+ if (!view.dom.contains(rowElement)) return null;
6397
6401
  const firstCell = rowElement.querySelector("th,td");
6398
- if (!firstCell) return null;
6402
+ if (!isCrossRealmTableCell(firstCell) || !view.dom.contains(firstCell)) return null;
6399
6403
  const cellPos = view.posAtDOM(firstCell, 0);
6404
+ if (!isValidProseMirrorPosition(view.state.doc, cellPos)) return null;
6400
6405
  const $pos = view.state.doc.resolve(cellPos);
6401
6406
  for (let depth = $pos.depth; depth > 0; depth -= 1) {
6402
6407
  const node = $pos.node(depth);
@@ -6438,14 +6443,14 @@ function getSelectionTableCell(view) {
6438
6443
  const browserSelection = realm?.getSelection();
6439
6444
  const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
6440
6445
  const anchorCell = anchorElement?.closest?.("th,td");
6441
- if (realm && anchorCell instanceof realm.HTMLElement) {
6446
+ if (isCrossRealmTableCell(anchorCell) && view.dom.contains(anchorCell)) {
6442
6447
  return anchorCell;
6443
6448
  }
6444
6449
  const { from } = view.state.selection;
6445
6450
  const domAtPos = view.domAtPos(from);
6446
6451
  const element = resolveEventElement(domAtPos.node);
6447
6452
  const cell = element?.closest?.("th,td");
6448
- return realm && cell instanceof realm.HTMLElement ? cell : null;
6453
+ return isCrossRealmTableCell(cell) && view.dom.contains(cell) ? cell : null;
6449
6454
  }
6450
6455
  function resolveRowResizeTarget(cell, clientX, clientY) {
6451
6456
  const rect = cell.getBoundingClientRect();
@@ -8448,18 +8453,22 @@ function getTableAnchorPos(editor) {
8448
8453
  const editorWindow = editorDocument.defaultView;
8449
8454
  const selectionAnchor = resolveEventElement(editorWindow?.getSelection()?.anchorNode ?? null);
8450
8455
  const selectionCell2 = selectionAnchor?.closest?.("th,td");
8451
- if (editorWindow && selectionCell2 instanceof editorWindow.HTMLTableCellElement && editor.view.dom.contains(selectionCell2)) {
8452
- return editor.view.posAtDOM(selectionCell2, 0) + 1;
8456
+ if (isCrossRealmTableCell(selectionCell2) && editor.view.dom.contains(selectionCell2)) {
8457
+ const cellPos2 = editor.view.posAtDOM(selectionCell2, 0);
8458
+ return isValidProseMirrorPosition(editor.state.doc, cellPos2) && isValidProseMirrorPosition(editor.state.doc, cellPos2 + 1) ? cellPos2 + 1 : null;
8453
8459
  }
8454
8460
  const activeElement = editorWindow && editorDocument.activeElement instanceof editorWindow.Element ? editorDocument.activeElement : null;
8455
8461
  const activeCell = activeElement?.closest?.("th,td");
8456
- if (editorWindow && activeCell instanceof editorWindow.HTMLTableCellElement && editor.view.dom.contains(activeCell)) {
8457
- return editor.view.posAtDOM(activeCell, 0) + 1;
8462
+ if (isCrossRealmTableCell(activeCell) && editor.view.dom.contains(activeCell)) {
8463
+ const cellPos2 = editor.view.posAtDOM(activeCell, 0);
8464
+ return isValidProseMirrorPosition(editor.state.doc, cellPos2) && isValidProseMirrorPosition(editor.state.doc, cellPos2 + 1) ? cellPos2 + 1 : null;
8458
8465
  }
8459
8466
  const tables = editor.view.dom.querySelectorAll("table");
8460
8467
  if (tables.length !== 1) return null;
8461
8468
  const firstCell = tables[0]?.querySelector("th,td");
8462
- return editorWindow && firstCell instanceof editorWindow.HTMLTableCellElement ? editor.view.posAtDOM(firstCell, 0) + 1 : null;
8469
+ if (!isCrossRealmTableCell(firstCell) || !editor.view.dom.contains(firstCell)) return null;
8470
+ const cellPos = editor.view.posAtDOM(firstCell, 0);
8471
+ return isValidProseMirrorPosition(editor.state.doc, cellPos) && isValidProseMirrorPosition(editor.state.doc, cellPos + 1) ? cellPos + 1 : null;
8463
8472
  }
8464
8473
  function computeEditorUiRenderState(editor) {
8465
8474
  const textStyle = editor.getAttributes("textStyle");
@@ -15838,6 +15847,7 @@ var Combobox = ({
15838
15847
  virtualized = false,
15839
15848
  estimatedItemHeight = 44,
15840
15849
  overscan = 8,
15850
+ enableSearch: enableSearchProp,
15841
15851
  searchMode = "auto",
15842
15852
  onSearchChange,
15843
15853
  searchDebounceMs = 0,
@@ -15866,28 +15876,28 @@ var Combobox = ({
15866
15876
  const autoId = (0, import_react18.useId)();
15867
15877
  const resolvedId = id ? String(id) : `combobox-${autoId}`;
15868
15878
  const labelId = label ? `${resolvedId}-label` : void 0;
15869
- const enableSearch = options.length > 10 || searchMode === "manual" || minSearchLength > 0 || !!onSearchChange;
15879
+ const isSearchEnabled = enableSearchProp ?? (options.length > 10 || searchMode === "manual" || minSearchLength > 0 || !!onSearchChange);
15870
15880
  const trimmedQuery = query.trim();
15871
15881
  const queryMeetsMinimum = trimmedQuery.length >= minSearchLength;
15872
- const shouldPromptForSearch = minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
15882
+ const shouldPromptForSearch = isSearchEnabled && minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
15873
15883
  const filteredOptions = React31.useMemo(
15874
15884
  () => {
15875
15885
  if (shouldPromptForSearch) return [];
15876
- if (!enableSearch || searchMode === "manual") return options;
15886
+ if (!isSearchEnabled || searchMode === "manual") return options;
15877
15887
  const normalizedQuery = trimmedQuery.toLowerCase();
15878
15888
  if (!normalizedQuery) return options;
15879
15889
  return options.filter((o) => getOptionLabel(o).toLowerCase().includes(normalizedQuery));
15880
15890
  },
15881
- [enableSearch, options, searchMode, shouldPromptForSearch, trimmedQuery]
15891
+ [isSearchEnabled, options, searchMode, shouldPromptForSearch, trimmedQuery]
15882
15892
  );
15883
15893
  const renderLimitedOptions = React31.useMemo(
15884
15894
  () => {
15885
- if (trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
15895
+ if (!isSearchEnabled || trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
15886
15896
  return filteredOptions;
15887
15897
  }
15888
15898
  return filteredOptions.slice(0, maxInitialOptions);
15889
15899
  },
15890
- [filteredOptions, maxInitialOptions, trimmedQuery]
15900
+ [filteredOptions, isSearchEnabled, maxInitialOptions, trimmedQuery]
15891
15901
  );
15892
15902
  const canVirtualize = virtualized && !groupBy;
15893
15903
  const optionVirtualizer = (0, import_react_virtual.useVirtualizer)({
@@ -15948,7 +15958,7 @@ var Combobox = ({
15948
15958
  setQuery("");
15949
15959
  setActiveIndex(null);
15950
15960
  scrollVirtualListToStart();
15951
- } else if (enableSearch) {
15961
+ } else if (isSearchEnabled) {
15952
15962
  const triggerWindow = triggerRef.current?.ownerDocument.defaultView;
15953
15963
  if (!triggerWindow) return void 0;
15954
15964
  const timeoutId = triggerWindow.setTimeout(() => {
@@ -15957,14 +15967,14 @@ var Combobox = ({
15957
15967
  return () => triggerWindow.clearTimeout(timeoutId);
15958
15968
  }
15959
15969
  return void 0;
15960
- }, [enableSearch, open, scrollVirtualListToStart]);
15970
+ }, [isSearchEnabled, open, scrollVirtualListToStart]);
15961
15971
  React31.useEffect(() => {
15962
- if (!onSearchChange) return void 0;
15972
+ if (!isSearchEnabled || !onSearchChange) return void 0;
15963
15973
  const triggerWindow = triggerRef.current?.ownerDocument.defaultView;
15964
15974
  if (!triggerWindow) return void 0;
15965
15975
  const timeoutId = triggerWindow.setTimeout(() => onSearchChange(query), searchDebounceMs);
15966
15976
  return () => triggerWindow.clearTimeout(timeoutId);
15967
- }, [onSearchChange, query, searchDebounceMs]);
15977
+ }, [isSearchEnabled, onSearchChange, query, searchDebounceMs]);
15968
15978
  React31.useEffect(() => {
15969
15979
  if (process.env.NODE_ENV !== "production" && options.length > 300 && !virtualized && searchMode !== "manual" && maxInitialOptions === void 0) {
15970
15980
  console.warn(
@@ -16100,7 +16110,7 @@ var Combobox = ({
16100
16110
  style: usePortal ? { maxHeight: "var(--underverse-popover-available-height)" } : void 0,
16101
16111
  className: cn("w-full overflow-hidden", usePortal && "flex min-h-0 flex-col", getPanelBorderRadiusClass(resolvedBorderMode)),
16102
16112
  children: [
16103
- enableSearch && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: cn("relative shrink-0 border-b border-border/30", size === "xs" ? "p-1.5" : size === "sm" ? "p-2" : size === "lg" ? "p-3" : size === "xl" ? "p-3.5" : "p-2.5"), children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "relative", children: [
16113
+ isSearchEnabled && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: cn("relative shrink-0 border-b border-border/30", size === "xs" ? "p-1.5" : size === "sm" ? "p-2" : size === "lg" ? "p-3" : size === "xl" ? "p-3.5" : "p-2.5"), children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "relative", children: [
16104
16114
  /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
16105
16115
  import_lucide_react15.Search,
16106
16116
  {
@@ -16256,11 +16266,24 @@ var Combobox = ({
16256
16266
  "aria-invalid": !!effectiveError,
16257
16267
  id: resolvedId,
16258
16268
  "aria-labelledby": labelId,
16269
+ "aria-activedescendant": activeIndex !== null ? `${resolvedId}-item-${activeIndex}` : void 0,
16259
16270
  onKeyDown: (e) => {
16260
16271
  if (disabled) return;
16261
- if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
16272
+ if (!open && (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ")) {
16262
16273
  e.preventDefault();
16263
16274
  setOpen(true);
16275
+ if (!isSearchEnabled && e.key === "ArrowDown") moveActiveIndex(1);
16276
+ } else if (open && !isSearchEnabled && e.key === "ArrowDown") {
16277
+ e.preventDefault();
16278
+ moveActiveIndex(1);
16279
+ } else if (open && !isSearchEnabled && e.key === "ArrowUp") {
16280
+ e.preventDefault();
16281
+ moveActiveIndex(-1);
16282
+ } else if (open && !isSearchEnabled && e.key === "Enter") {
16283
+ e.preventDefault();
16284
+ if (activeIndex !== null && renderLimitedOptions[activeIndex] && !getOptionDisabled(renderLimitedOptions[activeIndex])) {
16285
+ handleSelect(renderLimitedOptions[activeIndex]);
16286
+ }
16264
16287
  } else if (e.key === "Escape") {
16265
16288
  e.preventDefault();
16266
16289
  setOpen(false);
@@ -26250,6 +26273,7 @@ var MultiCombobox = ({
26250
26273
  virtualized = false,
26251
26274
  estimatedItemHeight = 44,
26252
26275
  overscan = 8,
26276
+ enableSearch: enableSearchProp,
26253
26277
  searchMode = "auto",
26254
26278
  onSearchChange,
26255
26279
  searchDebounceMs = 0,
@@ -26282,25 +26306,25 @@ var MultiCombobox = ({
26282
26306
  ),
26283
26307
  [options]
26284
26308
  );
26285
- const enableSearch = normalizedOptions.length > 10 || searchMode === "manual" || minSearchLength > 0 || !!onSearchChange;
26309
+ const isSearchEnabled = enableSearchProp ?? (normalizedOptions.length > 10 || searchMode === "manual" || minSearchLength > 0 || !!onSearchChange);
26286
26310
  const trimmedQuery = query.trim();
26287
26311
  const queryMeetsMinimum = trimmedQuery.length >= minSearchLength;
26288
- const shouldPromptForSearch = minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
26312
+ const shouldPromptForSearch = isSearchEnabled && minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
26289
26313
  const filtered = React47.useMemo(() => {
26290
26314
  if (shouldPromptForSearch) return [];
26291
- if (!enableSearch || searchMode === "manual") return normalizedOptions;
26315
+ if (!isSearchEnabled || searchMode === "manual") return normalizedOptions;
26292
26316
  const normalizedQuery = trimmedQuery.toLowerCase();
26293
26317
  if (!normalizedQuery) return normalizedOptions;
26294
26318
  return normalizedOptions.filter(
26295
26319
  (opt) => opt.label.toLowerCase().includes(normalizedQuery) || opt.description?.toLowerCase().includes(normalizedQuery)
26296
26320
  );
26297
- }, [enableSearch, normalizedOptions, searchMode, shouldPromptForSearch, trimmedQuery]);
26321
+ }, [isSearchEnabled, normalizedOptions, searchMode, shouldPromptForSearch, trimmedQuery]);
26298
26322
  const renderLimitedOptions = React47.useMemo(() => {
26299
- if (trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
26323
+ if (!isSearchEnabled || trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
26300
26324
  return filtered;
26301
26325
  }
26302
26326
  return filtered.slice(0, maxInitialOptions);
26303
- }, [filtered, maxInitialOptions, trimmedQuery]);
26327
+ }, [filtered, isSearchEnabled, maxInitialOptions, trimmedQuery]);
26304
26328
  const canVirtualize = virtualized && !groupBy;
26305
26329
  const optionVirtualizer = (0, import_react_virtual2.useVirtualizer)({
26306
26330
  count: canVirtualize ? renderLimitedOptions.length : 0,
@@ -26344,24 +26368,32 @@ var MultiCombobox = ({
26344
26368
  onChange(value.filter((v) => v !== val));
26345
26369
  };
26346
26370
  const handleKeyDown2 = (e) => {
26347
- if (!open) setOpen(true);
26348
26371
  if (e.key === "ArrowDown") {
26349
26372
  e.preventDefault();
26373
+ if (!open) setOpen(true);
26350
26374
  if (renderLimitedOptions.length === 0) return;
26351
26375
  const next = activeIndex === null ? 0 : (activeIndex + 1) % renderLimitedOptions.length;
26352
26376
  setActiveIndex(next);
26353
26377
  scrollVirtualListToIndex(next);
26354
26378
  } else if (e.key === "ArrowUp") {
26355
26379
  e.preventDefault();
26380
+ if (!open) setOpen(true);
26356
26381
  if (renderLimitedOptions.length === 0) return;
26357
26382
  const next = activeIndex === null ? renderLimitedOptions.length - 1 : (activeIndex - 1 + renderLimitedOptions.length) % renderLimitedOptions.length;
26358
26383
  setActiveIndex(next);
26359
26384
  scrollVirtualListToIndex(next);
26360
26385
  } else if (e.key === "Enter") {
26361
26386
  e.preventDefault();
26387
+ if (!open) {
26388
+ setOpen(true);
26389
+ return;
26390
+ }
26362
26391
  if (activeIndex !== null && renderLimitedOptions[activeIndex]) {
26363
26392
  toggleSelect(renderLimitedOptions[activeIndex].value);
26364
26393
  }
26394
+ } else if (e.key === " " && !open) {
26395
+ e.preventDefault();
26396
+ setOpen(true);
26365
26397
  } else if (e.key === "Escape") {
26366
26398
  e.preventDefault();
26367
26399
  setOpen(false);
@@ -26377,7 +26409,7 @@ var MultiCombobox = ({
26377
26409
  }
26378
26410
  }, [disabled, required, value.length]);
26379
26411
  React47.useEffect(() => {
26380
- if (open && enableSearch) {
26412
+ if (open && isSearchEnabled) {
26381
26413
  setTimeout(() => {
26382
26414
  inputRef.current?.focus();
26383
26415
  }, 100);
@@ -26386,12 +26418,12 @@ var MultiCombobox = ({
26386
26418
  setActiveIndex(null);
26387
26419
  scrollVirtualListToStart();
26388
26420
  }
26389
- }, [enableSearch, open, scrollVirtualListToStart]);
26421
+ }, [isSearchEnabled, open, scrollVirtualListToStart]);
26390
26422
  React47.useEffect(() => {
26391
- if (!onSearchChange) return void 0;
26423
+ if (!isSearchEnabled || !onSearchChange) return void 0;
26392
26424
  const timeoutId = window.setTimeout(() => onSearchChange(query), searchDebounceMs);
26393
26425
  return () => window.clearTimeout(timeoutId);
26394
- }, [onSearchChange, query, searchDebounceMs]);
26426
+ }, [isSearchEnabled, onSearchChange, query, searchDebounceMs]);
26395
26427
  React47.useEffect(() => {
26396
26428
  if (process.env.NODE_ENV !== "production" && normalizedOptions.length > 300 && !virtualized && searchMode !== "manual" && maxInitialOptions === void 0) {
26397
26429
  console.warn(
@@ -26466,6 +26498,10 @@ var MultiCombobox = ({
26466
26498
  return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
26467
26499
  "li",
26468
26500
  {
26501
+ id: `${resolvedId}-option-${index}`,
26502
+ role: "option",
26503
+ "aria-selected": isSelected,
26504
+ "aria-disabled": isDisabled,
26469
26505
  ref: (node) => {
26470
26506
  measureRef?.(node);
26471
26507
  listRef.current[index] = node;
@@ -26477,7 +26513,7 @@ var MultiCombobox = ({
26477
26513
  e.preventDefault();
26478
26514
  e.stopPropagation();
26479
26515
  if (!isDisabled) toggleSelect(item.value);
26480
- inputRef.current?.focus();
26516
+ (isSearchEnabled ? inputRef.current : triggerRef.current)?.focus();
26481
26517
  },
26482
26518
  children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: cn("dropdown-item", isDisabled && "opacity-50 cursor-not-allowed pointer-events-none"), children: renderOption(item, isSelected) })
26483
26519
  },
@@ -26487,6 +26523,10 @@ var MultiCombobox = ({
26487
26523
  return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
26488
26524
  "li",
26489
26525
  {
26526
+ id: `${resolvedId}-option-${index}`,
26527
+ role: "option",
26528
+ "aria-selected": isSelected,
26529
+ "aria-disabled": isDisabled,
26490
26530
  ref: (node) => {
26491
26531
  measureRef?.(node);
26492
26532
  listRef.current[index] = node;
@@ -26498,7 +26538,7 @@ var MultiCombobox = ({
26498
26538
  e.preventDefault();
26499
26539
  e.stopPropagation();
26500
26540
  if (!isDisabled) toggleSelect(item.value);
26501
- inputRef.current?.focus();
26541
+ (isSearchEnabled ? inputRef.current : triggerRef.current)?.focus();
26502
26542
  },
26503
26543
  children: /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
26504
26544
  "div",
@@ -26554,7 +26594,7 @@ var MultiCombobox = ({
26554
26594
  }
26555
26595
  )
26556
26596
  ] }),
26557
- enableSearch && /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "relative border-b border-border/40", children: [
26597
+ isSearchEnabled && /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "relative border-b border-border/40", children: [
26558
26598
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react27.Search, { className: cn("absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", sizeStyles8[size].icon) }),
26559
26599
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
26560
26600
  "input",
@@ -26666,6 +26706,8 @@ var MultiCombobox = ({
26666
26706
  "aria-controls": listboxId,
26667
26707
  "aria-required": required,
26668
26708
  "aria-invalid": !!effectiveError,
26709
+ "aria-activedescendant": activeIndex !== null ? `${resolvedId}-option-${activeIndex}` : void 0,
26710
+ onKeyDown: !isSearchEnabled ? handleKeyDown2 : void 0,
26669
26711
  className: cn(
26670
26712
  "group flex w-full items-center gap-2 transition-all duration-200",
26671
26713
  formControlFixedClass,
@@ -26735,7 +26777,13 @@ var MultiCombobox = ({
26735
26777
  e.stopPropagation();
26736
26778
  handleClearAll();
26737
26779
  },
26738
- onKeyDown: (e) => (e.key === "Enter" || e.key === " ") && handleClearAll(),
26780
+ onKeyDown: (e) => {
26781
+ if (e.key === "Enter" || e.key === " ") {
26782
+ e.preventDefault();
26783
+ e.stopPropagation();
26784
+ handleClearAll();
26785
+ }
26786
+ },
26739
26787
  className: "opacity-0 group-hover:opacity-100 transition-all duration-200 p-1 rounded-lg hover:bg-destructive/10 text-muted-foreground hover:text-destructive",
26740
26788
  children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react27.X, { className: "h-3.5 w-3.5" })
26741
26789
  }
@@ -28381,7 +28429,7 @@ function CategoryTreeSelect(props) {
28381
28429
  const trimmedQuery = (0, import_react26.useMemo)(() => query.trim(), [query]);
28382
28430
  const normalizedQuery = (0, import_react26.useMemo)(() => trimmedQuery.toLowerCase(), [trimmedQuery]);
28383
28431
  const queryMeetsMinimum = trimmedQuery.length >= minSearchLength;
28384
- const shouldPromptForSearch = minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
28432
+ const shouldPromptForSearch = isSearchEnabled && minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
28385
28433
  const isSearchMode = isSearchEnabled && normalizedQuery.length > 0;
28386
28434
  const shouldAutoExpandSearchResults = searchMode === "auto" && isSearchMode;
28387
28435
  const effectiveExpandedNodes = (0, import_react26.useMemo)(() => getExpandedNodesState(expandedIds, expandedNodes), [expandedIds, expandedNodes]);
@@ -28425,12 +28473,12 @@ function CategoryTreeSelect(props) {
28425
28473
  return visible;
28426
28474
  }, [byId, categories, childrenMap, isSearchMode, normalizedQuery, searchMode, shouldPromptForSearch]);
28427
28475
  (0, import_react26.useEffect)(() => {
28428
- if (!onSearchChange) return;
28476
+ if (!isSearchEnabled || !onSearchChange) return;
28429
28477
  const timeoutId = window.setTimeout(() => {
28430
28478
  onSearchChange(trimmedQuery);
28431
28479
  }, Math.max(0, searchDebounceMs));
28432
28480
  return () => window.clearTimeout(timeoutId);
28433
- }, [onSearchChange, searchDebounceMs, trimmedQuery]);
28481
+ }, [isSearchEnabled, onSearchChange, searchDebounceMs, trimmedQuery]);
28434
28482
  (0, import_react26.useEffect)(() => {
28435
28483
  if (!isOpen) return;
28436
28484
  if (!isSearchEnabled) return;
@@ -28513,7 +28561,7 @@ function CategoryTreeSelect(props) {
28513
28561
  const flattenedRows = (0, import_react26.useMemo)(() => {
28514
28562
  if (shouldPromptForSearch) return [];
28515
28563
  const rows = flattenVisibleCategories(effectiveParentCategories, effectiveChildrenMap, effectiveExpandedNodes, shouldAutoExpandSearchResults);
28516
- if (trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
28564
+ if (!isSearchEnabled || trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
28517
28565
  return rows;
28518
28566
  }
28519
28567
  const limitedRows = rows.slice(0, maxInitialOptions);
@@ -28541,6 +28589,7 @@ function CategoryTreeSelect(props) {
28541
28589
  effectiveExpandedNodes,
28542
28590
  effectiveParentCategories,
28543
28591
  expandToId,
28592
+ isSearchEnabled,
28544
28593
  maxInitialOptions,
28545
28594
  selectedIds,
28546
28595
  shouldAutoExpandSearchResults,
@@ -40821,6 +40870,7 @@ function getBubbleMenuPosition({
40821
40870
  end,
40822
40871
  menuSize,
40823
40872
  viewport,
40873
+ preferredPlacement = "auto",
40824
40874
  offset = 16,
40825
40875
  padding = 8
40826
40876
  }) {
@@ -40828,7 +40878,7 @@ function getBubbleMenuPosition({
40828
40878
  const selectionBottom = Math.max(start.bottom, end.bottom);
40829
40879
  const spaceAbove = selectionTop - offset - padding;
40830
40880
  const spaceBelow = viewport.height - padding - selectionBottom - offset;
40831
- const placement = menuSize.height <= spaceAbove || spaceAbove >= spaceBelow ? "top" : "bottom";
40881
+ const placement = preferredPlacement === "auto" ? menuSize.height <= spaceAbove || spaceAbove >= spaceBelow ? "top" : "bottom" : preferredPlacement;
40832
40882
  const halfWidth = menuSize.width / 2;
40833
40883
  const minLeft = padding + halfWidth;
40834
40884
  const maxLeft = viewport.width - padding - halfWidth;
@@ -41732,6 +41782,7 @@ var LinkPreviewContent = ({ editor, onEdit }) => {
41732
41782
  };
41733
41783
  var CustomBubbleMenu = ({
41734
41784
  editor,
41785
+ placement = "auto",
41735
41786
  fontSizes,
41736
41787
  lineHeights
41737
41788
  }) => {
@@ -41825,6 +41876,7 @@ var CustomBubbleMenu = ({
41825
41876
  width: editorWindow?.innerWidth ?? window.innerWidth,
41826
41877
  height: editorWindow?.innerHeight ?? window.innerHeight
41827
41878
  },
41879
+ preferredPlacement: placement,
41828
41880
  offset: BUBBLE_MENU_OFFSET
41829
41881
  }));
41830
41882
  if (keepOpenRef.current) {
@@ -41871,7 +41923,7 @@ var CustomBubbleMenu = ({
41871
41923
  editorWindow?.removeEventListener("resize", schedulePositionUpdate);
41872
41924
  editorWindow?.removeEventListener("scroll", schedulePositionUpdate, true);
41873
41925
  };
41874
- }, [editor, editorDocument, editorWindow]);
41926
+ }, [editor, editorDocument, editorWindow, placement]);
41875
41927
  (0, import_react76.useEffect)(() => {
41876
41928
  if (!isVisible || !portalWindow?.ResizeObserver) return;
41877
41929
  const menu = menuRef.current;
@@ -41925,6 +41977,7 @@ var CustomBubbleMenu = ({
41925
41977
  {
41926
41978
  ref: menuRef,
41927
41979
  "data-popover": true,
41980
+ "data-placement": position.placement,
41928
41981
  className: "fixed z-[99999] flex rounded-xl border border-border/40 bg-card text-card-foreground shadow-lg backdrop-blur-sm overflow-hidden animate-in fade-in-0 zoom-in-95",
41929
41982
  style: {
41930
41983
  top: `${position.top}px`,
@@ -46740,6 +46793,7 @@ function getCellFromTarget(target) {
46740
46793
  return null;
46741
46794
  }
46742
46795
  function findTableInfo(editor, pos) {
46796
+ if (!isValidProseMirrorPosition(editor.state.doc, pos)) return null;
46743
46797
  const $pos = editor.state.doc.resolve(pos);
46744
46798
  for (let depth = $pos.depth; depth > 0; depth -= 1) {
46745
46799
  const node = $pos.node(depth);
@@ -46782,6 +46836,7 @@ function buildLogicalColumnMetrics({
46782
46836
  for (const tableCell of Array.from(firstRow.cells)) {
46783
46837
  if (!isTableCellElement(tableCell)) continue;
46784
46838
  const cellPos = editor.view.posAtDOM(tableCell, 0);
46839
+ if (!isValidProseMirrorPosition(editor.state.doc, cellPos)) continue;
46785
46840
  const relativeCellPos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
46786
46841
  if (relativeCellPos == null) continue;
46787
46842
  const cellMapRect = map.findCell(relativeCellPos);
@@ -46855,18 +46910,20 @@ function buildLogicalRowMetrics({
46855
46910
  if (visualRows.length > 0) {
46856
46911
  return visualRows.sort((a, b) => a.index - b.index);
46857
46912
  }
46858
- return rows.map((tableRow, index) => {
46913
+ return rows.flatMap((tableRow, index) => {
46859
46914
  const rowRect = tableRow.getBoundingClientRect();
46860
46915
  const anchorCell = tableRow.cells.item(0) ?? cornerCell;
46861
46916
  const start = rowRect.height > 0 ? rowRect.top - surfaceRect.top - surface.clientTop + surface.scrollTop : tableTop + index * fallbackHeight;
46862
46917
  const size = metricOrFallback(rowRect.height, fallbackHeight);
46863
- return {
46918
+ const cellPos = editor.view.posAtDOM(anchorCell, 0);
46919
+ if (!isValidProseMirrorPosition(editor.state.doc, cellPos)) return [];
46920
+ return [{
46864
46921
  index,
46865
- cellPos: editor.view.posAtDOM(anchorCell, 0),
46922
+ cellPos,
46866
46923
  start,
46867
46924
  size,
46868
46925
  center: start + size / 2
46869
- };
46926
+ }];
46870
46927
  });
46871
46928
  }
46872
46929
  function getTableCellTextSelectionRange(editor, cellPos) {
@@ -46886,6 +46943,7 @@ function getTableCellTextSelectionRange(editor, cellPos) {
46886
46943
  return from !== null && to !== null && from < to ? { from, to } : null;
46887
46944
  }
46888
46945
  function buildTableControlLayout(editor, surface, cell) {
46946
+ if (!editor.view.dom.contains(cell)) return null;
46889
46947
  const row = cell.closest("tr");
46890
46948
  const table = cell.closest("table");
46891
46949
  if (!isTableRowElement(row) || !isTableElement(table)) {
@@ -46894,6 +46952,7 @@ function buildTableControlLayout(editor, surface, cell) {
46894
46952
  const rows = Array.from(table.rows).filter(isTableRowElement);
46895
46953
  const cornerCell = getLastCell(table);
46896
46954
  const cellPos = editor.view.posAtDOM(cell, 0);
46955
+ if (!isValidProseMirrorPosition(editor.state.doc, cellPos)) return null;
46897
46956
  const tableInfo = findTableInfo(editor, cellPos);
46898
46957
  if (rows.length === 0 || !tableInfo || !isTableCellElement(cornerCell)) {
46899
46958
  return null;
@@ -47851,7 +47910,7 @@ function getSelectedCell(editor) {
47851
47910
  const browserSelection = editorWindow?.getSelection();
47852
47911
  const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
47853
47912
  const anchorCell = anchorElement?.closest?.("th,td");
47854
- if (isCrossRealmTableCell(anchorCell)) {
47913
+ if (isCrossRealmTableCell(anchorCell) && editor.view.dom.contains(anchorCell)) {
47855
47914
  return anchorCell;
47856
47915
  }
47857
47916
  const domAtPos = editor.view.domAtPos(editor.state.selection.from);
@@ -49328,12 +49387,12 @@ function getFormulaEditingTableContext(view) {
49328
49387
  if (!getCellText2(node).startsWith("=")) return null;
49329
49388
  const cellPos = $from.before(depth);
49330
49389
  const cellDom = view.nodeDOM(cellPos);
49331
- if (!(cellDom instanceof HTMLTableCellElement)) return null;
49390
+ if (!isCrossRealmTableCell(cellDom) || !view.dom.contains(cellDom)) return null;
49332
49391
  const tableDepth = depth - 2;
49333
49392
  const tableNode = tableDepth > 0 ? $from.node(tableDepth) : null;
49334
49393
  if (!tableNode || tableNode.type.name !== "table") return null;
49335
49394
  const tableDom = cellDom.closest("table");
49336
- if (!(tableDom instanceof HTMLTableElement)) return null;
49395
+ if (!isCrossRealmTable(tableDom) || !view.dom.contains(tableDom)) return null;
49337
49396
  const tableMap = import_tables9.TableMap.get(tableNode);
49338
49397
  const tableStart = $from.start(tableDepth);
49339
49398
  const formulaCellRect = tableMap.findCell(cellPos - tableStart);
@@ -49405,6 +49464,7 @@ function getReferenceInsertionPrefix(view, cellContentStart, insertionPos) {
49405
49464
  return isInsideFunctionArguments && followsReference ? "," : "";
49406
49465
  }
49407
49466
  function findTableForPos(view, pos) {
49467
+ if (!isValidProseMirrorPosition(view.state.doc, pos)) return null;
49408
49468
  const $pos = view.state.doc.resolve(pos);
49409
49469
  for (let depth = $pos.depth; depth > 0; depth -= 1) {
49410
49470
  const node = $pos.node(depth);
@@ -49433,8 +49493,9 @@ function getCellRelativePosFromDomPos2(map, tableStart, domPos) {
49433
49493
  function getFormulaTableCellInfo(view, target, tablePos) {
49434
49494
  const element = resolveEventElement(target);
49435
49495
  const cell = element?.closest?.("th,td");
49436
- if (!(cell instanceof HTMLTableCellElement)) return null;
49496
+ if (!isCrossRealmTableCell(cell) || !view.dom.contains(cell)) return null;
49437
49497
  const domPos = view.posAtDOM(cell, 0);
49498
+ if (!isValidProseMirrorPosition(view.state.doc, domPos)) return null;
49438
49499
  const tableInfo = findTableForPos(view, domPos);
49439
49500
  if (!tableInfo || tableInfo.pos !== tablePos) return null;
49440
49501
  const map = import_tables9.TableMap.get(tableInfo.node);
@@ -50132,6 +50193,7 @@ var UEditor = import_react86.default.forwardRef(({
50132
50193
  autofocus = false,
50133
50194
  showToolbar = true,
50134
50195
  showBubbleMenu = true,
50196
+ bubbleMenuPlacement = "auto",
50135
50197
  showFloatingMenu = false,
50136
50198
  showCharacterCount = true,
50137
50199
  showFooter = true,
@@ -50491,6 +50553,7 @@ var UEditor = import_react86.default.forwardRef(({
50491
50553
  CustomBubbleMenu,
50492
50554
  {
50493
50555
  editor,
50556
+ placement: bubbleMenuPlacement,
50494
50557
  fontSizes,
50495
50558
  lineHeights
50496
50559
  }