@homebound/beam 2.219.0 → 2.221.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/components/ButtonMenu.d.ts +1 -0
- package/dist/components/ButtonMenu.js +2 -2
- package/dist/components/internal/Menu.d.ts +1 -0
- package/dist/components/internal/Menu.js +41 -11
- package/dist/inputs/internal/ListBox.d.ts +1 -0
- package/dist/inputs/internal/ListBox.js +3 -3
- package/dist/inputs/internal/ListBoxSection.d.ts +1 -0
- package/dist/inputs/internal/ListBoxSection.js +2 -2
- package/dist/inputs/internal/MenuSearchField.d.ts +7 -0
- package/dist/inputs/internal/MenuSearchField.js +16 -0
- package/dist/inputs/internal/Option.d.ts +1 -0
- package/dist/inputs/internal/Option.js +21 -16
- package/dist/inputs/internal/SelectFieldBase.d.ts +4 -1
- package/dist/inputs/internal/SelectFieldBase.js +15 -4
- package/dist/inputs/internal/VirtualizedOptions.d.ts +1 -0
- package/dist/inputs/internal/VirtualizedOptions.js +2 -2
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import { OverlayTriggerProps } from "./internal/OverlayTrigger";
|
|
|
3
3
|
interface ButtonMenuProps extends Pick<OverlayTriggerProps, "trigger" | "placement" | "disabled" | "tooltip" | "showActiveBorder"> {
|
|
4
4
|
items: MenuItem[];
|
|
5
5
|
persistentItems?: MenuItem[];
|
|
6
|
+
searchable?: boolean;
|
|
6
7
|
defaultOpen?: boolean;
|
|
7
8
|
}
|
|
8
9
|
export declare function ButtonMenu(props: ButtonMenuProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -9,11 +9,11 @@ const Menu_1 = require("./internal/Menu");
|
|
|
9
9
|
const OverlayTrigger_1 = require("./internal/OverlayTrigger");
|
|
10
10
|
const utils_1 = require("../utils");
|
|
11
11
|
function ButtonMenu(props) {
|
|
12
|
-
const { defaultOpen, disabled, items, persistentItems, trigger } = props;
|
|
12
|
+
const { defaultOpen, disabled, items, persistentItems, trigger, searchable } = props;
|
|
13
13
|
const state = (0, react_stately_1.useMenuTriggerState)({ isOpen: defaultOpen });
|
|
14
14
|
const buttonRef = (0, react_1.useRef)(null);
|
|
15
15
|
const { menuTriggerProps, menuProps } = (0, react_aria_1.useMenuTrigger)({ isDisabled: !!disabled }, state, buttonRef);
|
|
16
16
|
const tid = (0, utils_1.useTestIds)(props, (0, OverlayTrigger_1.isTextButton)(trigger) ? trigger.label : (0, OverlayTrigger_1.isIconButton)(trigger) ? trigger.icon : trigger.name);
|
|
17
|
-
return ((0, jsx_runtime_1.jsx)(OverlayTrigger_1.OverlayTrigger, Object.assign({}, props, { menuTriggerProps: menuTriggerProps, state: state, buttonRef: buttonRef }, tid, { children: (0, jsx_runtime_1.jsx)(Menu_1.Menu, Object.assign({ ariaMenuProps: menuProps, onClose: () => state.close(), items: items, persistentItems: persistentItems }, tid), void 0) }), void 0));
|
|
17
|
+
return ((0, jsx_runtime_1.jsx)(OverlayTrigger_1.OverlayTrigger, Object.assign({}, props, { menuTriggerProps: menuTriggerProps, state: state, buttonRef: buttonRef }, tid, { children: (0, jsx_runtime_1.jsx)(Menu_1.Menu, Object.assign({ ariaMenuProps: menuProps, onClose: () => state.close(), items: items, persistentItems: persistentItems, searchable: searchable }, tid), void 0) }), void 0));
|
|
18
18
|
}
|
|
19
19
|
exports.ButtonMenu = ButtonMenu;
|
|
@@ -4,6 +4,7 @@ interface MenuProps<T> {
|
|
|
4
4
|
ariaMenuProps: HTMLAttributes<HTMLElement>;
|
|
5
5
|
onClose: VoidFunction;
|
|
6
6
|
items: MenuItem[];
|
|
7
|
+
searchable?: boolean;
|
|
7
8
|
persistentItems?: MenuItem[];
|
|
8
9
|
}
|
|
9
10
|
export declare function Menu<T>(props: PropsWithChildren<MenuProps<T>>): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -8,9 +8,10 @@ const react_aria_1 = require("react-aria");
|
|
|
8
8
|
const react_stately_1 = require("react-stately");
|
|
9
9
|
const MenuSection_1 = require("./MenuSection");
|
|
10
10
|
const Css_1 = require("../../Css");
|
|
11
|
+
const MenuSearchField_1 = require("../../inputs/internal/MenuSearchField");
|
|
11
12
|
const utils_1 = require("../../utils");
|
|
12
13
|
function Menu(props) {
|
|
13
|
-
const { ariaMenuProps, items, persistentItems, onClose } = props;
|
|
14
|
+
const { ariaMenuProps, items, persistentItems, onClose, searchable } = props;
|
|
14
15
|
// Build out the Menu's Tree data to include the Persistent Action, if any. This is a collection of Nodes that is used
|
|
15
16
|
// by React-Aria to keep track of item states such as focus, and provide hooks for calling those actions.
|
|
16
17
|
const tree = (0, react_stately_1.useTreeData)({
|
|
@@ -18,20 +19,49 @@ function Menu(props) {
|
|
|
18
19
|
getKey: (item) => (0, change_case_1.camelCase)(item.label),
|
|
19
20
|
getChildren: (item) => { var _a; return (_a = item.items) !== null && _a !== void 0 ? _a : []; },
|
|
20
21
|
});
|
|
22
|
+
const [search, setSearch] = (0, react_1.useState)(undefined);
|
|
23
|
+
let { contains } = (0, react_aria_1.useFilter)({
|
|
24
|
+
sensitivity: "base",
|
|
25
|
+
});
|
|
26
|
+
// Filter our tree data items based on the search term
|
|
27
|
+
const filteredTree = (0, react_1.useMemo)(() => {
|
|
28
|
+
const { items, ...others } = tree;
|
|
29
|
+
const [itemsSection, persistentSection] = items;
|
|
30
|
+
if (search) {
|
|
31
|
+
const filteredChildren = itemsSection.children.filter((item) => contains(item.value.label, search));
|
|
32
|
+
const { items, ...otherValues } = itemsSection.value;
|
|
33
|
+
const filteredValue = items === null || items === void 0 ? void 0 : items.filter((item) => contains(item.label, search));
|
|
34
|
+
return {
|
|
35
|
+
...others,
|
|
36
|
+
items: [
|
|
37
|
+
{ ...itemsSection, value: { ...otherValues, children: filteredChildren, items: filteredValue } },
|
|
38
|
+
persistentSection,
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return tree;
|
|
44
|
+
}
|
|
45
|
+
}, [tree, search, contains]);
|
|
21
46
|
const menuChildren = (0, react_1.useMemo)(() => {
|
|
22
|
-
return
|
|
23
|
-
}, [
|
|
24
|
-
const state = (0, react_stately_1.useTreeState)({
|
|
47
|
+
return filteredTree.items.map(({ value: s }) => ((0, jsx_runtime_1.jsx)(react_stately_1.Section, Object.assign({ title: s.label, items: s.items }, { children: (item) => (0, jsx_runtime_1.jsx)(react_stately_1.Item, { children: item.label }, item.label.replace(/"/g, "")) }), s.label.replace(/"/g, ""))));
|
|
48
|
+
}, [filteredTree]);
|
|
49
|
+
const state = (0, react_stately_1.useTreeState)({
|
|
50
|
+
children: menuChildren,
|
|
51
|
+
items: filteredTree.items.map((i) => i.value),
|
|
52
|
+
selectionMode: "none",
|
|
53
|
+
});
|
|
25
54
|
const menuRef = (0, react_1.useRef)(null);
|
|
26
|
-
const { menuProps } = (0, react_aria_1.useMenu)({ ...ariaMenuProps, autoFocus: true }, state, menuRef);
|
|
55
|
+
const { menuProps } = (0, react_aria_1.useMenu)({ ...ariaMenuProps, autoFocus: searchable ? false : true }, state, menuRef);
|
|
27
56
|
const tid = (0, utils_1.useTestIds)(props);
|
|
28
57
|
// Bulk updates of MenuItems below. If we find this to be of sluggish performance, then we can change to be more surgical in our updating.
|
|
29
58
|
// If our list of items change, update the "items" menu section. (key is based on label in `getKey` above)
|
|
30
|
-
(0, react_1.useEffect)(() =>
|
|
31
|
-
return ((0, jsx_runtime_1.jsx)(react_aria_1.FocusScope, { children: (0, jsx_runtime_1.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
59
|
+
(0, react_1.useEffect)(() => filteredTree.update("items", { label: "items", items }), [items]);
|
|
60
|
+
return ((0, jsx_runtime_1.jsx)(react_aria_1.FocusScope, { children: (0, jsx_runtime_1.jsxs)("div", Object.assign({
|
|
61
|
+
// Using `max-height: inherit` allows us to take advantage of the height set on the overlay container, which updates based on the available space for the overlay within the viewport
|
|
62
|
+
css: {
|
|
63
|
+
...Css_1.Css.df.fdc.myPx(4).bgWhite.outline0.br4.bshBasic.maxh("inherit").overflowAuto.$,
|
|
64
|
+
"&:hover": Css_1.Css.bshHover.$,
|
|
65
|
+
} }, { children: [searchable && ((0, jsx_runtime_1.jsx)(MenuSearchField_1.MenuSearchField, Object.assign({ label: "", value: search, placeholder: "Search...", inlineLabel: true, onChange: setSearch }, tid), void 0)), (0, jsx_runtime_1.jsx)("ul", Object.assign({ css: Css_1.Css.listReset.$ }, menuProps, { ref: menuRef }, tid.menu, { children: [...state.collection].map((item) => ((0, jsx_runtime_1.jsx)(MenuSection_1.MenuSectionImpl, Object.assign({ section: item, state: state, onClose: onClose }, tid), item.key))) }), void 0)] }), void 0) }, void 0));
|
|
36
66
|
}
|
|
37
67
|
exports.Menu = Menu;
|
|
@@ -9,6 +9,7 @@ interface ListBoxProps<O, V extends Key> {
|
|
|
9
9
|
contrast?: boolean;
|
|
10
10
|
positionProps: React.HTMLAttributes<Element>;
|
|
11
11
|
loading?: boolean | (() => JSX.Element);
|
|
12
|
+
disabledOptionsWithReasons?: Record<string, string | undefined>;
|
|
12
13
|
}
|
|
13
14
|
/** A ListBox is an internal component used by SelectField and MultiSelectField to display the list of options */
|
|
14
15
|
export declare function ListBox<O, V extends Key>(props: ListBoxProps<O, V>): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -12,7 +12,7 @@ const VirtualizedOptions_1 = require("./VirtualizedOptions");
|
|
|
12
12
|
/** A ListBox is an internal component used by SelectField and MultiSelectField to display the list of options */
|
|
13
13
|
function ListBox(props) {
|
|
14
14
|
var _a;
|
|
15
|
-
const { state, listBoxRef, selectedOptions = [], getOptionLabel, getOptionValue, contrast = false, positionProps, loading, } = props;
|
|
15
|
+
const { state, listBoxRef, selectedOptions = [], getOptionLabel, getOptionValue, contrast = false, positionProps, loading, disabledOptionsWithReasons = {}, } = props;
|
|
16
16
|
const { listBoxProps } = (0, react_aria_1.useListBox)({ disallowEmptySelection: true, ...props }, state, listBoxRef);
|
|
17
17
|
const positionMaxHeight = (_a = positionProps.style) === null || _a === void 0 ? void 0 : _a.maxHeight;
|
|
18
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`).
|
|
@@ -57,9 +57,9 @@ function ListBox(props) {
|
|
|
57
57
|
"&:hover": Css_1.Css.bshHover.$,
|
|
58
58
|
}, 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").$, ref: selectedList }, { children: selectedOptions.map((o) => ((0, jsx_runtime_1.jsx)(ListBoxToggleChip_1.ListBoxToggleChip, { 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.fg1.$ }, { children: hasSections ? ([...state.collection].map((section) => ((0, jsx_runtime_1.jsx)(ListBoxSection_1.ListBoxSection, { section: section, state: state, contrast: contrast, onListHeightChange: onListHeightChange, popoverHeight: popoverHeight,
|
|
59
59
|
// Only scroll on focus if using VirtualFocus (used for ComboBoxState (SelectField), but not SelectState (ChipSelectField))
|
|
60
|
-
scrollOnFocus: props.shouldUseVirtualFocus }, section.key)))) : ((0, jsx_runtime_1.jsx)(VirtualizedOptions_1.VirtualizedOptions, { state: state, items: [...state.collection], onListHeightChange: onListHeightChange, contrast: contrast,
|
|
60
|
+
scrollOnFocus: props.shouldUseVirtualFocus, disabledOptionsWithReasons: disabledOptionsWithReasons }, section.key)))) : ((0, jsx_runtime_1.jsx)(VirtualizedOptions_1.VirtualizedOptions, { state: state, items: [...state.collection], onListHeightChange: onListHeightChange, contrast: contrast,
|
|
61
61
|
// Only scroll on focus if using VirtualFocus (used for ComboBoxState (SelectField), but not SelectState (ChipSelectField))
|
|
62
|
-
scrollOnFocus: props.shouldUseVirtualFocus, loading: loading }, void 0)) }), void 0)] }), void 0));
|
|
62
|
+
scrollOnFocus: props.shouldUseVirtualFocus, loading: loading, disabledOptionsWithReasons: disabledOptionsWithReasons }, void 0)) }), void 0)] }), void 0));
|
|
63
63
|
}
|
|
64
64
|
exports.ListBox = ListBox;
|
|
65
65
|
// UX specified maximum height for a ListBox (in pixels)
|
|
@@ -7,6 +7,7 @@ interface ListBoxSectionProps<O> {
|
|
|
7
7
|
onListHeightChange: (height: number) => void;
|
|
8
8
|
popoverHeight: number;
|
|
9
9
|
scrollOnFocus?: boolean;
|
|
10
|
+
disabledOptionsWithReasons: Record<string, string | undefined>;
|
|
10
11
|
}
|
|
11
12
|
export declare function ListBoxSection<O>(props: ListBoxSectionProps<O>): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
12
13
|
export {};
|
|
@@ -10,11 +10,11 @@ const VirtualizedOptions_1 = require("./VirtualizedOptions");
|
|
|
10
10
|
// Creates a section of options within a ListBox.
|
|
11
11
|
// Currently only expects two possible sections; 1. The list of options, and 2. A persistent action (in that order).
|
|
12
12
|
function ListBoxSection(props) {
|
|
13
|
-
const { section, state, contrast, onListHeightChange, popoverHeight, scrollOnFocus } = props;
|
|
13
|
+
const { section, state, contrast, onListHeightChange, popoverHeight, scrollOnFocus, disabledOptionsWithReasons } = props;
|
|
14
14
|
const { itemProps, groupProps } = (0, react_aria_1.useListBoxSection)(section);
|
|
15
15
|
const { separatorProps } = (0, react_aria_1.useSeparator)({ elementType: "li" });
|
|
16
16
|
const isPersistentSection = section.key !== state.collection.getFirstKey();
|
|
17
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));
|
|
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, disabledReason: disabledOptionsWithReasons[item.key] }, item.key)))) : ((0, jsx_runtime_1.jsx)(VirtualizedOptions_1.VirtualizedOptions, { state: state, items: childNodes, onListHeightChange: onListHeightChange, contrast: contrast, scrollOnFocus: scrollOnFocus, disabledOptionsWithReasons: disabledOptionsWithReasons }, void 0)) }), void 0) }), void 0)] }, void 0));
|
|
19
19
|
}
|
|
20
20
|
exports.ListBoxSection = ListBoxSection;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Only } from "../../Css";
|
|
2
|
+
import { BeamTextFieldProps, TextFieldXss } from "../../interfaces";
|
|
3
|
+
interface TextFieldProps<X> extends BeamTextFieldProps<X> {
|
|
4
|
+
inlineLabel: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function MenuSearchField<X extends Only<TextFieldXss, X>>(props: TextFieldProps<X>): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MenuSearchField = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
5
|
+
const textfield_1 = require("@react-aria/textfield");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const components_1 = require("../../components");
|
|
8
|
+
const utils_1 = require("../../utils");
|
|
9
|
+
const TextFieldBase_1 = require("../TextFieldBase");
|
|
10
|
+
function MenuSearchField(props) {
|
|
11
|
+
const tid = (0, utils_1.useTestIds)(props);
|
|
12
|
+
const inputRef = (0, react_1.useRef)(null);
|
|
13
|
+
const { labelProps, inputProps } = (0, textfield_1.useTextField)({ ...props }, inputRef);
|
|
14
|
+
return ((0, jsx_runtime_1.jsx)(TextFieldBase_1.TextFieldBase, Object.assign({ label: "", labelProps: labelProps, inputProps: inputProps, startAdornment: (0, jsx_runtime_1.jsx)(components_1.Icon, { icon: "search" }, void 0) }, tid.search), void 0));
|
|
15
|
+
}
|
|
16
|
+
exports.MenuSearchField = MenuSearchField;
|
|
@@ -5,6 +5,7 @@ interface OptionProps<O> {
|
|
|
5
5
|
state: ListState<O> | TreeState<O>;
|
|
6
6
|
contrast?: boolean;
|
|
7
7
|
scrollToIndex?: (index: number) => void;
|
|
8
|
+
disabledReason?: string;
|
|
8
9
|
}
|
|
9
10
|
/** Represents a single option within a ListBox - used by SelectField and MultiSelectField */
|
|
10
11
|
export declare function Option<O>(props: OptionProps<O>): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -4,12 +4,13 @@ exports.Option = 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 components_1 = require("../../components");
|
|
7
8
|
const Icon_1 = require("../../components/Icon");
|
|
8
9
|
const Css_1 = require("../../Css");
|
|
9
10
|
const ChipSelectField_1 = require("../ChipSelectField");
|
|
10
11
|
/** Represents a single option within a ListBox - used by SelectField and MultiSelectField */
|
|
11
12
|
function Option(props) {
|
|
12
|
-
const { item, state, contrast = false, scrollToIndex } = props;
|
|
13
|
+
const { item, state, contrast = false, scrollToIndex, disabledReason } = props;
|
|
13
14
|
const ref = (0, react_1.useRef)(null);
|
|
14
15
|
const { hoverProps, isHovered } = (0, react_aria_1.useHover)({});
|
|
15
16
|
const themeStyles = {
|
|
@@ -39,20 +40,24 @@ function Option(props) {
|
|
|
39
40
|
scrollToIndex(toItem.index);
|
|
40
41
|
}
|
|
41
42
|
}, [scrollToIndex, state]);
|
|
42
|
-
return (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
43
|
+
return (0, components_1.maybeTooltip)({
|
|
44
|
+
title: disabledReason,
|
|
45
|
+
placement: "right",
|
|
46
|
+
children: ((0, jsx_runtime_1.jsxs)("li", Object.assign({}, (0, react_aria_1.mergeProps)(optionProps, hoverProps, { onKeyDown }), { ref: ref, css: {
|
|
47
|
+
...Css_1.Css.df.aic.jcsb.py1.px2.mh("42px").outline0.cursorPointer.sm.$,
|
|
48
|
+
// Assumes only one Persistent Item per list - will need to change to utilize Sections if that assumption is incorrect.
|
|
49
|
+
...((0, ChipSelectField_1.isPersistentKey)(item.key) ? Css_1.Css.bt.bGray200.$ : {}),
|
|
50
|
+
...themeStyles.item,
|
|
51
|
+
...(isHovered && !isDisabled ? themeStyles.hover : {}),
|
|
52
|
+
...(isFocused ? themeStyles.focus : {}),
|
|
53
|
+
...(isDisabled ? themeStyles.disabled : {}),
|
|
54
|
+
} }, { children: [item.rendered, isSelected && ((0, jsx_runtime_1.jsx)("span", Object.assign({ css: Css_1.Css.fs0.$ }, { children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { icon: "check", color: !contrast
|
|
55
|
+
? isDisabled
|
|
56
|
+
? Css_1.Palette.Gray400
|
|
57
|
+
: Css_1.Palette.LightBlue700
|
|
58
|
+
: isDisabled
|
|
59
|
+
? Css_1.Palette.Gray500
|
|
60
|
+
: Css_1.Palette.White }, void 0) }), void 0))] }), void 0)),
|
|
61
|
+
});
|
|
57
62
|
}
|
|
58
63
|
exports.Option = Option;
|
|
@@ -11,7 +11,10 @@ export interface BeamSelectFieldBaseProps<O, V extends Value> extends BeamFocusa
|
|
|
11
11
|
values: V[] | undefined;
|
|
12
12
|
onSelect: (values: V[], opts: O[]) => void;
|
|
13
13
|
multiselect?: boolean;
|
|
14
|
-
disabledOptions?: V
|
|
14
|
+
disabledOptions?: (V | {
|
|
15
|
+
value: V;
|
|
16
|
+
reason: string;
|
|
17
|
+
})[];
|
|
15
18
|
options: OptionsOrLoad<O>;
|
|
16
19
|
/** Whether the field is disabled. If a ReactNode, it's treated as a "disabled reason" that's shown in a tooltip. */
|
|
17
20
|
disabled?: boolean | ReactNode;
|
|
@@ -22,7 +22,7 @@ const utils_1 = require("../../utils");
|
|
|
22
22
|
* and so we cannot easily change them.
|
|
23
23
|
*/
|
|
24
24
|
function SelectFieldBase(props) {
|
|
25
|
-
var _a;
|
|
25
|
+
var _a, _b;
|
|
26
26
|
const { disabled, readOnly, onSelect, options, multiselect = false, values = [], nothingSelectedText = "", contrast, disabledOptions, borderless, unsetLabel, ...otherProps } = props;
|
|
27
27
|
// Call `initializeOptions` to prepend the `unset` option if the `unsetLabel` was provided.
|
|
28
28
|
const maybeOptions = (0, react_1.useMemo)(() => initializeOptions(options, unsetLabel), [options, unsetLabel]);
|
|
@@ -143,9 +143,12 @@ function SelectFieldBase(props) {
|
|
|
143
143
|
const inputWrapRef = (0, react_1.useRef)(null);
|
|
144
144
|
const listBoxRef = (0, react_1.useRef)(null);
|
|
145
145
|
const popoverRef = (0, react_1.useRef)(null);
|
|
146
|
+
// `disabledKeys` from ComboBoxState does not support additional meta for showing a disabled reason to the user
|
|
147
|
+
// This lookup map helps us cleanly prune out the optional reason text, then access it further down the component tree
|
|
148
|
+
const disabledOptionsWithReasons = Object.fromEntries((_a = disabledOptions === null || disabledOptions === void 0 ? void 0 : disabledOptions.map(disabledOptionToKeyedTuple)) !== null && _a !== void 0 ? _a : []);
|
|
146
149
|
const comboBoxProps = {
|
|
147
150
|
...otherProps,
|
|
148
|
-
disabledKeys:
|
|
151
|
+
disabledKeys: Object.keys(disabledOptionsWithReasons),
|
|
149
152
|
inputValue: fieldState.inputValue,
|
|
150
153
|
items: fieldState.filteredOptions,
|
|
151
154
|
isDisabled,
|
|
@@ -240,11 +243,11 @@ function SelectFieldBase(props) {
|
|
|
240
243
|
});
|
|
241
244
|
positionProps.style = {
|
|
242
245
|
...positionProps.style,
|
|
243
|
-
width: (
|
|
246
|
+
width: (_b = comboBoxRef === null || comboBoxRef === void 0 ? void 0 : comboBoxRef.current) === null || _b === void 0 ? void 0 : _b.clientWidth,
|
|
244
247
|
// Ensures the menu never gets too small.
|
|
245
248
|
minWidth: 200,
|
|
246
249
|
};
|
|
247
|
-
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, inputProps: inputProps, inputRef: inputRef, inputWrapRef: inputWrapRef, state: state, labelProps: labelProps, selectedOptions: fieldState.selectedOptions, getOptionValue: getOptionValue, getOptionLabel: getOptionLabel, contrast: contrast, nothingSelectedText: nothingSelectedText, borderless: borderless, tooltip: (0, components_1.resolveTooltip)(disabled, undefined, readOnly), resetField: resetField }), 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, loading: fieldState.optionsLoading }), void 0) }), void 0))] }), void 0));
|
|
250
|
+
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, inputProps: inputProps, inputRef: inputRef, inputWrapRef: inputWrapRef, state: state, labelProps: labelProps, selectedOptions: fieldState.selectedOptions, getOptionValue: getOptionValue, getOptionLabel: getOptionLabel, contrast: contrast, nothingSelectedText: nothingSelectedText, borderless: borderless, tooltip: (0, components_1.resolveTooltip)(disabled, undefined, readOnly), resetField: resetField }), 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, loading: fieldState.optionsLoading, disabledOptionsWithReasons: disabledOptionsWithReasons }), void 0) }), void 0))] }), void 0));
|
|
248
251
|
}
|
|
249
252
|
exports.SelectFieldBase = SelectFieldBase;
|
|
250
253
|
function getInputValue(selectedOptions, getOptionLabel, multiselect, nothingSelectedText) {
|
|
@@ -268,3 +271,11 @@ function getOptionsWithUnset(unsetLabel, options) {
|
|
|
268
271
|
return [exports.unsetOption, ...options];
|
|
269
272
|
}
|
|
270
273
|
exports.unsetOption = {};
|
|
274
|
+
function disabledOptionToKeyedTuple(disabledOption) {
|
|
275
|
+
if (typeof disabledOption === "object" && disabledOption !== null) {
|
|
276
|
+
return [(0, Value_1.valueToKey)(disabledOption.value), disabledOption.reason];
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
return [(0, Value_1.valueToKey)(disabledOption), undefined];
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -7,6 +7,7 @@ interface VirtualizedOptionsProps<O> {
|
|
|
7
7
|
contrast: boolean;
|
|
8
8
|
scrollOnFocus?: boolean;
|
|
9
9
|
loading?: boolean | (() => JSX.Element);
|
|
10
|
+
disabledOptionsWithReasons: Record<string, string | undefined>;
|
|
10
11
|
}
|
|
11
12
|
export declare function VirtualizedOptions<O>(props: VirtualizedOptionsProps<O>): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
12
13
|
export {};
|
|
@@ -8,7 +8,7 @@ const LoadingDots_1 = require("./LoadingDots");
|
|
|
8
8
|
const Option_1 = require("./Option");
|
|
9
9
|
// Displays ListBox options in a virtualized container for performance reasons
|
|
10
10
|
function VirtualizedOptions(props) {
|
|
11
|
-
const { state, items, onListHeightChange, contrast, scrollOnFocus, loading } = props;
|
|
11
|
+
const { state, items, onListHeightChange, contrast, scrollOnFocus, loading, disabledOptionsWithReasons } = props;
|
|
12
12
|
const virtuosoRef = (0, react_1.useRef)(null);
|
|
13
13
|
const focusedItem = state.collection.getItem(state.selectionManager.focusedKey);
|
|
14
14
|
const selectedItem = state.selectionManager.selectedKeys.size > 0
|
|
@@ -32,7 +32,7 @@ function VirtualizedOptions(props) {
|
|
|
32
32
|
if (item) {
|
|
33
33
|
return ((0, jsx_runtime_1.jsx)(Option_1.Option, { item: item, state: state, contrast: contrast,
|
|
34
34
|
// Only send scrollToIndex functionality forward if we are not auto-scrolling on focus.
|
|
35
|
-
scrollToIndex: scrollOnFocus ? undefined : (_a = virtuosoRef.current) === null || _a === void 0 ? void 0 : _a.scrollToIndex }, item.key));
|
|
35
|
+
scrollToIndex: scrollOnFocus ? undefined : (_a = virtuosoRef.current) === null || _a === void 0 ? void 0 : _a.scrollToIndex, disabledReason: disabledOptionsWithReasons[item.key] }, item.key));
|
|
36
36
|
}
|
|
37
37
|
}, components: !loading
|
|
38
38
|
? {}
|