@homebound/beam 2.91.2 → 2.91.6

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.
@@ -7,6 +7,11 @@ const utils_1 = require("../../utils");
7
7
  function useModal() {
8
8
  const { modalState, modalCanCloseChecks } = (0, BeamContext_1.useBeamContext)();
9
9
  const lastCanClose = (0, react_1.useRef)();
10
+ (0, react_1.useEffect)(() => {
11
+ return () => {
12
+ modalCanCloseChecks.current = modalCanCloseChecks.current.filter((c) => c !== lastCanClose.current);
13
+ };
14
+ }, [modalCanCloseChecks]);
10
15
  return (0, react_1.useMemo)(() => ({
11
16
  openModal(props) {
12
17
  // TODO Check already open?
@@ -340,7 +340,7 @@ function calcVirtualGridColumns(columns, firstLastColumnWidth) {
340
340
  else {
341
341
  throw new Error("as=virtual only supports px, percentage, or fr units");
342
342
  }
343
- }, { claimedPercentages: 0, claimedPixels: 0, totalFr: 0 });
343
+ }, { claimedPercentages: 0, claimedPixels: firstLastColumnWidth ? firstLastColumnWidth * 2 : 0, totalFr: 0 });
344
344
  // This is our "fake but for some reason it lines up better" fr calc
345
345
  function fr(myFr) {
346
346
  return `calc((100% - ${claimedPercentages}% - ${claimedPixels}px) * (${myFr} / ${totalFr}))`;
@@ -24,4 +24,10 @@ declare type PersistentItem = {
24
24
  };
25
25
  export declare function isPersistentItem<T extends PersistentItem>(opt: any): opt is PersistentItem;
26
26
  export declare function isPersistentKey(key: Key): boolean;
27
+ declare type ListBoxSection<O> = {
28
+ title: string;
29
+ options: O[];
30
+ isPersistent?: boolean;
31
+ };
32
+ export declare function isListBoxSection<O>(obj: O | ListBoxSection<O>): obj is ListBoxSection<O>;
27
33
  export {};
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isPersistentKey = exports.isPersistentItem = exports.persistentItemPrefix = exports.ChipSelectField = void 0;
3
+ exports.isListBoxSection = exports.isPersistentKey = exports.isPersistentItem = exports.persistentItemPrefix = exports.ChipSelectField = void 0;
4
4
  const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
+ const change_case_1 = require("change-case");
5
6
  const react_1 = require("react");
6
7
  const react_aria_1 = require("react-aria");
7
8
  const react_stately_1 = require("react-stately");
@@ -53,25 +54,28 @@ function ChipSelectField(props) {
53
54
  const wrapperRef = (0, react_1.useRef)(null);
54
55
  // Using `ListData` in order to dynamically update the items
55
56
  const listData = (0, react_stately_1.useListData)({
56
- initialItems: [...options, ...(onCreateNew ? [createNewOpt] : [])],
57
+ initialItems: !onCreateNew
58
+ ? options
59
+ : [
60
+ { title: "Options", options },
61
+ { title: "Actions", isPersistent: true, options: [createNewOpt] },
62
+ ],
57
63
  initialSelectedKeys: [(0, Value_1.valueToKey)(value)],
58
- getKey: (item) => (isPersistentItem(item) ? item.id : getOptionValue(item)),
64
+ getKey: (item) => (isListBoxSection(item) ? item.title : getOptionValue(item)),
59
65
  });
60
- // If the options change, blow away existing items and replace with new values.
61
- (0, react_1.useEffect)(() => {
62
- // Create a list of all existing keys to be removed.
63
- const optionKeys = listData.items.reduce(
64
- // Filter out Persistent Items
65
- (acc, o) => (isPersistentItem(o) ? acc : acc.concat(getOptionValue(o))), []);
66
- listData.remove(...optionKeys);
67
- // Using `prepend` to keep Persistent Items (if they exist) at the bottom of the list.
68
- listData.prepend(...options);
69
- }, [options]);
70
- const selectChildren = listData.items.map((o) => {
71
- const isPersistent = isPersistentItem(o);
72
- const value = isPersistent ? o.id : getOptionValue(o);
73
- const label = isPersistent ? o.name : getOptionLabel(o);
74
- return ((0, jsx_runtime_1.jsx)(react_stately_1.Item, Object.assign({ textValue: label }, { children: isPersistent ? (label) : ((0, jsx_runtime_1.jsx)("span", Object.assign({ css: { ...Css_1.Css.lineClamp1.breakAll.$, ...chipStyles }, title: label }, { children: label }), void 0)) }), value));
66
+ (0, react_1.useEffect)(() => listData.update("Options", { title: "Options", options }), [options]);
67
+ const selectChildren = listData.items.map((s) => {
68
+ if (isListBoxSection(s)) {
69
+ return ((0, jsx_runtime_1.jsx)(react_stately_1.Section, Object.assign({ title: s.title, items: s.options }, { children: (item) => {
70
+ if (isPersistentItem(item)) {
71
+ return ((0, jsx_runtime_1.jsx)(react_stately_1.Item, Object.assign({ textValue: item.name }, { children: item.name }), item.id));
72
+ }
73
+ const label = getOptionLabel(item);
74
+ return ((0, jsx_runtime_1.jsx)(react_stately_1.Item, Object.assign({ textValue: label }, { children: (0, jsx_runtime_1.jsx)("span", Object.assign({ css: { ...Css_1.Css.lineClamp1.breakAll.$, ...chipStyles }, title: label }, { children: label }), void 0) }), getOptionValue(item)));
75
+ } }), (0, change_case_1.camelCase)(s.title)));
76
+ }
77
+ const label = getOptionLabel(s);
78
+ return ((0, jsx_runtime_1.jsx)(react_stately_1.Item, Object.assign({ textValue: label }, { children: (0, jsx_runtime_1.jsx)("span", Object.assign({ css: { ...Css_1.Css.lineClamp1.breakAll.$, ...chipStyles }, title: label }, { children: label }), void 0) }), getOptionValue(s)));
75
79
  });
76
80
  const selectHookProps = {
77
81
  label,
@@ -121,6 +125,7 @@ function ChipSelectField(props) {
121
125
  isOpen: state.isOpen,
122
126
  onClose: state.close,
123
127
  placement: "bottom left",
128
+ offset: 8,
124
129
  });
125
130
  overlayProps.style = {
126
131
  ...overlayProps.style,
@@ -132,10 +137,10 @@ function ChipSelectField(props) {
132
137
  const [showInput, setShowInput] = (0, react_1.useState)(false);
133
138
  const [inputValue, setInputValue] = (0, react_1.useState)("Add new");
134
139
  const removeCreateNewField = (0, react_1.useCallback)(() => {
135
- var _a;
136
140
  setShowInput(false);
137
141
  setInputValue("Add new");
138
- (_a = buttonRef.current) === null || _a === void 0 ? void 0 : _a.focus();
142
+ // Trigger onBlur to initiate any auto-saving behavior.
143
+ (0, utils_1.maybeCall)(onBlur);
139
144
  }, [setShowInput, setInputValue]);
140
145
  const field = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [showInput && onCreateNew && ((0, jsx_runtime_1.jsx)(ChipTextField_1.ChipTextField, Object.assign({ autoFocus: true, label: "Add new", value: inputValue, onChange: setInputValue, onEnter: async () => {
141
146
  await onCreateNew(inputValue);
@@ -157,7 +162,7 @@ function ChipSelectField(props) {
157
162
  onSelect(undefined, undefined);
158
163
  (0, utils_1.maybeCall)(onBlur);
159
164
  setIsClearFocused(false);
160
- }, "aria-label": "Remove" }, tid.clearButton, { children: (0, jsx_runtime_1.jsx)(components_1.Icon, { icon: "x", inc: typeScale === "xs" ? 2 : undefined }, void 0) }), void 0))] }), void 0), state.isOpen && ((0, jsx_runtime_1.jsx)(internal_1.Popover, Object.assign({ triggerRef: buttonRef, popoverRef: popoverRef, positionProps: overlayProps, onClose: state.close, isOpen: state.isOpen, shouldCloseOnBlur: true }, { children: (0, jsx_runtime_1.jsx)(ListBox_1.ListBox, Object.assign({}, menuProps, { listBoxRef: listBoxRef, state: state, getOptionLabel: getOptionLabel, getOptionValue: getOptionValue, positionProps: overlayProps, positionOffset: 8 }), void 0) }), void 0))] }, void 0));
165
+ }, "aria-label": "Remove" }, tid.clearButton, { children: (0, jsx_runtime_1.jsx)(components_1.Icon, { icon: "x", inc: typeScale === "xs" ? 2 : undefined }, void 0) }), void 0))] }), void 0), state.isOpen && ((0, jsx_runtime_1.jsx)(internal_1.Popover, Object.assign({ triggerRef: buttonRef, popoverRef: popoverRef, positionProps: overlayProps, onClose: state.close, isOpen: state.isOpen, shouldCloseOnBlur: true }, { children: (0, jsx_runtime_1.jsx)(ListBox_1.ListBox, Object.assign({}, menuProps, { listBoxRef: listBoxRef, state: state, getOptionLabel: getOptionLabel, getOptionValue: getOptionValue, positionProps: overlayProps }), void 0) }), void 0))] }, void 0));
161
166
  const tooltipText = selectHookProps.isDisabled && typeof disabled !== "boolean" ? disabled : undefined;
