@homebound/beam 3.2.0-alpha.3 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -11295,6 +11295,7 @@ function VirtualizedOptions(props) {
11295
11295
  // eslint-disable-next-line react-hooks/exhaustive-deps
11296
11296
  [focusedItem]
11297
11297
  );
11298
+ const key = process.env.NODE_ENV === "test" ? items.length : "virtuoso";
11298
11299
  return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
11299
11300
  import_react_virtuoso.Virtuoso,
11300
11301
  {
@@ -11302,11 +11303,6 @@ function VirtualizedOptions(props) {
11302
11303
  totalListHeightChanged: onListHeightChange,
11303
11304
  totalCount: items.length,
11304
11305
  ...process.env.NODE_ENV === "test" ? {
11305
- // In tests, we render all rows so assertions can see expands/async-loaded items. However,
11306
- // the `initialItemCount` (next prop) is only applied on amount, so we set `key={items.length}`
11307
- // to force a remount when our list changes -- and we only want/need this in tests, b/c otherwise
11308
- // in production a Virtuoso remount causes visible flashing.
11309
- key: items.length,
11310
11306
  // We don't really need to set this, but it's handy for tests, which would otherwise render
11311
11307
  // just 1 row. A better way to do this would be to jest.mock out Virtuoso with an impl that
11312
11308
  // just rendered everything, but doing this for now.
@@ -11349,7 +11345,8 @@ function VirtualizedOptions(props) {
11349
11345
  components: !loading ? {} : {
11350
11346
  Footer: typeof loading === "function" ? loading : () => /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(LoadingDots, { contrast })
11351
11347
  }
11352
- }
11348
+ },
11349
+ key
11353
11350
  );
11354
11351
  }
11355
11352
 
@@ -11476,7 +11473,7 @@ function ListBox(props) {
11476
11473
  boxShadow: "bshBasic h_bshHover"
11477
11474
  } : {}
