@homebound/beam 3.62.0 → 3.63.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
@@ -10063,6 +10063,8 @@ type FormSectionLayoutProps = {
10063
10063
  description?: ReactNode;
10064
10064
  /** Rendered top-right of the title row, e.g. a "Save draft" button. */
10065
10065
  actions?: ButtonProps[];
10066
+ /** Rendered before form sections. Useful in forms that only need the main form title, or for form fields that shape the context of a form beforehand. */
10067
+ initialFields?: ReactNode;
10066
10068
  sections: FormSectionProps[];
10067
10069
  };
10068
10070
  /** Centered (720px) width/column shell for a form built out of `FormSection`s — e.g. as a `WorkflowLayoutStep`'s `content`. */
package/dist/index.d.ts CHANGED
@@ -10063,6 +10063,8 @@ type FormSectionLayoutProps = {
10063
10063
  description?: ReactNode;
10064
10064
  /** Rendered top-right of the title row, e.g. a "Save draft" button. */
10065
10065
  actions?: ButtonProps[];
10066
+ /** Rendered before form sections. Useful in forms that only need the main form title, or for form fields that shape the context of a form beforehand. */
10067
+ initialFields?: ReactNode;
10066
10068
  sections: FormSectionProps[];
10067
10069
  };
10068
10070
  /** Centered (720px) width/column shell for a form built out of `FormSection`s — e.g. as a `WorkflowLayoutStep`'s `content`. */
package/dist/index.js CHANGED
@@ -11762,7 +11762,6 @@ function TreeOption(props) {
11762
11762
  const {
11763
11763
  item,
11764
11764
  state,
11765
- allowCollapsing = true,
11766
11765
  disabledReason
11767
11766
  } = props;
11768
11767
  const leveledOption = item.value;
@@ -11791,7 +11790,7 @@ function TreeOption(props) {
11791
11790
  shouldFocusOnHover: false
11792
11791
  }, state, ref);
11793
11792
  const isGroup = groupKeys.includes(item.key);