162
167
  return tooltipText ? ((0, jsx_runtime_1.jsx)(components_1.Tooltip, Object.assign({ title: tooltipText, placement: "top" }, { children: field }), void 0)) : (field);
163
168
  }
@@ -175,3 +180,7 @@ function isPersistentKey(key) {
175
180
  return typeof key === "string" && key.startsWith(exports.persistentItemPrefix);
176
181
  }
177
182
  exports.isPersistentKey = isPersistentKey;
183
+ function isListBoxSection(obj) {
184
+ return typeof obj === "object" && "options" in obj;
185
+ }
186
+ exports.isListBoxSection = isListBoxSection;
@@ -8,7 +8,6 @@ interface ListBoxProps<O, V extends Key> {
8
8
  getOptionValue: (opt: O) => V;
9
9
  contrast?: boolean;
10
10
  positionProps: React.HTMLAttributes<Element>;
11
- positionOffset?: number;
12
11
  }
13
12
  /** A ListBox is an internal component used by SelectField and MultiSelectField to display the list of options */
14
13
  export declare function ListBox<O, V extends Key>(props: ListBoxProps<O, V>): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -4,45 +4,40 @@ exports.ListBox = void 0;
4
4
  const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const react_aria_1 = require("react-aria");
7
- const react_virtuoso_1 = require("react-virtuoso");
8
7
  const ToggleChip_1 = require("../../components/ToggleChip");