11478
11475
  }), ref: listBoxRef, ...listBoxProps, children: [
11479
- isMultiSelect && !isTree && state.selectionManager.selectedKeys.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("ul", { className: "p_0 m_0 lis_none pt2 pl2 pb1 pr1 df bbs_solid bbw_1px bcGray200 flexWrap_wrap maxh_50 oa", ref: selectedList, children: selectedOptions.map((o) => /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ListBoxToggleChip, { state, option: o, getOptionValue, getOptionLabel, disabled: state.disabledKeys.has(getOptionValue(o)) }, getOptionValue(o))) }),
11476
+ isMultiSelect && selectedOptions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("ul", { className: "p_0 m_0 lis_none pt2 pl2 pb1 pr1 df bbs_solid bbw_1px bcGray200 flexWrap_wrap maxh_50 oa", ref: selectedList, children: selectedOptions.map((o) => /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ListBoxToggleChip, { state, option: o, getOptionValue, getOptionLabel, disabled: state.disabledKeys.has(getOptionValue(o)) }, getOptionValue(o))) }),
11480
11477
  /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("ul", { className: "p_0 m_0 lis_none fg1", children: hasSections ? [...state.collection].map((section) => /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
11481
11478
  ListBoxSection,
11482
11479
  {
@@ -11627,14 +11624,15 @@ function TreeSelectFieldBase(props) {
11627
11624
  if (!maybeOption) return [];
11628
11625
  return [maybeOption.option];
11629
11626
  });
11630
- const selectedOptionsLabels = chipDisplay === "root" ? initialOptions.flatMap((o) => getTopLevelSelections(o, selectedKeys, getOptionValue)).map(getOptionLabel) : chipDisplay === "leaf" ? selectedOptions.filter((o) => !o.children || o.children.length === 0).map(getOptionLabel) : selectedOptions.map(getOptionLabel);
11627
+ const selectedChipState = getSelectedChipState(initialOptions, selectedOptions, selectedKeys, chipDisplay, getOptionLabel, getOptionValue);
11631
11628
  return {
11632
11629
  selectedKeys: [...selectedKeys],
11633
11630
  searchValue: void 0,
11634
- inputValue: selectedOptions.length === 1 ? getOptionLabel([...selectedOptions][0]) : isReadOnly && selectedOptions.length > 0 ? selectedOptionsLabels.join(", ") : selectedOptions.length === 0 ? nothingSelectedText : "",
11631
+ inputValue: selectedOptions.length === 1 ? getOptionLabel([...selectedOptions][0]) : isReadOnly && selectedOptions.length > 0 ? selectedChipState.labels.join(", ") : selectedOptions.length === 0 ? nothingSelectedText : "",
11635
11632
  selectedOptions,
11633
+ selectedChipOptions: selectedChipState.options,
11636
11634
  allOptions: initialOptions,
11637
- selectedOptionsLabels,
11635
+ selectedOptionsLabels: selectedChipState.labels,
11638
11636
  optionsLoading: false,
11639
11637
  allowCollapsing: true
11640
11638
  };
@@ -11733,7 +11731,9 @@ function TreeSelectFieldBase(props) {
11733
11731
  searchValue: void 0,
11734
11732
  allowCollapsing: true,
11735
11733
  selectedKeys: [],
11736
- selectedOptions: []
11734
+ selectedOptions: [],
11735
+ selectedChipOptions: [],
11736
+ selectedOptionsLabels: []
11737
11737
  }));
11738
11738
  onSelect({
11739
11739
  all: {
@@ -11800,6 +11800,7 @@ function TreeSelectFieldBase(props) {
11800
11800
  const rootValues = rootOptions.map(getOptionValue);
11801
11801
  const leafOptions = selectedOptions.filter((o) => !o.children || o.children.length === 0);
11802
11802
  const leafValues = leafOptions.map(getOptionValue);
11803
+ const selectedChipState = getSelectedChipState(fieldState.allOptions, selectedOptions, selectedKeys, chipDisplay, getOptionLabel, getOptionValue);
11803
11804
  setFieldState((prevState) => ({
11804
11805
  ...prevState,
11805
11806
  // Since we reset the list of options upon selection changes, then set the `inputValue` to empty string to reflect that.
@@ -11807,7 +11808,8 @@ function TreeSelectFieldBase(props) {
11807
11808
  searchValue: void 0,
11808
11809
  selectedKeys: [...selectedKeys],
11809
11810
  selectedOptions,
11810
- selectedOptionsLabels: chipDisplay === "root" ? rootOptions.map(getOptionLabel) : chipDisplay === "leaf" ? leafOptions.map(getOptionLabel) : selectedOptions.map(getOptionLabel)
11811
+ selectedChipOptions: selectedChipState.options,
11812
+ selectedOptionsLabels: selectedChipState.labels
11811
11813
  }));
11812
11814
  onSelect({
11813
11815
  all: {
@@ -11906,7 +11908,7 @@ function TreeSelectFieldBase(props) {
11906
11908
  onClose: () => state.toggle(),
11907
11909
  isOpen: state.isOpen,
11908
11910
  minWidth: 320,
11909
- children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(ListBox, { ...listBoxProps, positionProps, state, listBoxRef, selectedOptions: fieldState.selectedOptions, getOptionLabel, getOptionValue: (o) => valueToKey(getOptionValue(o)), contrast, horizontalLayout: labelStyle === "left", loading: fieldState.optionsLoading, allowCollapsing: fieldState.allowCollapsing, isTree: true })
11911
+ children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(ListBox, { ...listBoxProps, positionProps, state, listBoxRef, selectedOptions: fieldState.selectedChipOptions, getOptionLabel, getOptionValue: (o) => valueToKey(getOptionValue(o)), contrast, horizontalLayout: labelStyle === "left", loading: fieldState.optionsLoading, allowCollapsing: fieldState.allowCollapsing, isTree: true })
11910
11912
  }
11911
11913
  )
11912
11914
  ] });
@@ -11936,6 +11938,25 @@ function isOptionFullySelected(option, selectedKeys, disabledKeys, groupKeys, ge
11936
11938
  if (!option.children || option.children.length === 0) return false;
11937
11939
  return option.children.every((child) => isOptionFullySelected(child, selectedKeys, disabledKeys, groupKeys, getOptionValue));
11938
11940
  }
11941
+ function getSelectedChipState(allOptions, selectedOptions, selectedKeys, chipDisplay, getOptionLabel, getOptionValue) {
11942
+ const options = chipDisplay === "root" ? dedupeOptionsByValue(allOptions.flatMap((option) => getTopLevelSelections(option, selectedKeys, getOptionValue)), getOptionValue) : chipDisplay === "leaf" ? selectedOptions.filter(isLeafOption) : selectedOptions;
11943
+ return {
11944
+ options,
11945
+ labels: options.map(getOptionLabel)
11946
+ };
11947
+ }
11948
+ function dedupeOptionsByValue(options, getOptionValue) {
11949
+ const seen = /* @__PURE__ */ new Set();
11950
+ return options.filter(function filterOption(option) {
11951
+ const key = valueToKey(getOptionValue(option));
11952
+ if (seen.has(key)) return false;
11953
+ seen.add(key);
11954
+ return true;
11955
+ });
11956
+ }
11957
+ function isLeafOption(option) {
11958
+ return !option.children || option.children.length === 0;
11959
+ }
11939
11960
 
11940
11961
  // src/inputs/internal/ComboBoxInput.tsx
11941
11962
  var import_jsx_runtime55 = require("react/jsx-runtime");
@@ -11974,7 +11995,6 @@ function ComboBoxInput(props) {
11974
11995
  } = useTreeSelectFieldProvider();
11975
11996
  const [isFocused, setIsFocused] = (0, import_react45.useState)(false);
11976
11997
  const isMultiSelect = state.selectionManager.selectionMode === "multiple";
11977
- const showNumSelection = isMultiSelect && state.selectionManager.selectedKeys.size > 1;
11978
11998
  const showChipSelection = isMultiSelect && state.selectionManager.selectedKeys.size > 0;
11979
11999
  const showFieldDecoration = (!isMultiSelect || isMultiSelect && !isFocused) && fieldDecoration && selectedOptions.length === 1;
11980
12000
  const multilineProps = allowWrap ? {
@@ -11982,6 +12002,8 @@ function ComboBoxInput(props) {
11982
12002
  multiline: true
11983
12003
  } : {};
11984
12004
  const chipLabels = isTree ? selectedOptionsLabels || [] : selectedOptions.map((o) => getOptionLabel(o));
12005
+ const selectedChipCount = chipLabels.length;
12006
+ const showNumSelection = isMultiSelect && selectedChipCount > 1;
11985
12007
  useGrowingTextField({
11986
12008
  // This says: When using a multiselect, then only enable the growing textfield when we are focused on it.
11987
12009
  // Because otherwise, we're not displaying the input element that dictates the height (we're displaying <Chips/>). This would cause incorrect calculations
@@ -11992,7 +12014,7 @@ function ComboBoxInput(props) {
11992
12014
  });
11993
12015
  return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(TextFieldBase, { ...otherProps, ...multilineProps, unfocusedPlaceholder: showChipSelection && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Chips, { compact: otherProps.compact, values: chipLabels, wrap: allowWrap }), inputRef, inputWrapRef, errorMsg, contrast, xss: otherProps.labelStyle !== "inline" && !inputProps.readOnly ? {
11994
12016
  fontWeight: "fw5"
11995
- } : void 0, startAdornment: showNumSelection && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Tooltip, { title: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(SelectedOptionBullets, { labels: chipLabels }), children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(CountBadge, { count: isTree ? selectedOptionsLabels?.length ?? 0 : state.selectionManager.selectedKeys.size, "data-testid": "selectedOptionsCount" }) }) || showFieldDecoration && fieldDecoration(selectedOptions[0]), endAdornment: !inputProps.readOnly && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("button", { ...buttonProps, disabled: inputProps.disabled, ref: buttonRef, ...(0, import_runtime40.trussProps)({
12017
+ } : void 0, startAdornment: showNumSelection && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Tooltip, { title: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(SelectedOptionBullets, { labels: chipLabels }), children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(CountBadge, { count: selectedChipCount, "data-testid": "selectedOptionsCount" }) }) || showFieldDecoration && fieldDecoration(selectedOptions[0]), endAdornment: !inputProps.readOnly && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("button", { ...buttonProps, disabled: inputProps.disabled, ref: buttonRef, ...(0, import_runtime40.trussProps)({
11996
12018
  ...{
11997
12019
  borderRadius: "br4",
11998
12020
  outline: "outline0",