11794
- const canCollapse = allowCollapsing && !!option.children?.length;
11793
+ const canCollapse = !!option.children?.length;
11795
11794
  function toggleCollapsed() {
11796
11795
  if (!canCollapse) return;
11797
11796
  setCollapsedKeys((prevKeys) => collapsedKeys.includes(item.key) ? prevKeys.filter((k) => k !== item.key) : [...prevKeys, item.key]);
@@ -11849,7 +11848,7 @@ function TreeOption(props) {
11849
11848
  ...isFocused && !isGroup ? listItemStyles.focus : {},
11850
11849
  ...isDisabled && !isGroup ? listItemStyles.disabled : {}
11851
11850
  }), children: [
11852
- allowCollapsing && /* @__PURE__ */ jsx52("span", { className: "w_18px fs0 df aic", children: canCollapse && /* @__PURE__ */ jsx52("button", { onClick: (e) => {
11851
+ /* @__PURE__ */ jsx52("span", { className: "w_18px fs0 df aic", children: canCollapse && /* @__PURE__ */ jsx52("button", { onClick: (e) => {
11853
11852
  e.preventDefault();
11854
11853
  e.stopPropagation();
11855
11854
  toggleCollapsed();
@@ -11908,16 +11907,7 @@ function isLeveledNode(node) {
11908
11907
  // src/inputs/internal/VirtualizedOptions.tsx
11909
11908
  import { jsx as jsx53 } from "react/jsx-runtime";
11910
11909
  function VirtualizedOptions(props) {
11911
- const {
11912
- state,
11913
- items,
11914
- onListHeightChange,
11915
- scrollOnFocus,
11916
- loading,
11917
- disabledOptionsWithReasons,
11918
- isTree,
11919
- allowCollapsing
11920
- } = props;
11910
+ const { state, items, onListHeightChange, scrollOnFocus, loading, disabledOptionsWithReasons, isTree } = props;
11921
11911
  const virtuosoRef = useRef21(null);
11922
11912
  const focusedKey = state.selectionManager.focusedKey;
11923
11913
  const focusedItem = focusedKey != null ? state.collection.getItem(focusedKey) : null;
@@ -11958,7 +11948,6 @@ function VirtualizedOptions(props) {
11958
11948
  {
11959
11949
  item,
11960
11950
  state,
11961
- allowCollapsing,
11962
11951
  disabledReason: disabledOptionsWithReasons[item.key]
11963
11952
  },
11964
11953
  item.key
@@ -12057,8 +12046,7 @@ function ListBox(props) {
12057
12046
  horizontalLayout = false,
12058
12047
  loading,
12059
12048
  disabledOptionsWithReasons = {},
12060
- isTree,
12061
- allowCollapsing
12049
+ isTree
12062
12050
  } = props;
12063
12051
  const {
12064
12052
  listBoxProps
@@ -12136,8 +12124,7 @@ function ListBox(props) {
12136
12124
  scrollOnFocus: props.shouldUseVirtualFocus,
12137
12125
  loading,
12138
12126
  disabledOptionsWithReasons,
12139
- isTree,
12140
- allowCollapsing
12127
+ isTree
12141
12128
  }
12142
12129
  ) })
12143
12130
  ] });
@@ -12230,7 +12217,8 @@ function TreeSelectFieldBase(props) {
12230
12217
  sensitivity: "base"
12231
12218
  });
12232
12219
  const {
12233
- collapsedKeys
12220
+ collapsedKeys,
12221
+ setCollapsedKeys
12234
12222
  } = useTreeSelectFieldProvider();
12235
12223
  const groupKeys = useMemo16(() => _groupOptions?.map((option) => valueToKey(option)) ?? [], [_groupOptions]);
12236
12224
  const groupKeySet = useMemo16(() => new Set(groupKeys), [groupKeys]);
@@ -12272,8 +12260,7 @@ function TreeSelectFieldBase(props) {
12272
12260
  selectedChipOptions: selectedChipState.options,
12273
12261
  allOptions: initialOptions,
12274
12262
  selectedOptionsLabels: selectedChipState.labels,
12275
- optionsLoading: false,
12276
- allowCollapsing: true
12263
+ optionsLoading: false
12277
12264
  };
12278
12265
  }, [initialOptions, values, chipDisplay, getOptionLabel, isReadOnly, nothingSelectedText, getOptionValue, groupKeySet]);
12279
12266
  const [fieldState, setFieldState] = useState18(() => initTreeFieldState());
@@ -12295,14 +12282,26 @@ function TreeSelectFieldBase(props) {
12295
12282
  setFieldState(initTreeFieldState());
12296
12283
  }
12297
12284
  }, [getOptionValue, initTreeFieldState, values]);
12285
+ const preSearchCollapsedKeys = useRef23(void 0);
12286
+ useEffect13(() => {
12287
+ if (fieldState.searchValue) {
12288
+ if (preSearchCollapsedKeys.current === void 0) {
12289
+ preSearchCollapsedKeys.current = collapsedKeys;
12290
+ if (collapsedKeys.length > 0) setCollapsedKeys([]);
12291
+ }
12292
+ } else if (preSearchCollapsedKeys.current !== void 0) {
12293
+ const keys = preSearchCollapsedKeys.current;
12294
+ preSearchCollapsedKeys.current = void 0;
12295
+ setCollapsedKeys(keys);
12296
+ }
12297
+ }, [fieldState.searchValue, collapsedKeys, setCollapsedKeys]);
12298
12298
  const filteredOptions = useMemo16(() => getFilteredOptions(fieldState.allOptions, fieldState.searchValue, collapsedKeys, contains, getOptionLabel, getOptionValue), [fieldState.allOptions, fieldState.searchValue, collapsedKeys, contains, getOptionLabel, getOptionValue]);
12299
12299
  const onInputChange = useCallback9((inputValue) => {
12300
12300
  setFieldState((prevState) => {
12301
12301
  return {
12302
12302
  ...prevState,
12303
12303
  inputValue,
12304
- searchValue: inputValue.length === 0 ? void 0 : inputValue,
12305
- allowCollapsing: inputValue.length === 0
12304
+ searchValue: inputValue.length === 0 ? void 0 : inputValue
12306
12305
  };
12307
12306
  });
12308
12307
  }, []);
@@ -12330,8 +12329,7 @@ function TreeSelectFieldBase(props) {
12330
12329
  setFieldState((prevState) => ({
12331
12330
  ...prevState,
12332
12331
  inputValue: "",
12333
- searchValue: void 0,
12334
- allowCollapsing: true
12332
+ searchValue: void 0
12335
12333
  }));
12336
12334
  }
12337
12335
  }
@@ -12366,9 +12364,8 @@ function TreeSelectFieldBase(props) {
12366
12364
  if (newKeys.size === 0) {
12367
12365
  setFieldState((prevState) => ({
12368
12366
  ...prevState,
12369
- inputValue: nothingSelectedText,
12370
- searchValue: void 0,
12371
- allowCollapsing: true,
12367
+ // Unselecting the last option shouldn't drop the search either
12368
+ inputValue: prevState.searchValue ?? nothingSelectedText,
12372
12369
  selectedKeys: [],
12373
12370
  selectedOptions: [],
12374
12371
  selectedChipOptions: [],
@@ -12442,9 +12439,9 @@ function TreeSelectFieldBase(props) {
12442
12439
  const selectedChipState = getSelectedChipState(fieldState.allOptions, selectedOptions, selectedKeys, chipDisplay, getOptionLabel, getOptionValue);
12443
12440
  setFieldState((prevState) => ({
12444
12441
  ...prevState,
12445
- // Since we reset the list of options upon selection changes, then set the `inputValue` to empty string to reflect that.
12446
- inputValue: "",
12447
- searchValue: void 0,
12442
+ // Keep any search in place, so picking several options out of the same filtered list doesn't
12443
+ // make the user retype it; when they're not searching the input stays empty as before.
12444
+ inputValue: prevState.searchValue ?? "",
12448
12445
  selectedKeys: [...selectedKeys],
12449
12446
  selectedOptions,
12450
12447
  selectedChipOptions: selectedChipState.options,
@@ -12476,8 +12473,7 @@ function TreeSelectFieldBase(props) {
12476
12473
  setFieldState((prevState) => ({
12477
12474
  ...prevState,
12478
12475
  inputValue: selectedOptions.length === 1 ? getOptionLabel(selectedOptions[0]) : selectedOptions.length === 0 ? nothingSelectedText : "",
12479
- searchValue: void 0,
12480
- allowCollapsing: true
12476
+ searchValue: void 0
12481
12477
  }));
12482
12478
  }
12483
12479
  }
@@ -12547,17 +12543,16 @@ function TreeSelectFieldBase(props) {
12547
12543
  onClose: () => state.toggle(),
12548
12544
  isOpen: state.isOpen,
12549
12545
  minWidth: 320,
12550
- children: /* @__PURE__ */ jsx57(ListBox, { ...listBoxProps, positionProps, state, listBoxRef, selectedOptions: fieldState.selectedChipOptions, getOptionLabel, getOptionValue: (o) => valueToKey(getOptionValue(o)), horizontalLayout: labelStyle === "left", loading: fieldState.optionsLoading, allowCollapsing: fieldState.allowCollapsing, disabledOptionsWithReasons, isTree: true })
12546
+ children: /* @__PURE__ */ jsx57(ListBox, { ...listBoxProps, positionProps, state, listBoxRef, selectedOptions: fieldState.selectedChipOptions, getOptionLabel, getOptionValue: (o) => valueToKey(getOptionValue(o)), horizontalLayout: labelStyle === "left", loading: fieldState.optionsLoading, disabledOptionsWithReasons, isTree: true })
12551
12547
  }
12552
12548
  )
12553
12549
  ] });
12554
12550
  }
12555
- function levelOptions(o, level, filtering, collapsedKeys, getOptionValue) {
12556
- const actualLevel = filtering ? 0 : level;
12551
+ function levelOptions(o, level, collapsedKeys, getOptionValue) {
12557
12552
  return [
12558
- [o, actualLevel],
12559
- // Flat map the children if the parent is not collapsed or if we are filtering (for the search results)
12560
- ...o.children?.length && (!collapsedKeys.includes(valueToKey(getOptionValue(o))) || filtering) ? o.children.flatMap((oc) => levelOptions(oc, actualLevel + 1, filtering, collapsedKeys, getOptionValue)) : []
12553
+ [o, level],
12554
+ // Flat map the children if the parent is not collapsed
12555
+ ...o.children?.length && !collapsedKeys.includes(valueToKey(getOptionValue(o))) ? o.children.flatMap((oc) => levelOptions(oc, level + 1, collapsedKeys, getOptionValue)) : []
12561
12556
  ];
12562
12557
  }
12563
12558
  function getTopLevelSelections(o, selectedKeys, getOptionValue) {
@@ -12566,7 +12561,16 @@ function getTopLevelSelections(o, selectedKeys, getOptionValue) {
12566
12561
  return [];
12567
12562
  }
12568
12563
  function getFilteredOptions(allOptions, searchValue, collapsedKeys, contains, getOptionLabel, getOptionValue) {
12569
- return allOptions.flatMap((option) => levelOptions(option, 0, !!searchValue, collapsedKeys, getOptionValue).filter(([nestedOption]) => searchValue ? contains(getOptionLabel(nestedOption), searchValue) : true));
12564
+ if (!searchValue) return allOptions.flatMap((option) => levelOptions(option, 0, collapsedKeys, getOptionValue));
12565
+ const search = searchValue;
12566
+ return allOptions.flatMap((option) => matchedOptions(option, 0, false));
12567
+ function matchedOptions(o, level, hasMatchedParent) {
12568
+ const isDirectlyMatched = contains(getOptionLabel(o), search);
12569
+ const children = o.children?.flatMap((oc) => matchedOptions(oc, level + 1, hasMatchedParent || isDirectlyMatched));
12570
+ if (!isDirectlyMatched && !hasMatchedParent && !children?.length) return [];
12571
+ const isCollapsed = collapsedKeys.includes(valueToKey(getOptionValue(o)));
12572
+ return isCollapsed ? [[o, level]] : [[o, level], ...children ?? []];
12573
+ }
12570
12574
  }
12571
12575
  function isOptionFullySelected(option, selectedKeys, disabledKeys, groupKeys, getOptionValue) {
12572
12576
  const key = valueToKey(getOptionValue(option));
@@ -25315,6 +25319,7 @@ function FormSectionLayout(props) {
25315
25319
  title,
25316
25320
  description,
25317
25321
  actions,
25322
+ initialFields,
25318
25323
  sections
25319
25324
  } = props;
25320
25325
  const tid = useTestIds(props, "formSectionLayout");
@@ -25333,6 +25338,7 @@ function FormSectionLayout(props) {
25333
25338
  ] }),
25334
25339
  actions && /* @__PURE__ */ jsx207("div", { className: "df gap1 fs0", ...tid.actions, children: actions.map((action) => /* @__PURE__ */ jsx207(Button, { ...action }, `${action.label}`)) })
25335
25340
  ] }),
25341
+ initialFields,
25336
25342
  /* @__PURE__ */ jsx207("div", { className: "df fdc gap6", children: sections.map((section, i) => /* @__PURE__ */ jsx207(FormSection, { ...section }, defaultTestId(section.title) || i)) })
25337
25343
  ] });
25338
25344
  }