9
8
  const Css_1 = require("../../Css");
10
- const Option_1 = require("./Option");
9
+ const constants_1 = require("./constants");
10
+ const ListBoxSection_1 = require("./ListBoxSection");
11
+ const VirtualizedOptions_1 = require("./VirtualizedOptions");
11
12
  /** A ListBox is an internal component used by SelectField and MultiSelectField to display the list of options */
12
13
  function ListBox(props) {
13
14
  var _a;
14
- const { state, listBoxRef, selectedOptions = [], getOptionLabel, getOptionValue, contrast = false, positionProps, positionOffset = 4, } = props;
15
- const { listBoxProps } = (0, react_aria_1.useListBox)({ disallowEmptySelection: true, shouldFocusOnHover: true, ...props }, state, listBoxRef);
15
+ const { state, listBoxRef, selectedOptions = [], getOptionLabel, getOptionValue, contrast = false, positionProps, } = props;
16
+ const { listBoxProps } = (0, react_aria_1.useListBox)({ disallowEmptySelection: true, ...props }, state, listBoxRef);
16
17
  const positionMaxHeight = (_a = positionProps.style) === null || _a === void 0 ? void 0 : _a.maxHeight;
17
- // The maxListHeight will be based on the value defined by the positionProps returned from `useOverlayPosition` (which will always be a defined as a `number` based on React-Aria's `calculatePosition`).
18
- // If `maxHeight` is set use that, otherwise use `273` as a default (`42px` is the min-height of each option, so this allows
19
- // 6.5 options in view at a time (doing `.5` so the user can easily tell if there are more).
20
- const maxListHeight = positionMaxHeight && typeof positionMaxHeight === "number" ? positionMaxHeight : 273;
21
- const [listHeight, setListHeight] = (0, react_1.useState)(maxListHeight);
18
+ // The popoverMaxHeight will be based on the value defined by the positionProps returned from `useOverlayPosition` (which will always be a defined as a `number` based on React-Aria's `calculatePosition`).
19
+ // If `maxHeight` is set use that, otherwise use `maxPopoverHeight` as a default, per UX guidelines.
20
+ // (`positionMaxHeight` should always be set and defined as a number, but we need to do these type checks to make TS happy)
21
+ const popoverMaxHeight = positionMaxHeight && typeof positionMaxHeight === "number"
22
+ ? Math.min(positionMaxHeight, maxPopoverHeight)
23
+ : maxPopoverHeight;
24
+ const [popoverHeight, setPopoverHeight] = (0, react_1.useState)(popoverMaxHeight);
22
25
  const isMultiSelect = state.selectionManager.selectionMode === "multiple";
23
- const virtuosoRef = (0, react_1.useRef)(null);
24
- const focusedItem = state.collection.getItem(state.selectionManager.focusedKey);
25
- // Handle scrolling to the item in focus when navigating options via Keyboard
26
- (0, react_1.useEffect)(() => {
27
- if (virtuosoRef.current && (focusedItem === null || focusedItem === void 0 ? void 0 : focusedItem.index)) {
28
- virtuosoRef.current.scrollToIndex({ index: focusedItem.index, align: "center" });
29
- }
30
- }, [focusedItem]);
26
+ const firstItem = state.collection.at(0);
27
+ const hasSections = firstItem && firstItem.type === "section";
28
+ const onListHeightChange = (height) => {
29
+ // Using Math.min to choose between the smaller height, either the total height of the List (`height` arg), or the maximum height defined by the space allotted on screen or our hard coded max.
30
+ // If there are ListBoxSections, then we assume it is the persistent section with a single item and account for that height.
31
+ setPopoverHeight(Math.min(popoverMaxHeight, hasSections ? height + constants_1.persistentItemHeight + constants_1.sectionSeparatorHeight : height));
32
+ };
31
33
  return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: {
32
- ...Css_1.Css.bgWhite.br4.w100.bshBasic.myPx(positionOffset).if(contrast).bgGray700.$,
34
+ ...Css_1.Css.bgWhite.br4.w100.bshBasic.if(contrast).bgGray700.$,
33
35
  "&:hover": Css_1.Css.bshHover.$,
34
- }, ref: listBoxRef }, listBoxProps, { children: [isMultiSelect && state.selectionManager.selectedKeys.size > 0 && ((0, jsx_runtime_1.jsx)("ul", Object.assign({ css: Css_1.Css.listReset.pt2.pl2.pb1.pr1.df.bb.bGray200.add("flexWrap", "wrap").$ }, { children: selectedOptions.map((o) => ((0, jsx_runtime_1.jsx)(ListBoxChip, { state: state, option: o, getOptionValue: getOptionValue, getOptionLabel: getOptionLabel, disabled: state.disabledKeys.has(getOptionValue(o)) }, getOptionValue(o)))) }), void 0)), (0, jsx_runtime_1.jsx)("ul", Object.assign({ css: Css_1.Css.listReset.hPx(Math.min(maxListHeight, listHeight)).$ }, { children: (0, jsx_runtime_1.jsx)(react_virtuoso_1.Virtuoso, { ref: virtuosoRef, totalListHeightChanged: setListHeight, totalCount: state.collection.size,
35
- // We don't really need to set this, but it's handy for tests, which would
36
- // otherwise render just 1 row. A better way to do this would be to jest.mock
37
- // out Virtuoso with an impl that just rendered everything, but doing this for now.
38
- initialItemCount: 5, itemContent: (idx) => {
39
- // MapIterator doesn't have at/index lookup so make a copy
40
- const keys = [...state.collection.getKeys()];
41
- const item = state.collection.getItem(keys[idx]);
42
- if (item) {
43
- return (0, jsx_runtime_1.jsx)(Option_1.Option, { item: item, state: state, contrast: contrast }, item.key);
44
- }
45
- } }, void 0) }), void 0)] }), void 0));
36
+ }, ref: listBoxRef }, listBoxProps, { children: [isMultiSelect && state.selectionManager.selectedKeys.size > 0 && ((0, jsx_runtime_1.jsx)("ul", Object.assign({ css: Css_1.Css.listReset.pt2.pl2.pb1.pr1.df.bb.bGray200.add("flexWrap", "wrap").$ }, { children: selectedOptions.map((o) => ((0, jsx_runtime_1.jsx)(ListBoxChip, { state: state, option: o, getOptionValue: getOptionValue, getOptionLabel: getOptionLabel, disabled: state.disabledKeys.has(getOptionValue(o)) }, getOptionValue(o)))) }), void 0)), (0, jsx_runtime_1.jsx)("ul", Object.assign({ css: Css_1.Css.listReset.hPx(popoverHeight).$ }, { children: hasSections ? ([...state.collection].map((section) => ((0, jsx_runtime_1.jsx)(ListBoxSection_1.ListBoxSection, { section: section, state: state, contrast: contrast, onListHeightChange: onListHeightChange, popoverHeight: popoverHeight,
37
+ // Only scroll on focus if using VirtualFocus (used for ComboBoxState (SelectField), but not SelectState (ChipSelectField))
38
+ scrollOnFocus: props.shouldUseVirtualFocus }, section.key)))) : ((0, jsx_runtime_1.jsx)(VirtualizedOptions_1.VirtualizedOptions, { state: state, items: [...state.collection], onListHeightChange: onListHeightChange, contrast: contrast,
39
+ // Only scroll on focus if using VirtualFocus (used for ComboBoxState (SelectField), but not SelectState (ChipSelectField))
40
+ scrollOnFocus: props.shouldUseVirtualFocus }, void 0)) }), void 0)] }), void 0));
46
41
  }
