@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.js CHANGED
@@ -10889,6 +10889,7 @@ function VirtualizedOptions(props) {
10889
10889
  // eslint-disable-next-line react-hooks/exhaustive-deps
10890
10890
  [focusedItem]
10891
10891
  );
10892
+ const key = process.env.NODE_ENV === "test" ? items.length : "virtuoso";
10892
10893
  return /* @__PURE__ */ jsx50(
10893
10894
  Virtuoso,
10894
10895
  {
@@ -10896,11 +10897,6 @@ function VirtualizedOptions(props) {
10896
10897
  totalListHeightChanged: onListHeightChange,
10897
10898
  totalCount: items.length,
10898
10899
  ...process.env.NODE_ENV === "test" ? {
10899
- // In tests, we render all rows so assertions can see expands/async-loaded items. However,
10900
- // the `initialItemCount` (next prop) is only applied on amount, so we set `key={items.length}`
10901
- // to force a remount when our list changes -- and we only want/need this in tests, b/c otherwise
10902
- // in production a Virtuoso remount causes visible flashing.
10903
- key: items.length,
10904
10900
  // We don't really need to set this, but it's handy for tests, which would otherwise render
10905
10901
  // just 1 row. A better way to do this would be to jest.mock out Virtuoso with an impl that
10906
10902
  // just rendered everything, but doing this for now.
@@ -10943,7 +10939,8 @@ function VirtualizedOptions(props) {
10943
10939
  components: !loading ? {} : {
10944
10940
  Footer: typeof loading === "function" ? loading : () => /* @__PURE__ */ jsx50(LoadingDots, { contrast })
10945
10941
  }
10946
- }
10942
+ },
10943
+ key
10947
10944
  );
10948
10945
  }
10949
10946
 
@@ -11070,7 +11067,7 @@ function ListBox(props) {
11070
11067
  boxShadow: "bshBasic h_bshHover"
11071
11068
  } : {}
11072
11069
  }), ref: listBoxRef, ...listBoxProps, children: [
11073
- isMultiSelect && !isTree && state.selectionManager.selectedKeys.size > 0 && /* @__PURE__ */ jsx53("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__ */ jsx53(ListBoxToggleChip, { state, option: o, getOptionValue, getOptionLabel, disabled: state.disabledKeys.has(getOptionValue(o)) }, getOptionValue(o))) }),
11070
+ isMultiSelect && selectedOptions.length > 0 && /* @__PURE__ */ jsx53("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__ */ jsx53(ListBoxToggleChip, { state, option: o, getOptionValue, getOptionLabel, disabled: state.disabledKeys.has(getOptionValue(o)) }, getOptionValue(o))) }),
11074
11071
  /* @__PURE__ */ jsx53("ul", { className: "p_0 m_0 lis_none fg1", children: hasSections ? [...state.collection].map((section) => /* @__PURE__ */ jsx53(
11075
11072
  ListBoxSection,
11076
11073
  {
@@ -11221,14 +11218,15 @@ function TreeSelectFieldBase(props) {
11221
11218
  if (!maybeOption) return [];
11222
11219
  return [maybeOption.option];
11223
11220
  });
11224
- 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);
11221
+ const selectedChipState = getSelectedChipState(initialOptions, selectedOptions, selectedKeys, chipDisplay, getOptionLabel, getOptionValue);
11225
11222
  return {
11226
11223
  selectedKeys: [...selectedKeys],
11227
11224
  searchValue: void 0,
11228
- inputValue: selectedOptions.length === 1 ? getOptionLabel([...selectedOptions][0]) : isReadOnly && selectedOptions.length > 0 ? selectedOptionsLabels.join(", ") : selectedOptions.length === 0 ? nothingSelectedText : "",
11225
+ inputValue: selectedOptions.length === 1 ? getOptionLabel([...selectedOptions][0]) : isReadOnly && selectedOptions.length > 0 ? selectedChipState.labels.join(", ") : selectedOptions.length === 0 ? nothingSelectedText : "",
11229
11226
  selectedOptions,
11227
+ selectedChipOptions: selectedChipState.options,
11230
11228
  allOptions: initialOptions,
11231
- selectedOptionsLabels,
11229
+ selectedOptionsLabels: selectedChipState.labels,
11232
11230
  optionsLoading: false,
11233
11231
  allowCollapsing: true
11234
11232
  };
@@ -11327,7 +11325,9 @@ function TreeSelectFieldBase(props) {
11327
11325
  searchValue: void 0,
11328
11326
  allowCollapsing: true,
11329
11327
  selectedKeys: [],
11330
- selectedOptions: []
11328
+ selectedOptions: [],
11329
+ selectedChipOptions: [],
11330
+ selectedOptionsLabels: []
11331
11331
  }));
11332
11332
  onSelect({
11333
11333
  all: {
@@ -11394,6 +11394,7 @@ function TreeSelectFieldBase(props) {
11394
11394
  const rootValues = rootOptions.map(getOptionValue);
11395
11395
  const leafOptions = selectedOptions.filter((o) => !o.children || o.children.length === 0);
11396
11396
  const leafValues = leafOptions.map(getOptionValue);
11397
+ const selectedChipState = getSelectedChipState(fieldState.allOptions, selectedOptions, selectedKeys, chipDisplay, getOptionLabel, getOptionValue);
11397
11398
  setFieldState((prevState) => ({
11398
11399
  ...prevState,
11399
11400
  // Since we reset the list of options upon selection changes, then set the `inputValue` to empty string to reflect that.
@@ -11401,7 +11402,8 @@ function TreeSelectFieldBase(props) {
11401
11402
  searchValue: void 0,
11402
11403
  selectedKeys: [...selectedKeys],
11403
11404
  selectedOptions,
11404
- selectedOptionsLabels: chipDisplay === "root" ? rootOptions.map(getOptionLabel) : chipDisplay === "leaf" ? leafOptions.map(getOptionLabel) : selectedOptions.map(getOptionLabel)
11405
+ selectedChipOptions: selectedChipState.options,
11406
+ selectedOptionsLabels: selectedChipState.labels
11405
11407
  }));
11406
11408
  onSelect({
11407
11409
  all: {
@@ -11500,7 +11502,7 @@ function TreeSelectFieldBase(props) {
11500
11502
  onClose: () => state.toggle(),
11501
11503
  isOpen: state.isOpen,
11502
11504
  minWidth: 320,
11503
- children: /* @__PURE__ */ jsx54(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 })
11505
+ children: /* @__PURE__ */ jsx54(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 })
11504
11506
  }
11505
11507
  )
11506
11508
  ] });
@@ -11530,6 +11532,25 @@ function isOptionFullySelected(option, selectedKeys, disabledKeys, groupKeys, ge
11530
11532
  if (!option.children || option.children.length === 0) return false;
11531
11533
  return option.children.every((child) => isOptionFullySelected(child, selectedKeys, disabledKeys, groupKeys, getOptionValue));
11532
11534
  }
11535
+ function getSelectedChipState(allOptions, selectedOptions, selectedKeys, chipDisplay, getOptionLabel, getOptionValue) {
11536
+ const options = chipDisplay === "root" ? dedupeOptionsByValue(allOptions.flatMap((option) => getTopLevelSelections(option, selectedKeys, getOptionValue)), getOptionValue) : chipDisplay === "leaf" ? selectedOptions.filter(isLeafOption) : selectedOptions;
11537
+ return {
11538
+ options,
11539
+ labels: options.map(getOptionLabel)
11540
+ };
11541
+ }
11542
+ function dedupeOptionsByValue(options, getOptionValue) {
11543
+ const seen = /* @__PURE__ */ new Set();
11544
+ return options.filter(function filterOption(option) {
11545
+ const key = valueToKey(getOptionValue(option));
11546
+ if (seen.has(key)) return false;
11547
+ seen.add(key);
11548
+ return true;
11549
+ });
11550
+ }
11551
+ function isLeafOption(option) {
11552
+ return !option.children || option.children.length === 0;
11553
+ }
11533
11554
 
11534
11555
  // src/inputs/internal/ComboBoxInput.tsx
11535
11556
  import { jsx as jsx55 } from "react/jsx-runtime";
@@ -11568,7 +11589,6 @@ function ComboBoxInput(props) {
11568
11589
  } = useTreeSelectFieldProvider();
11569
11590
  const [isFocused, setIsFocused] = useState18(false);
11570
11591
  const isMultiSelect = state.selectionManager.selectionMode === "multiple";
11571
- const showNumSelection = isMultiSelect && state.selectionManager.selectedKeys.size > 1;
11572
11592
  const showChipSelection = isMultiSelect && state.selectionManager.selectedKeys.size > 0;
11573
11593
  const showFieldDecoration = (!isMultiSelect || isMultiSelect && !isFocused) && fieldDecoration && selectedOptions.length === 1;
11574
11594
  const multilineProps = allowWrap ? {
@@ -11576,6 +11596,8 @@ function ComboBoxInput(props) {
11576
11596
  multiline: true
11577
11597
  } : {};
11578
11598
  const chipLabels = isTree ? selectedOptionsLabels || [] : selectedOptions.map((o) => getOptionLabel(o));
11599
+ const selectedChipCount = chipLabels.length;
11600
+ const showNumSelection = isMultiSelect && selectedChipCount > 1;
11579
11601
  useGrowingTextField({
11580
11602
  // This says: When using a multiselect, then only enable the growing textfield when we are focused on it.
11581
11603
  // Because otherwise, we're not displaying the input element that dictates the height (we're displaying <Chips/>). This would cause incorrect calculations
@@ -11586,7 +11608,7 @@ function ComboBoxInput(props) {
11586
11608
  });
11587
11609
  return /* @__PURE__ */ jsx55(TextFieldBase, { ...otherProps, ...multilineProps, unfocusedPlaceholder: showChipSelection && /* @__PURE__ */ jsx55(Chips, { compact: otherProps.compact, values: chipLabels, wrap: allowWrap }), inputRef, inputWrapRef, errorMsg, contrast, xss: otherProps.labelStyle !== "inline" && !inputProps.readOnly ? {
11588
11610
  fontWeight: "fw5"
11589
- } : void 0, startAdornment: showNumSelection && /* @__PURE__ */ jsx55(Tooltip, { title: /* @__PURE__ */ jsx55(SelectedOptionBullets, { labels: chipLabels }), children: /* @__PURE__ */ jsx55(CountBadge, { count: isTree ? selectedOptionsLabels?.length ?? 0 : state.selectionManager.selectedKeys.size, "data-testid": "selectedOptionsCount" }) }) || showFieldDecoration && fieldDecoration(selectedOptions[0]), endAdornment: !inputProps.readOnly && /* @__PURE__ */ jsx55("button", { ...buttonProps, disabled: inputProps.disabled, ref: buttonRef, ...trussProps38({
11611
+ } : void 0, startAdornment: showNumSelection && /* @__PURE__ */ jsx55(Tooltip, { title: /* @__PURE__ */ jsx55(SelectedOptionBullets, { labels: chipLabels }), children: /* @__PURE__ */ jsx55(CountBadge, { count: selectedChipCount, "data-testid": "selectedOptionsCount" }) }) || showFieldDecoration && fieldDecoration(selectedOptions[0]), endAdornment: !inputProps.readOnly && /* @__PURE__ */ jsx55("button", { ...buttonProps, disabled: inputProps.disabled, ref: buttonRef, ...trussProps38({
11590
11612
  ...{
11591
11613
  borderRadius: "br4",
11592
11614
  outline: "outline0",