@homebound/beam 4.61.0 → 4.62.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.d.cts CHANGED
@@ -8768,6 +8768,31 @@ type OptionsOrLoad<O> = O[] | {
8768
8768
  options: O[] | undefined;
8769
8769
  };
8770
8770
 
8771
+ type NestedOption<O> = O & {
8772
+ children?: NestedOption<O>[];
8773
+ defaultCollapsed?: boolean;
8774
+ };
8775
+ type NestedOptionsOrLoad<O> = NestedOption<O>[] | {
8776
+ current: NestedOption<O>[];
8777
+ load: () => Promise<{
8778
+ options: NestedOption<O>[];
8779
+ }>;
8780
+ };
8781
+ type TreeSelectResponse<O, V extends Value> = {
8782
+ all: {
8783
+ values: V[];
8784
+ options: O[];
8785
+ };
8786
+ leaf: {
8787
+ values: V[];
8788
+ options: O[];
8789
+ };
8790
+ root: {
8791
+ values: V[];
8792
+ options: O[];
8793
+ };
8794
+ };
8795
+
8771
8796
  /** Props shared by `MenuSelectField`, `MenuMultiSelectField`, and `MenuTreeSelectField`. */
8772
8797
  type MenuSelectFieldBaseProps<O, V extends Value> = {
8773
8798
  label: string;
@@ -8781,7 +8806,7 @@ type MenuSelectFieldBaseProps<O, V extends Value> = {
8781
8806
  getOptionMenuLabel?: (opt: O, isUnsetOpt: boolean) => ReactNode;
8782
8807
  /** Extra text the menu search matches, in addition to `getOptionLabel`. */
8783
8808
  getOptionSearchText?: (opt: O) => string | readonly string[];
8784
- /** Sort options by `getOptionLabel`. On by default. `MenuTreeSelectField` defaults this to false. */
8809
+ /** Sort options by `getOptionLabel`. On by default. `MenuTreeSelectField` defaults this to false, and so does `MenuSelectField` when an option has `children`. */
8785
8810
  autoSort?: boolean;
8786
8811
  /** Debounced menu search text. The caller replaces `options` and should keep `mixedValues` in that list. Closing calls this with `""`. */
8787
8812
  onSearch?: (search: string) => void;
@@ -8806,7 +8831,9 @@ type MenuSelectFieldBaseProps<O, V extends Value> = {
8806
8831
  onFocus?: VoidFunction;
8807
8832
  onBlur?: VoidFunction;
8808
8833
  } & Pick<PresentationFieldProps, "labelStyle" | "compact" | "borderless" | "borderOnHover" | "fullWidth" | "visuallyDisabled" | "errorInTooltip" | "inputStylePalette">;
8809
- type MenuSelectFieldProps<O, V extends Value> = MenuSelectFieldBaseProps<O, V> & {
8834
+ type MenuSelectFieldProps<O, V extends Value> = Omit<MenuSelectFieldBaseProps<O, V>, "options"> & {
8835
+ /** Flat `O[]`, or `NestedOption<O>[]` when a row has `children`. `{ current, load, options }` loads either list. */
8836
+ options: OptionsOrLoad<O> | OptionsOrLoad<NestedOption<O>>;
8810
8837
  value: V | undefined;
8811
8838
  /** Value proposed by an AI model; puts the field in AI mode. */
8812
8839
  proposedValue?: V;
@@ -8846,31 +8873,6 @@ type BoundMenuSelectFieldProps<O, V extends Value> = Omit<MenuSelectFieldProps<O
8846
8873
  /** Binds `MenuSelectField` to a form field. */
8847
8874
  declare function BoundMenuSelectField<O, V extends Value>(props: BoundMenuSelectFieldProps<O, V>): JSX.Element;
8848
8875
 
8849
- type NestedOption<O> = O & {
8850
- children?: NestedOption<O>[];
8851
- defaultCollapsed?: boolean;
8852
- };
8853
- type NestedOptionsOrLoad<O> = NestedOption<O>[] | {
8854
- current: NestedOption<O>[];
8855
- load: () => Promise<{
8856
- options: NestedOption<O>[];
8857
- }>;
8858
- };
8859
- type TreeSelectResponse<O, V extends Value> = {
8860
- all: {
8861
- values: V[];
8862
- options: O[];
8863
- };
8864
- leaf: {
8865
- values: V[];
8866
- options: O[];
8867
- };
8868
- root: {
8869
- values: V[];
8870
- options: O[];
8871
- };
8872
- };
8873
-
8874
8876
  type MenuTreeSelectFieldProps<O, V extends Value> = MenuSelectFieldBaseProps<NestedOption<O>, V> & {
8875
8877
  /** Selected values. A parent selects its whole subtree; a parent whose children are all selected is selected too. */
8876
8878
  values: V[];
@@ -12038,7 +12040,9 @@ type ErrorMessageProps = {
12038
12040
  */
12039
12041
  declare function ErrorMessage(props: ErrorMessageProps): React$1.JSX.Element;
12040
12042
 
12041
- /** Select whose trigger shows the value and opens a menu. Search is included when the list is longer than `searchOptionLimit`, or when `onSearch` is set. See `MenuSelectField.mdx`. */
12043
+ /** Select whose trigger shows the value and opens a menu.
12044
+ *
12045
+ * Options with `children` are a tree: every node stays visible, and the trigger shows the path. Search is included when the list is longer than `searchOptionLimit`, or when `onSearch` is set. See `MenuSelectField.mdx`. */
12042
12046
  declare function MenuSelectField<O, V extends Value>(props: MenuSelectFieldProps<O, V>): JSX.Element;
12043
12047
 
12044
12048
  type ToggleButtonProps = {
package/dist/index.d.ts CHANGED
@@ -8768,6 +8768,31 @@ type OptionsOrLoad<O> = O[] | {
8768
8768
  options: O[] | undefined;
8769
8769
  };
8770
8770
 
8771
+ type NestedOption<O> = O & {
8772
+ children?: NestedOption<O>[];
8773
+ defaultCollapsed?: boolean;
8774
+ };
8775
+ type NestedOptionsOrLoad<O> = NestedOption<O>[] | {
8776
+ current: NestedOption<O>[];
8777
+ load: () => Promise<{
8778
+ options: NestedOption<O>[];
8779
+ }>;
8780
+ };
8781
+ type TreeSelectResponse<O, V extends Value> = {
8782
+ all: {
8783
+ values: V[];
8784
+ options: O[];
8785
+ };
8786
+ leaf: {
8787
+ values: V[];
8788
+ options: O[];
8789
+ };
8790
+ root: {
8791
+ values: V[];
8792
+ options: O[];
8793
+ };
8794
+ };
8795
+
8771
8796
  /** Props shared by `MenuSelectField`, `MenuMultiSelectField`, and `MenuTreeSelectField`. */
8772
8797
  type MenuSelectFieldBaseProps<O, V extends Value> = {
8773
8798
  label: string;
@@ -8781,7 +8806,7 @@ type MenuSelectFieldBaseProps<O, V extends Value> = {
8781
8806
  getOptionMenuLabel?: (opt: O, isUnsetOpt: boolean) => ReactNode;
8782
8807
  /** Extra text the menu search matches, in addition to `getOptionLabel`. */
8783
8808
  getOptionSearchText?: (opt: O) => string | readonly string[];
8784
- /** Sort options by `getOptionLabel`. On by default. `MenuTreeSelectField` defaults this to false. */
8809
+ /** Sort options by `getOptionLabel`. On by default. `MenuTreeSelectField` defaults this to false, and so does `MenuSelectField` when an option has `children`. */
8785
8810
  autoSort?: boolean;
8786
8811
  /** Debounced menu search text. The caller replaces `options` and should keep `mixedValues` in that list. Closing calls this with `""`. */
8787
8812
  onSearch?: (search: string) => void;
@@ -8806,7 +8831,9 @@ type MenuSelectFieldBaseProps<O, V extends Value> = {
8806
8831
  onFocus?: VoidFunction;
8807
8832
  onBlur?: VoidFunction;
8808
8833
  } & Pick<PresentationFieldProps, "labelStyle" | "compact" | "borderless" | "borderOnHover" | "fullWidth" | "visuallyDisabled" | "errorInTooltip" | "inputStylePalette">;
8809
- type MenuSelectFieldProps<O, V extends Value> = MenuSelectFieldBaseProps<O, V> & {
8834
+ type MenuSelectFieldProps<O, V extends Value> = Omit<MenuSelectFieldBaseProps<O, V>, "options"> & {
8835
+ /** Flat `O[]`, or `NestedOption<O>[]` when a row has `children`. `{ current, load, options }` loads either list. */
8836
+ options: OptionsOrLoad<O> | OptionsOrLoad<NestedOption<O>>;
8810
8837
  value: V | undefined;
8811
8838
  /** Value proposed by an AI model; puts the field in AI mode. */
8812
8839
  proposedValue?: V;
@@ -8846,31 +8873,6 @@ type BoundMenuSelectFieldProps<O, V extends Value> = Omit<MenuSelectFieldProps<O
8846
8873
  /** Binds `MenuSelectField` to a form field. */
8847
8874
  declare function BoundMenuSelectField<O, V extends Value>(props: BoundMenuSelectFieldProps<O, V>): JSX.Element;
8848
8875
 
8849
- type NestedOption<O> = O & {
8850
- children?: NestedOption<O>[];
8851
- defaultCollapsed?: boolean;
8852
- };
8853
- type NestedOptionsOrLoad<O> = NestedOption<O>[] | {
8854
- current: NestedOption<O>[];
8855
- load: () => Promise<{
8856
- options: NestedOption<O>[];
8857
- }>;
8858
- };
8859
- type TreeSelectResponse<O, V extends Value> = {
8860
- all: {
8861
- values: V[];
8862
- options: O[];
8863
- };
8864
- leaf: {
8865
- values: V[];
8866
- options: O[];
8867
- };
8868
- root: {
8869
- values: V[];
8870
- options: O[];
8871
- };
8872
- };
8873
-
8874
8876
  type MenuTreeSelectFieldProps<O, V extends Value> = MenuSelectFieldBaseProps<NestedOption<O>, V> & {
8875
8877
  /** Selected values. A parent selects its whole subtree; a parent whose children are all selected is selected too. */
8876
8878
  values: V[];
@@ -12038,7 +12040,9 @@ type ErrorMessageProps = {
12038
12040
  */
12039
12041
  declare function ErrorMessage(props: ErrorMessageProps): React$1.JSX.Element;
12040
12042
 
12041
- /** Select whose trigger shows the value and opens a menu. Search is included when the list is longer than `searchOptionLimit`, or when `onSearch` is set. See `MenuSelectField.mdx`. */
12043
+ /** Select whose trigger shows the value and opens a menu.
12044
+ *
12045
+ * Options with `children` are a tree: every node stays visible, and the trigger shows the path. Search is included when the list is longer than `searchOptionLimit`, or when `onSearch` is set. See `MenuSelectField.mdx`. */
12042
12046
  declare function MenuSelectField<O, V extends Value>(props: MenuSelectFieldProps<O, V>): JSX.Element;
12043
12047
 
12044
12048
  type ToggleButtonProps = {
package/dist/index.js CHANGED
@@ -18823,7 +18823,7 @@ function valueToKey(value) {
18823
18823
  }
18824
18824
 
18825
18825
  // src/inputs/internal/selectFieldHelpers.ts
18826
- function initializeOptions(optionsOrLoad, getOptionValue, getOptionLabel, unsetLabel, autoSort) {
18826
+ function initializeOptions(optionsOrLoad, getOptionValue, getOptionLabel, unsetLabel, autoSort, getChildren) {
18827
18827
  const result = [];
18828
18828
  if (unsetLabel) {
18829
18829
  result.push(unsetOption);
@@ -18840,8 +18840,8 @@ function initializeOptions(optionsOrLoad, getOptionValue, getOptionLabel, unsetL
18840
18840
  const toCheck = Array.isArray(current2) ? current2 : [current2];
18841
18841
  toCheck.forEach((current3) => {
18842
18842
  const value = getOptionValue(current3);
18843
- const found = options && options.find((o) => getOptionValue(o) === value);
18844
- if (!found) {
18843
+ const alreadyListed = options !== void 0 && optionsIncludeValue(options, value, getOptionValue, getChildren);
18844
+ if (!alreadyListed) {
18845
18845
  userOptions.push(current3);
18846
18846
  }
18847
18847
  });
@@ -18851,6 +18851,13 @@ function initializeOptions(optionsOrLoad, getOptionValue, getOptionLabel, unsetL
18851
18851
  return result;
18852
18852
  }
18853
18853
  var unsetOption = {};
18854
+ function optionsIncludeValue(options, value, getOptionValue, getChildren) {
18855
+ return options.some((option) => {
18856
+ if (getOptionValue(option) === value) return true;
18857
+ const children = getChildren?.(option);
18858
+ return children !== void 0 && optionsIncludeValue(children, value, getOptionValue, getChildren);
18859
+ });
18860
+ }
18854
18861
  function disabledOptionToKeyedTuple(disabledOption) {
18855
18862
  if (typeof disabledOption === "object" && disabledOption !== null) {
18856
18863
  return [valueToKey(disabledOption.value), disabledOption.reason];
@@ -19620,7 +19627,9 @@ function BaseOptionRow(props) {
19620
19627
  id,
19621
19628
  disabledReason,
19622
19629
  mixedLegendId,
19623
- variant
19630
+ variant,
19631
+ indent,
19632
+ isPathRoot
19624
19633
  } = props;
19625
19634
  const entry = item.value;
19626
19635
  const ref = useRef34(null);
@@ -19655,6 +19664,11 @@ function BaseOptionRow(props) {
19655
19664
  ref,
19656
19665
  ...trussProps76({
19657
19666
  ...rowCss,
19667
+ ...indent !== void 0 ? {
19668
+ paddingLeft: ["pl_var", {
19669
+ "--paddingLeft": `${indent}px`
19670
+ }]
19671
+ } : {},
19658
19672
  ...highlighted ? {
19659
19673
  backgroundColor: ["bgColor_var", {
19660
19674
  "--backgroundColor": "var(--b-surface-raised-hover)"
@@ -19671,7 +19685,17 @@ function BaseOptionRow(props) {
19671
19685
  children: [
19672
19686
  variant === "checkbox" && /* @__PURE__ */ jsx115(StyledCheckbox, { isSelected, isDisabled }),
19673
19687
  variant === "checkbox" && mixedLegendId && /* @__PURE__ */ jsx115(MixedDot, {}),
19674
- /* @__PURE__ */ jsx115("span", { className: "fg1 mw0", children: item.rendered }),
19688
+ /* @__PURE__ */ jsx115("span", { ...trussProps76({
19689
+ ...{
19690
+ flexGrow: "fg1",
19691
+ minWidth: "mw0"
19692
+ },
19693
+ ...isPathRoot ? {
19694
+ fontWeight: "fw6",
19695
+ fontSize: "fz_14px",
19696
+ lineHeight: "lh_20px"
19697
+ } : {}
19698
+ }), children: item.rendered }),
19675
19699
  variant === "check" && isSelected && /* @__PURE__ */ jsx115("span", { className: "df aic fs0", children: /* @__PURE__ */ jsx115(Icon, { icon: "check", color: isDisabled ? "--b-text-disabled" /* TextDisabled */ : "--b-selection-indicator" /* SelectionIndicator */ }) }),
19676
19700
  variant === "check" && mixedLegendId && // Centered in the check icon's 24px box.
19677
19701
  /* @__PURE__ */ jsx115("span", { className: "df aic jcc fs0 h_24px w_24px", children: /* @__PURE__ */ jsx115(MixedDot, {}) })
@@ -19681,6 +19705,21 @@ function BaseOptionRow(props) {
19681
19705
  });
19682
19706
  }
19683
19707
  function TreeOptionRow(props) {
19708
+ const {
19709
+ selectionMode,
19710
+ onExpandedChange,
19711
+ ...rowProps
19712
+ } = props;
19713
+ if (selectionMode === "single") return /* @__PURE__ */ jsx115(SingleTreeOptionRow, { ...rowProps });
19714
+ return /* @__PURE__ */ jsx115(MultiTreeOptionRow, { ...rowProps, selectionMode, onExpandedChange });
19715
+ }
19716
+ function SingleTreeOptionRow({
19717
+ row,
19718
+ ...rest
19719
+ }) {
19720
+ return /* @__PURE__ */ jsx115(BaseOptionRow, { ...rest, variant: "check", indent: rowPaddingX + row.level * singleTreeIndent, isPathRoot: row.level === 0 });
19721
+ }
19722
+ function MultiTreeOptionRow(props) {
19684
19723
  const {
19685
19724
  item,
19686
19725
  state,
@@ -19771,6 +19810,7 @@ function TreeOptionRow(props) {
19771
19810
  }
19772
19811
  var menuRowHeight = 40;
19773
19812
  var rowPaddingX = 16;
19813
+ var singleTreeIndent = 16;
19774
19814
  var treeIndent = 28;
19775
19815
  var rowCss = {
19776
19816
  display: "df",
@@ -19848,7 +19888,7 @@ function OptionList(props) {
19848
19888
  disabledReasons,
19849
19889
  initialIndex,
19850
19890
  loading,
19851
- rowVariant,
19891
+ selectionMode,
19852
19892
  tree,
19853
19893
  mixed
19854
19894
  } = menu;
@@ -19877,8 +19917,7 @@ function OptionList(props) {
19877
19917
  });
19878
19918
  }, [focusedKey, state.collection]);
19879
19919
  const [initialTop] = useState30(() => Math.max(0, Math.min(initialIndex, items.length - 1)));
19880
- const Row2 = rowVariant === "checkbox" ? MultiOptionRow : SingleOptionRow;
19881
- const isTree = rowVariant === "tree" && tree !== void 0;
19920
+ const Row2 = selectionMode === "multiple" ? MultiOptionRow : SingleOptionRow;
19882
19921
  const isTest = process.env.NODE_ENV === "test";
19883
19922
  return /* @__PURE__ */ jsx116("div", { ...listBoxProps, ...onKeyDown ? {
19884
19923
  onKeyDown,
@@ -19902,10 +19941,10 @@ function OptionList(props) {
19902
19941
  const item = items[index];
19903
19942
  const entry = item?.value;
19904
19943
  if (!item || !entry) return null;
19905
- const treeRow = isTree ? tree?.rows.get(String(item.key)) : void 0;
19944
+ const treeRow = tree?.rows.get(String(item.key));
19906
19945
  const mixedLegendId = mixed?.keys.has(entry.valueKey) ? mixed.legendId : void 0;
19907
19946
  if (treeRow && tree) {
19908
- return /* @__PURE__ */ jsx116(TreeOptionRow, { item, state, id: optionDomId(id, index), disabledReason: disabledReasons[entry.valueKey], mixedLegendId, row: treeRow, onExpandedChange: (expanded) => tree.setExpanded(String(item.key), expanded), ...tid });
19947
+ return /* @__PURE__ */ jsx116(TreeOptionRow, { item, state, id: optionDomId(id, index), disabledReason: disabledReasons[entry.valueKey], mixedLegendId, row: treeRow, selectionMode, onExpandedChange: (expanded) => tree.setExpanded(String(item.key), expanded), ...tid });
19909
19948
  }
19910
19949
  return /* @__PURE__ */ jsx116(Row2, { item, state, id: optionDomId(id, index), disabledReason: disabledReasons[entry.valueKey], mixedLegendId, ...tid });
19911
19950
  } }, isTest ? items.length : "virtuoso"),
@@ -20178,10 +20217,10 @@ function useMenuSelectField(props) {
20178
20217
  const getOptionLabel = (option) => unsetLabel && option === unsetOption ? unsetLabel : propGetOptionLabel(option);
20179
20218
  const getOptionValue = (option) => unsetLabel && option === unsetOption ? void 0 : propGetOptionValue(option);
20180
20219
  const options = useMemo28(
20181
- () => initializeOptions(optionsProp, getOptionValue, getOptionLabel, unsetLabel, autoSort),
20220
+ () => initializeOptions(optionsProp, getOptionValue, getOptionLabel, unsetLabel, autoSort, getChildren),
20182
20221
  // getOptionValue and getOptionLabel are typically inline lambdas
20183
20222
  // oxlint-disable-next-line react-hooks/exhaustive-deps
20184
- [optionsProp, autoSort, unsetLabel]
20223
+ [optionsProp, autoSort, unsetLabel, getChildren]
20185
20224
  );
20186
20225
  const { hoverProps, isHovered } = useHover13({});
20187
20226
  const { focusProps, isFocusVisible } = useFocusRing10();
@@ -20192,6 +20231,8 @@ function useMenuSelectField(props) {
20192
20231
  // oxlint-disable-next-line react-hooks/exhaustive-deps
20193
20232
  [options, getChildren]
20194
20233
  );
20234
+ const isSingleSelectTree = !multiple && menuTree !== void 0;
20235
+ const isMultiSelectTree = multiple && menuTree !== void 0;
20195
20236
  const allOptions = useMemo28(
20196
20237
  () => menuTree ? menuTree.orderedKeys.map((k) => menuTree.nodes.get(k).option) : options,
20197
20238
  [menuTree, options]
@@ -20210,12 +20251,17 @@ function useMenuSelectField(props) {
20210
20251
  const { effectiveValue, proposalProps } = useAiProposal(
20211
20252
  propValues,
20212
20253
  proposedValues,
20213
- (vs) => labelsFor(vs, allOptions, getOptionValue, getOptionLabel).join(", ")
20254
+ (vs) => labelsFor(
20255
+ vs,
20256
+ allOptions,
20257
+ getOptionValue,
20258
+ (option) => isSingleSelectTree && menuTree ? optionPathLabel(menuTree, valueToKey(getOptionValue(option)), getOptionLabel) ?? getOptionLabel(option) : getOptionLabel(option)
20259
+ ).join(", ")
20214
20260
  );
20215
20261
  const values = useMemo28(() => effectiveValue ?? [], [effectiveValue]);
20216
20262
  const selectedKeys = useMemo28(
20217
- () => menuTree ? normalizeTreeSelection(menuTree, values.map(valueToKey), disabledKeySet) : new Set(values.map(valueToKey)),
20218
- [menuTree, values, disabledKeySet]
20263
+ () => isMultiSelectTree && menuTree ? normalizeTreeSelection(menuTree, values.map(valueToKey), disabledKeySet) : new Set(values.map(valueToKey)),
20264
+ [isMultiSelectTree, menuTree, values, disabledKeySet]
20219
20265
  );
20220
20266
  const selectedRowKeys = useMemo28(
20221
20267
  () => menuTree ? new Set(menuTree.orderedKeys.filter((key) => selectedKeys.has(menuTree.nodes.get(key).valueKey))) : selectedKeys,
@@ -20259,10 +20305,11 @@ function useMenuSelectField(props) {
20259
20305
  return visibleNestedRows(
20260
20306
  roots,
20261
20307
  (node) => childNodes(menuTree, node),
20262
- (node) => isExpanded(node.key),
20308
+ // A single-select hierarchy has no chevrons, so every node stays visible.
20309
+ isMultiSelectTree ? (node) => isExpanded(node.key) : () => true,
20263
20310
  matchKeys ? (node) => matchKeys.has(node.valueKey) : void 0
20264
20311
  );
20265
- }, [menuTree, allOptions, search, serverSearch, contains, expandedKeys, searchCollapsedKeys]);
20312
+ }, [menuTree, isMultiSelectTree, allOptions, search, serverSearch, contains, expandedKeys, searchCollapsedKeys]);
20266
20313
  const filteredOptions = useMemo28(
20267
20314
  () => menuTree || !search || serverSearch ? options : options.filter(
20268
20315
  (option) => unsetLabel && option === unsetOption ? contains(getOptionLabel(option), search) : matchesSearch(option, search, getOptionLabel, getOptionSearchText, contains)
@@ -20273,7 +20320,7 @@ function useMenuSelectField(props) {
20273
20320
  );
20274
20321
  function onSelectionChange(keys) {
20275
20322
  if (keys === "all") return;
20276
- if (menuTree) {
20323
+ if (isMultiSelectTree && menuTree) {
20277
20324
  const added = [...keys].map(String).find((key2) => !selectedRowKeys.has(key2));
20278
20325
  const removed = [...selectedRowKeys].find((key2) => !keys.has(key2));
20279
20326
  const key = added ?? removed;
@@ -20290,7 +20337,7 @@ function useMenuSelectField(props) {
20290
20337
  close({ restoreFocus: true });
20291
20338
  return;
20292
20339
  }
20293
- const opt = optionsByKey.get(String(key));
20340
+ const opt = isSingleSelectTree && menuTree ? menuTree.nodes.get(String(key))?.option : optionsByKey.get(String(key));
20294
20341
  proposalProps.onUserEdit?.();
20295
20342
  onCommit(opt === void 0 ? [] : [getOptionValue(opt)], opt === void 0 ? [] : [opt]);
20296
20343
  close({ restoreFocus: true });
@@ -20416,7 +20463,7 @@ function useMenuSelectField(props) {
20416
20463
  switch (e.key) {
20417
20464
  case "ArrowRight":
20418
20465
  case "ArrowLeft":
20419
- onTreeArrow(e, current2);
20466
+ if (isMultiSelectTree) onTreeArrow(e, current2);
20420
20467
  break;
20421
20468
  case "ArrowDown":
20422
20469
  e.preventDefault();
@@ -20487,20 +20534,20 @@ function useMenuSelectField(props) {
20487
20534
  0,
20488
20535
  treeRows ? treeRows.findIndex(({ node }) => revealKeys.has(node.valueKey)) : filteredOptions.findIndex((o) => revealKeys.has(valueToKey(getOptionValue(o))))
20489
20536
  );
20490
- const selectedText = !multiple && selectedOptions[0] !== void 0 ? getOptionLabel(selectedOptions[0]) : "";
20537
+ const selectedText = !multiple && selectedOptions[0] !== void 0 ? isSingleSelectTree && menuTree ? optionPathLabel(menuTree, valueToKey(getOptionValue(selectedOptions[0])), getOptionLabel) ?? getOptionLabel(selectedOptions[0]) : getOptionLabel(selectedOptions[0]) : "";
20491
20538
  const hasSelection = menuTree ? selectedKeys.size > 0 : values.length > 0;
20492
20539
  const isMixed = dottedKeys.size > 0 && (multiple || !hasSelection);
20493
20540
  const displayText = isMixed ? mixedLabel : selectedText || (hasSelection ? "" : nothingSelectedText || placeholder || "");
20494
20541
  const isPlaceholder = !isMixed && !selectedText && !hasSelection && !nothingSelectedText && !!placeholder;
20495
- const selectedLabels = menuTree && tree && getChildren ? bucketTreeSelection(selectedOptions, getChildren)[tree.display].map((option) => ({
20542
+ const selectedLabels = isMultiSelectTree && getChildren && tree?.display ? bucketTreeSelection(selectedOptions, getChildren)[tree.display].map((option) => ({
20496
20543
  key: valueToKey(getOptionValue(option)),
20497
20544
  label: getOptionLabel(option)
20498
20545
  })) : multiple ? selectedOptions.map((o) => ({ key: valueToKey(getOptionValue(o)), label: getOptionLabel(o) })) : [];
20499
20546
  const badgeKeys = isMixed ? /* @__PURE__ */ new Set([...selectedLabels.map(({ key }) => key), ...dottedKeys]) : void 0;
20500
20547
  const badgeLabels = badgeKeys ? [...optionsByKey].filter(([key]) => badgeKeys.has(key)).map(([key, option]) => ({ key, label: getOptionLabel(option) })) : selectedLabels;
20501
20548
  const partialKeys = useMemo28(
20502
- () => menuTree ? partiallySelectedKeys(menuTree, selectedKeys) : void 0,
20503
- [menuTree, selectedKeys]
20549
+ () => isMultiSelectTree && menuTree ? partiallySelectedKeys(menuTree, selectedKeys) : void 0,
20550
+ [isMultiSelectTree, menuTree, selectedKeys]
20504
20551
  );
20505
20552
  const treeRowStates = treeRows ? new Map(
20506
20553
  treeRows.map(({ node, level }) => [
@@ -20508,15 +20555,15 @@ function useMenuSelectField(props) {
20508
20555
  {
20509
20556
  level,
20510
20557
  hasChildren: node.childKeys.length > 0,
20511
- isExpanded: node.childKeys.length > 0 && isExpanded(node.key),
20512
- isIndeterminate: !!partialKeys?.has(node.key)
20558
+ isExpanded: isMultiSelectTree ? node.childKeys.length > 0 && isExpanded(node.key) : true,
20559
+ isIndeterminate: isMultiSelectTree && !!partialKeys?.has(node.key)
20513
20560
  }
20514
20561
  ])
20515
20562
  ) : void 0;
20516
20563
  function deselect(key) {
20517
20564
  if (!multiple || disabledKeys.includes(key)) return;
20518
20565
  proposalProps.onUserEdit?.();
20519
- if (menuTree) {
20566
+ if (isMultiSelectTree && menuTree) {
20520
20567
  const toggledKeys = toggleTreeSelection(menuTree, selectedKeys, key, false, disabledKeySet);
20521
20568
  commitTree(toggledKeys, key);
20522
20569
  return;
@@ -20589,7 +20636,7 @@ function useMenuSelectField(props) {
20589
20636
  loading: optionsLoading,
20590
20637
  disabledReasons,
20591
20638
  initialIndex,
20592
- rowVariant: treeRowStates ? "tree" : multiple ? "checkbox" : "check",
20639
+ selectionMode,
20593
20640
  tree: treeRowStates ? { rows: treeRowStates, setExpanded: setTreeExpanded } : void 0,
20594
20641
  mixed: dottedKeys.size > 0 ? { keys: dottedKeys, legendId } : void 0,
20595
20642
  onKeyDown: onMenuKeyDown,
@@ -20659,6 +20706,17 @@ function labelsFor(values, options, getOptionValue, getOptionLabel) {
20659
20706
  }
20660
20707
  return labels;
20661
20708
  }
20709
+ function optionPathLabel(tree, valueKey, getOptionLabel) {
20710
+ const node = tree.orderedKeys.map((key) => tree.nodes.get(key)).find((entry) => entry.valueKey === valueKey);
20711
+ if (!node) return void 0;
20712
+ const labels = [];
20713
+ let current2 = node;
20714
+ while (current2) {
20715
+ labels.unshift(getOptionLabel(current2.option));
20716
+ current2 = current2.parentKey === void 0 ? void 0 : tree.nodes.get(current2.parentKey);
20717
+ }
20718
+ return labels.join(" > ");
20719
+ }
20662
20720
  function rootNodes(tree) {
20663
20721
  return tree.orderedKeys.filter((key) => tree.nodes.get(key).parentKey === void 0).map((key) => tree.nodes.get(key));
20664
20722
  }
@@ -20771,7 +20829,8 @@ function BaseMenuSelectField(props) {
20771
20829
  // src/inputs/MenuSelectField/MenuSelectField.tsx
20772
20830
  import { jsx as jsx119 } from "react/jsx-runtime";
20773
20831
  function MenuSelectField(props) {
20774
- const { value, proposedValue, onSelect, unsetLabel, ...rest } = props;
20832
+ const { value, proposedValue, onSelect, unsetLabel, options, autoSort, ...rest } = props;
20833
+ const nested = optionsHaveChildren(options);
20775
20834
  const values = useMemo29(() => {
20776
20835
  if (value !== void 0) return [value];
20777
20836
  return unsetLabel ? [void 0] : [];
@@ -20781,10 +20840,13 @@ function MenuSelectField(props) {
20781
20840
  BaseMenuSelectField,
20782
20841
  {
20783
20842
  ...rest,
20843
+ options,
20784
20844
  unsetLabel,
20845
+ autoSort: autoSort ?? !nested,
20785
20846
  selectionMode: "single",
20786
20847
  values,
20787
20848
  proposedValues,
20849
+ tree: nested ? { getChildren: menuOptionChildren } : void 0,
20788
20850
  onCommit: (next, opts) => {
20789
20851
  const option = opts[0];
20790
20852
  onSelect(next[0], option === unsetOption ? void 0 : option);
@@ -20792,6 +20854,19 @@ function MenuSelectField(props) {
20792
20854
  }
20793
20855
  );
20794
20856
  }
20857
+ function optionsHaveChildren(options) {
20858
+ const list = Array.isArray(options) ? options : [
20859
+ ...options.current === void 0 ? [] : Array.isArray(options.current) ? options.current : [options.current],
20860
+ ...options.options ?? []
20861
+ ];
20862
+ return list.some((option) => isNestedOption(option) && (option.children?.length ?? 0) > 0);
20863
+ }
20864
+ function menuOptionChildren(option) {
20865
+ return isNestedOption(option) ? option.children : void 0;
20866
+ }
20867
+ function isNestedOption(option) {
20868
+ return typeof option === "object" && option !== null && "children" in option && Array.isArray(option.children);
20869
+ }
20795
20870
 
20796
20871
  // src/components/Filters/Filters.tsx
20797
20872
  import { jsx as jsx120, jsxs as jsxs79 } from "react/jsx-runtime";