47
42
  exports.ListBox = ListBox;
48
43
  /** Chip used to display selections within ListBox when using the MultiSelectField */
@@ -52,3 +47,5 @@ function ListBoxChip(props) {
52
47
  state.selectionManager.toggleSelection(String(getOptionValue(option)));
53
48
  }, disabled: disabled }, void 0) }), void 0));
54
49
  }
50
+ // UX specified maximum height for a ListBox (in pixels)
51
+ const maxPopoverHeight = 512;
@@ -0,0 +1,12 @@
1
+ import { Node } from "@react-types/shared";
2
+ import { SelectState } from "react-stately";
3
+ interface ListBoxSectionProps<O> {
4
+ section: Node<O>;
5
+ state: SelectState<O>;
6
+ contrast: boolean;
7
+ onListHeightChange: (height: number) => void;
8
+ popoverHeight: number;
9
+ scrollOnFocus?: boolean;
10
+ }
11
+ export declare function ListBoxSection<O>(props: ListBoxSectionProps<O>): import("@emotion/react/jsx-runtime").JSX.Element;
12
+ export {};
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ListBoxSection = void 0;
4
+ const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
+ const react_aria_1 = require("react-aria");
6
+ const Css_1 = require("../../Css");
7
+ const constants_1 = require("./constants");
8
+ const Option_1 = require("./Option");
9
+ const VirtualizedOptions_1 = require("./VirtualizedOptions");
10
+ // Creates a section of options within a ListBox.
11
+ // Currently only expects two possible sections; 1. The list of options, and 2. A persistent action (in that order).
12
+ function ListBoxSection(props) {
13
+ const { section, state, contrast, onListHeightChange, popoverHeight, scrollOnFocus } = props;
14
+ const { itemProps, groupProps } = (0, react_aria_1.useListBoxSection)(section);
15
+ const { separatorProps } = (0, react_aria_1.useSeparator)({ elementType: "li" });
16
+ const isPersistentSection = section.key !== state.collection.getFirstKey();
17
+ const childNodes = [...section.childNodes];
18
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [isPersistentSection && (0, jsx_runtime_1.jsx)("li", Object.assign({}, separatorProps, { css: Css_1.Css.bt.bGray200.$ }), void 0), (0, jsx_runtime_1.jsx)("li", Object.assign({}, itemProps, { css: Css_1.Css.if(!isPersistentSection).overflowAuto.$ }, { children: (0, jsx_runtime_1.jsx)("ul", Object.assign({ css: Css_1.Css.listReset.if(!isPersistentSection).hPx(popoverHeight - constants_1.sectionSeparatorHeight - constants_1.persistentItemHeight).$ }, groupProps, { children: isPersistentSection ? (childNodes.map((item) => (0, jsx_runtime_1.jsx)(Option_1.Option, { item: item, state: state, contrast: contrast }, item.key))) : ((0, jsx_runtime_1.jsx)(VirtualizedOptions_1.VirtualizedOptions, { state: state, items: childNodes, onListHeightChange: onListHeightChange, contrast: contrast, scrollOnFocus: scrollOnFocus }, void 0)) }), void 0) }), void 0)] }, void 0));
19
+ }
20
+ exports.ListBoxSection = ListBoxSection;
@@ -4,6 +4,7 @@ interface OptionProps<O> {
4
4
  item: Node<O>;
5
5
  state: ListState<O> | TreeState<O>;
6
6
  contrast?: boolean;
7
+ scrollToIndex?: (index: number) => void;
7
8
  }
