@homebound/beam 2.218.1 → 2.220.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.
@@ -39,6 +39,7 @@ export declare type GridTableApi<R extends Kinded> = {
39
39
  selectRow: (id: string, selected?: boolean) => void;
40
40
  /** Toggle collapse state of a row by id */
41
41
  toggleCollapsedRow: (id: string) => void;
42
+ isCollapsedRow: (id: string) => boolean;
42
43
  setVisibleColumns: (ids: string[]) => void;
43
44
  getVisibleColumnIds: () => string[];
44
45
  };
@@ -55,6 +56,7 @@ export declare class GridTableApiImpl<R extends Kinded> implements GridTableApi<
55
56
  setActiveCellId(id: string | undefined): void;
56
57
  selectRow(id: string, selected?: boolean): void;
57
58
  toggleCollapsedRow(id: string): void;
59
+ isCollapsedRow(id: string): boolean;
58
60
  setVisibleColumns(ids: string[]): void;
59
61
  getVisibleColumnIds(): string[];
60
62
  }
@@ -68,6 +68,9 @@ class GridTableApiImpl {
68
68
  toggleCollapsedRow(id) {
69
69
  this.tableState.toggleCollapsed(id);
70
70
  }
71
+ isCollapsedRow(id) {
72
+ return this.tableState.isCollapsed(id);
73
+ }
71
74
  setVisibleColumns(ids) {
72
75
  this.tableState.setVisibleColumns(ids);
73
76
  }
@@ -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;
@@ -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 ((0, jsx_runtime_1.jsxs)("li", Object.assign({}, (0, react_aria_1.mergeProps)(optionProps, hoverProps, { onKeyDown }), { ref: ref, css: {
43
- ...Css_1.Css.df.aic.jcsb.py1.px2.mh("42px").outline0.cursorPointer.sm.$,
44
- // Assumes only one Persistent Item per list - will need to change to utilize Sections if that assumption is incorrect.
45
- ...((0, ChipSelectField_1.isPersistentKey)(item.key) ? Css_1.Css.bt.bGray200.$ : {}),
46
- ...themeStyles.item,
47
- ...(isHovered && !isDisabled ? themeStyles.hover : {}),
48
- ...(isFocused ? themeStyles.focus : {}),
49
- ...(isDisabled ? themeStyles.disabled : {}),
50
- } }, { 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
51
- ? isDisabled
52
- ? Css_1.Palette.Gray400
53
- : Css_1.Palette.LightBlue700
54
- : isDisabled
55
- ? Css_1.Palette.Gray500
56
- : Css_1.Palette.White }, void 0) }), void 0))] }), void 0));
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: disabledOptions === null || disabledOptions === void 0 ? void 0 : disabledOptions.map(Value_1.valueToKey),
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: (_a = comboBoxRef === null || comboBoxRef === void 0 ? void 0 : comboBoxRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth,
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
  ? {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.218.1",
3
+ "version": "2.220.0",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",