8
9
  /** Represents a single option within a ListBox - used by SelectField and MultiSelectField */
9
10
  export declare function Option<O>(props: OptionProps<O>): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -9,7 +9,7 @@ const Css_1 = require("../../Css");
9
9
  const ChipSelectField_1 = require("../ChipSelectField");
10
10
  /** Represents a single option within a ListBox - used by SelectField and MultiSelectField */
11
11
  function Option(props) {
12
- const { item, state, contrast = false } = props;
12
+ const { item, state, contrast = false, scrollToIndex } = props;
13
13
  const ref = (0, react_1.useRef)(null);
14
14
  const { hoverProps, isHovered } = (0, react_aria_1.useHover)({});
15
15
  const themeStyles = {
@@ -21,7 +21,25 @@ function Option(props) {
21
21
  // Get props for the option element.
22
22
  // Prevent options from receiving browser focus via shouldUseVirtualFocus.
23
23
  const { optionProps, isDisabled, isFocused, isSelected } = (0, react_aria_1.useOption)({ key: item.key, shouldSelectOnPressUp: true, shouldFocusOnHover: false }, state, ref);
24
- return ((0, jsx_runtime_1.jsxs)("li", Object.assign({}, (0, react_aria_1.mergeProps)(optionProps, hoverProps), { ref: ref, css: {
24
+ // Additional onKeyDown logic to ensure the the virtualized list (in <VirtualizedOptions />) scrolls to keep the "focused" option in view
25
+ const onKeyDown = (0, react_1.useCallback)((e) => {
26
+ if (!scrollToIndex || !(e.key === "ArrowDown" || e.key === "ArrowUp")) {
27
+ return;
28
+ }
29
+ const toKey = e.key === "ArrowDown" ? item.nextKey : item.prevKey;
30
+ if (!toKey) {
31
+ return;
32
+ }
33
+ const toItem = state.collection.getItem(toKey);
34
+ // Only scroll the "options" (`state.collection` is a flat list of sections and items - we want to avoid scrolling to a "section" as it is not shown in the UI)
35
+ if (toItem &&
36
+ // Ensure we are only ever scrolling to an "option".
37
+ (toItem.parentKey === "options" || (!toItem.parentKey && toItem.type === "item")) &&
38
+ toItem.index !== undefined) {
39
+ scrollToIndex(toItem.index);
40
+ }
41
+ }, [scrollToIndex, state]);
42
+ return ((0, jsx_runtime_1.jsxs)("li", Object.assign({}, (0, react_aria_1.mergeProps)(optionProps, hoverProps, { onKeyDown }), { ref: ref, css: {
25
43
  ...Css_1.Css.df.aic.jcsb.py1.px2.mh("42px").outline0.cursorPointer.sm.$,
26
44
  // Assumes only one Persistent Item per list - will need to change to utilize Sections if that assumption is incorrect.
27
45
  ...((0, ChipSelectField_1.isPersistentKey)(item.key) ? Css_1.Css.bt.bGray200.$ : {}),
@@ -177,6 +177,7 @@ function SelectFieldBase(props) {
177
177
  isOpen: state.isOpen,
178
178
  onClose: state.close,
179
179
  placement: "bottom left",
180
+ offset: borderless ? 8 : 4,
180
181
  });
181
182
  positionProps.style = {
182
183
  ...positionProps.style,
@@ -184,8 +185,6 @@ function SelectFieldBase(props) {
184
185
  // Ensures the menu never gets too small.
185
186
  minWidth: 200,
186
187
  };
187
- return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.df.fdc.w100.maxw((0, Css_1.px)(550)).$, ref: comboBoxRef }, { children: [(0, jsx_runtime_1.jsx)(SelectFieldInput_1.SelectFieldInput, Object.assign({}, otherProps, { buttonProps: buttonProps, buttonRef: triggerRef, compact: compact, errorMsg: errorMsg, helperText: helperText, fieldDecoration: fieldDecoration, inputProps: inputProps, inputRef: inputRef, inputWrapRef: inputWrapRef, isDisabled: isDisabled, required: required, isReadOnly: isReadOnly, state: state, onBlur: onBlur, onFocus: onFocus, inlineLabel: inlineLabel, label: label, hideLabel: hideLabel, labelProps: labelProps, selectedOptions: fieldState.selectedOptions, getOptionValue: getOptionValue, getOptionLabel: getOptionLabel, sizeToContent: sizeToContent, contrast: contrast, nothingSelectedText: nothingSelectedText, borderless: borderless }), void 0), state.isOpen && ((0, jsx_runtime_1.jsx)(internal_1.Popover, Object.assign({ triggerRef: triggerRef, popoverRef: popoverRef, positionProps: positionProps, onClose: () => state.close(), isOpen: state.isOpen, minWidth: 200 }, { children: (0, jsx_runtime_1.jsx)(ListBox_1.ListBox, Object.assign({}, listBoxProps, { positionProps: positionProps, state: state, listBoxRef: listBoxRef, selectedOptions: fieldState.selectedOptions, getOptionLabel: getOptionLabel, getOptionValue: (o) => (0, Value_1.valueToKey)(getOptionValue(o)), contrast: contrast,
188
- // If the field is set as `borderless`, then the focus state is done with a box-shadow and set further away from the input. If this happens then we want the ListBox to be positioned further away as well.
189
- positionOffset: borderless ? 8 : undefined }), void 0) }), void 0))] }), void 0));
188
+ return ((0, jsx_runtime_1.jsxs)("div", Object.assign({ css: Css_1.Css.df.fdc.w100.maxw((0, Css_1.px)(550)).$, ref: comboBoxRef }, { children: [(0, jsx_runtime_1.jsx)(SelectFieldInput_1.SelectFieldInput, Object.assign({}, otherProps, { buttonProps: buttonProps, buttonRef: triggerRef, compact: compact, errorMsg: errorMsg, helperText: helperText, fieldDecoration: fieldDecoration, inputProps: inputProps, inputRef: inputRef, inputWrapRef: inputWrapRef, isDisabled: isDisabled, required: required, isReadOnly: isReadOnly, state: state, onBlur: onBlur, onFocus: onFocus, inlineLabel: inlineLabel, label: label, hideLabel: hideLabel, labelProps: labelProps, selectedOptions: fieldState.selectedOptions, getOptionValue: getOptionValue, getOptionLabel: getOptionLabel, sizeToContent: sizeToContent, contrast: contrast, nothingSelectedText: nothingSelectedText, borderless: borderless }), void 0), state.isOpen && ((0, jsx_runtime_1.jsx)(internal_1.Popover, Object.assign({ triggerRef: triggerRef, popoverRef: popoverRef, positionProps: positionProps, onClose: () => state.close(), isOpen: state.isOpen, minWidth: 200 }, { children: (0, jsx_runtime_1.jsx)(ListBox_1.ListBox, Object.assign({}, listBoxProps, { positionProps: positionProps, state: state, listBoxRef: listBoxRef, selectedOptions: fieldState.selectedOptions, getOptionLabel: getOptionLabel, getOptionValue: (o) => (0, Value_1.valueToKey)(getOptionValue(o)), contrast: contrast }), void 0) }), void 0))] }), void 0));
190
189
  }
191
190
  exports.SelectFieldBase = SelectFieldBase;
@@ -0,0 +1,11 @@
1
+ import { Node } from "@react-types/shared";
2
+ import { SelectState } from "react-stately";
3
+ interface VirtualizedOptionsProps<O> {
4
+ state: SelectState<O>;
5
+ items: Node<O>[];
6
+ onListHeightChange: (height: number) => void;
7
+ contrast: boolean;
8
+ scrollOnFocus?: boolean;
9
+ }
10
+ export declare function VirtualizedOptions<O>(props: VirtualizedOptionsProps<O>): import("@emotion/react/jsx-runtime").JSX.Element;
11
+ export {};
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VirtualizedOptions = void 0;
4
+ const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const react_virtuoso_1 = require("react-virtuoso");
7
+ const Option_1 = require("./Option");
8
+ // Displays ListBox options in a virtualized container for performance reasons
9
+ function VirtualizedOptions(props) {
10
+ const { state, items, onListHeightChange, contrast, scrollOnFocus } = props;
11
+ const virtuosoRef = (0, react_1.useRef)(null);
12
+ const focusedItem = state.collection.getItem(state.selectionManager.focusedKey);
13
+ const selectedItem = state.selectionManager.selectedKeys.size > 0
14
+ ? state.collection.getItem([...state.selectionManager.selectedKeys.values()][0])
15
+ : undefined;
16
+ // Handle scrolling to the item in focus when navigating options via Keyboard - this should only be applied when using a "virtual focus", such as a ComboBox where the browser's focus remains in the <input /> element.
17
+ (0, react_1.useEffect)(() => {
18
+ if (scrollOnFocus && virtuosoRef.current && (focusedItem === null || focusedItem === void 0 ? void 0 : focusedItem.index)) {
19
+ virtuosoRef.current.scrollToIndex({ index: focusedItem.index, align: "center" });
20
+ }
21
+ }, [focusedItem]);
22
+ return ((0, jsx_runtime_1.jsx)(react_virtuoso_1.Virtuoso, { ref: virtuosoRef, totalListHeightChanged: onListHeightChange, totalCount: items.length,
23
+ // Ensure the selected item is visible when the list renders
24
+ initialTopMostItemIndex: selectedItem ? selectedItem.index : 0,
25
+ // We don't really need to set this, but it's handy for tests, which would
26
+ // otherwise render just 1 row. A better way to do this would be to jest.mock
27
+ // out Virtuoso with an impl that just rendered everything, but doing this for now.
28
+ initialItemCount: 5, itemContent: (idx) => {
29
+ var _a;
30
+ const item = items[idx];
31
+ if (item) {
32
+ return ((0, jsx_runtime_1.jsx)(Option_1.Option, { item: item, state: state, contrast: contrast,
33
+ // Only send scrollToIndex functionality forward if we are not auto-scrolling on focus.
34
+ scrollToIndex: scrollOnFocus ? undefined : (_a = virtuosoRef.current) === null || _a === void 0 ? void 0 : _a.scrollToIndex }, item.key));
35
+ }
36
+ } }, void 0));
37
+ }
38
+ exports.VirtualizedOptions = VirtualizedOptions;
@@ -0,0 +1,2 @@
1
+ export declare const persistentItemHeight = 42;
2
+ export declare const sectionSeparatorHeight = 1;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sectionSeparatorHeight = exports.persistentItemHeight = void 0;
4
+ exports.persistentItemHeight = 42;
5
+ exports.sectionSeparatorHeight = 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.91.2",
3
+ "version": "2.91.6",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",