@homebound/beam 2.320.1 → 2.321.1

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.
@@ -97,6 +97,7 @@ export declare const Icons: {
97
97
  file: import("@emotion/react/jsx-runtime").JSX.Element;
98
98
  images: import("@emotion/react/jsx-runtime").JSX.Element;
99
99
  openBook: import("@emotion/react/jsx-runtime").JSX.Element;
100
+ inbox: import("@emotion/react/jsx-runtime").JSX.Element;
100
101
  criticalPath: import("@emotion/react/jsx-runtime").JSX.Element;
101
102
  calendarError: import("@emotion/react/jsx-runtime").JSX.Element;
102
103
  nested: import("@emotion/react/jsx-runtime").JSX.Element;
@@ -106,6 +106,7 @@ exports.Icons = {
106
106
  images: ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("path", { d: "M20 2H8C6.897 2 6 2.897 6 4V16C6 17.103 6.897 18 8 18H20C21.103 18 22 17.103 22 16V4C22 2.897 21.103 2 20 2ZM8 16V4H20L20.002 16H8Z" }), (0, jsx_runtime_1.jsx)("path", { d: "M4 8H2V20C2 21.103 2.897 22 4 22H16V20H4V8Z" }), (0, jsx_runtime_1.jsx)("path", { d: "M12 12L11 11L9 14H19L15 8L12 12Z" })] })),
107
107
  openBook: ((0, jsx_runtime_1.jsx)("path", { d: "M21 3H14C13.229 3 12.532 3.301 12 3.78C11.468 3.301 10.771 3 10 3H3C2.447 3 2 3.448 2 4V19C2 19.553 2.447 20 3 20H8.758C9.284 20 9.8 20.214 10.172 20.586L11.293 21.707C11.302 21.716 11.314 21.719 11.323 21.728C11.409 21.807 11.505 21.877 11.617 21.924C11.618 21.924 11.618 21.924 11.619 21.924C11.74 21.974 11.869 22 12 22C12.131 22 12.26 21.974 12.381 21.924C12.382 21.924 12.382 21.924 12.383 21.924C12.495 21.877 12.591 21.807 12.677 21.728C12.686 21.719 12.698 21.716 12.707 21.707L13.828 20.586C14.2 20.214 14.716 20 15.242 20H21C21.553 20 22 19.553 22 19V4C22 3.448 21.553 3 21 3ZM8.758 18H4V5H10C10.552 5 11 5.449 11 6V18.689C10.342 18.246 9.557 18 8.758 18ZM20 18H15.242C14.443 18 13.658 18.246 13 18.689V6C13 5.449 13.448 5 14 5H20V18Z" })),
108
108
  // Misc
109
+ inbox: ((0, jsx_runtime_1.jsx)("path", { d: "M20 3H4C2.897 3 2 3.897 2 5V13V14V19C2 20.104 2.896 21 4 21H20C21.104 21 22 20.104 22 19V14V13V5C22 3.897 21.103 3 20 3ZM19 12H16H15.858C15.412 13.722 13.861 15 12 15C10.139 15 8.588 13.722 8.142 12H8H4V5H20V12H19Z" })),
109
110
  criticalPath: ((0, jsx_runtime_1.jsx)("path", { d: "M6.5 20H14.844C15.2 20.753 15.636 21.422 16.093 22H6.5C4.019 22 2 19.981 2 17.5C2 15.019 4.019 13 6.5 13H13.5C14.327 13 15 12.327 15 11.5C15 10.673 14.327 10 13.5 10H7.285C7.771 9.443 8.248 8.771 8.639 8H13.5C15.43 8 17 9.57 17 11.5C17 13.43 15.43 15 13.5 15H6.5C5.121 15 4 16.121 4 17.5C4 18.879 5.121 20 6.5 20ZM2 5C2 3.346 3.346 2 5 2C6.654 2 8 3.346 8 5C8 8.187 5 10 5 10C5 10 2 8.188 2 5ZM3.5 5C3.5 5.828 4.172 6.5 5 6.5C5.828 6.5 6.5 5.828 6.5 5C6.5 4.172 5.828 3.5 5 3.5C4.172 3.5 3.5 4.172 3.5 5ZM16 17C16 15.346 17.346 14 19 14C20.654 14 22 15.346 22 17C22 20.187 19 22 19 22C19 22 16 20.188 16 17ZM17.5 17C17.5 17.828 18.172 18.5 19 18.5C19.828 18.5 20.5 17.828 20.5 17C20.5 16.172 19.828 15.5 19 15.5C18.172 15.5 17.5 16.172 17.5 17Z" })),
110
111
  calendarError: ((0, jsx_runtime_1.jsx)("path", { d: "M2.99902 8V6C2.99902 4.897 3.89602 4 4.99902 4H6.99902V2H8.99902V4H14.999V2H16.999V4H18.999C20.102 4 20.999 4.897 20.999 6V8V20C20.999 21.103 20.102 22 18.999 22H4.99902C3.89602 22 2.99902 21.103 2.99902 20V8ZM4.99902 20H19.001L18.999 7H4.99902V20ZM13 9H11V15H13V9ZM13 16H11V18H13V16Z" })),
111
112
  nested: (0, jsx_runtime_1.jsx)("path", { d: "M7 6.99994V16.3333L16.3333 16.3333V14.3333H9L9 6.99994H7Z" }),
@@ -12,7 +12,6 @@ const Css_1 = require("../../Css");
12
12
  const ComboBoxInput_1 = require("./ComboBoxInput");
13
13
  const ListBox_1 = require("./ListBox");
14
14
  const Value_1 = require("../Value");
15
- const utils_1 = require("../../utils");
16
15
  /**
17
16
  * Provides a non-native select/dropdown widget that allows the user to type to filter the options.
18
17
  *
@@ -25,45 +24,49 @@ const utils_1 = require("../../utils");
25
24
  function ComboBoxBase(props) {
26
25
  var _a, _b, _c, _d;
27
26
  const { fieldProps } = (0, PresentationContext_1.usePresentationContext)();
28
- const { disabled, readOnly, onSelect, options: propOptions, multiselect = false, values = [], nothingSelectedText = "", contrast, disabledOptions, borderless, unsetLabel, getOptionLabel: propOptionLabel, getOptionValue: propOptionValue, getOptionMenuLabel: propOptionMenuLabel, ...otherProps } = props;
27
+ const { disabled, readOnly, onSelect, options: propOptions, multiselect = false, values: propValues, nothingSelectedText = "", contrast, disabledOptions, borderless, unsetLabel, getOptionLabel: propOptionLabel, getOptionValue: propOptionValue, getOptionMenuLabel: propOptionMenuLabel, ...otherProps } = props;
29
28
  const labelStyle = (_b = (_a = otherProps.labelStyle) !== null && _a !== void 0 ? _a : fieldProps === null || fieldProps === void 0 ? void 0 : fieldProps.labelStyle) !== null && _b !== void 0 ? _b : "above";
30
29
  // Memoize the callback functions and handle the `unset` option if provided.
31
- const getOptionLabel = (0, react_1.useCallback)((o) => (unsetLabel && o === exports.unsetOption ? unsetLabel : propOptionLabel(o)), [propOptionLabel, unsetLabel]);
32
- const getOptionValue = (0, react_1.useCallback)((o) => (unsetLabel && o === exports.unsetOption ? undefined : propOptionValue(o)), [propOptionValue, unsetLabel]);
33
- const getOptionMenuLabel = (0, react_1.useCallback)((o) => propOptionMenuLabel ? propOptionMenuLabel(o, Boolean(unsetLabel) && o === exports.unsetOption) : getOptionLabel(o), [propOptionMenuLabel, unsetLabel, getOptionLabel]);
30
+ const getOptionLabel = (0, react_1.useCallback)((o) => (unsetLabel && o === exports.unsetOption ? unsetLabel : propOptionLabel(o)),
31
+ // propOptionLabel is basically always a lambda, so don't dep on it
32
+ // eslint-disable-next-line react-hooks/exhaustive-deps
33
+ [unsetLabel]);
34
+ const getOptionValue = (0, react_1.useCallback)((o) => (unsetLabel && o === exports.unsetOption ? undefined : propOptionValue(o)),
35
+ // propOptionValue is basically always a lambda, so don't dep on it
36
+ // eslint-disable-next-line react-hooks/exhaustive-deps
37
+ [unsetLabel]);
38
+ const getOptionMenuLabel = (0, react_1.useCallback)((o) => propOptionMenuLabel ? propOptionMenuLabel(o, Boolean(unsetLabel) && o === exports.unsetOption) : getOptionLabel(o),
39
+ // propOptionMenuLabel is basically always a lambda, so don't dep on it
40
+ // eslint-disable-next-line react-hooks/exhaustive-deps
41
+ [unsetLabel, getOptionLabel]);
34
42
  // Call `initializeOptions` to prepend the `unset` option if the `unsetLabel` was provided.
35
43
  const options = (0, react_1.useMemo)(() => initializeOptions(propOptions, getOptionValue, unsetLabel),
36
44
  // If the caller is using { current, load, options }, memoize on only `current` and `options` values.
37
45
  // ...and don't bother on memoizing on getOptionValue b/c it's basically always a lambda
38
46
  // eslint-disable-next-line react-hooks/exhaustive-deps
39
47
  Array.isArray(propOptions) ? [propOptions, unsetLabel] : [propOptions.current, propOptions.options, unsetLabel]);
48
+ const values = (0, react_1.useMemo)(() => propValues !== null && propValues !== void 0 ? propValues : [], [propValues]);
49
+ const selectedOptions = (0, react_1.useMemo)(() => {
50
+ return options.filter((o) => values.includes(getOptionValue(o)));
51
+ }, [options, values, getOptionValue]);
40
52
  const { contains } = (0, react_aria_1.useFilter)({ sensitivity: "base" });
41
53
  const isDisabled = !!disabled;
42
54
  const isReadOnly = !!readOnly;
43
55
  // Do a one-time initialize of fieldState
44
56
  const [fieldState, setFieldState] = (0, react_1.useState)(() => {
45
- var _a;
46
- const selectedOptions = options.filter((o) => values.includes(getOptionValue(o)));
47
57
  return {
48
- selectedKeys: (_a = selectedOptions === null || selectedOptions === void 0 ? void 0 : selectedOptions.map((o) => (0, Value_1.valueToKey)(getOptionValue(o)))) !== null && _a !== void 0 ? _a : [],
49
- inputValue: getInputValue(options.filter((o) => values === null || values === void 0 ? void 0 : values.includes(getOptionValue(o))), getOptionLabel, multiselect, nothingSelectedText),
50
- filteredOptions: options,
51
- allOptions: options,
52
- selectedOptions,
58
+ inputValue: getInputValue(selectedOptions, getOptionLabel, multiselect, nothingSelectedText),
59
+ searchValue: undefined,
53
60
  optionsLoading: false,
54
61
  };
55
62
  });
63
+ const { searchValue } = fieldState;
64
+ const filteredOptions = (0, react_1.useMemo)(() => {
65
+ return !searchValue ? options : options.filter((o) => contains(getOptionLabel(o), searchValue));
66
+ }, [options, searchValue, getOptionLabel, contains]);
56
67
  /** Resets field's input value and filtered options list for cases where the user exits the field without making changes (on Escape, or onBlur) */
57
68
  function resetField() {
58
- const inputValue = getInputValue(fieldState.allOptions.filter((o) => values === null || values === void 0 ? void 0 : values.includes(getOptionValue(o))), getOptionLabel, multiselect, nothingSelectedText);
59
- // Conditionally reset the value if the current inputValue doesn't match that of the passed in value, or we filtered the list
60
- if (inputValue !== fieldState.inputValue || fieldState.filteredOptions.length !== fieldState.allOptions.length) {
61
- setFieldState((prevState) => ({
62
- ...prevState,
63
- inputValue,
64
- filteredOptions: prevState.allOptions,
65
- }));
66
- }
69
+ setFieldState((prevState) => ({ ...prevState, searchValue: undefined }));
67
70
  }
68
71
  function onSelectionChange(keys) {
69
72
  // We don't currently handle the "all" case
@@ -76,30 +79,11 @@ function ComboBoxBase(props) {
76
79
  const selectionChanged = !(keys.size === state.selectionManager.selectedKeys.size &&
77
80
  [...keys].every((value) => state.selectionManager.selectedKeys.has(value)));
78
81
  if (multiselect && keys.size === 0) {
79
- setFieldState({
80
- ...fieldState,
81
- inputValue: state.isOpen ? "" : nothingSelectedText,
82
- selectedKeys: [],
83
- selectedOptions: [],
84
- });
85
82
  selectionChanged && onSelect([], []);
86
83
  return;
87
84
  }
88
85
  const selectedKeys = [...keys.values()];
89
- const selectedOptions = fieldState.allOptions.filter((o) => selectedKeys.includes((0, Value_1.valueToKey)(getOptionValue(o))));
90
- const firstSelectedOption = selectedOptions[0];
91
- setFieldState((prevState) => ({
92
- ...prevState,
93
- // If menu is open then reset inputValue to "". Otherwise set inputValue depending on number of options selected.
94
- inputValue: multiselect && (state.isOpen || selectedKeys.length > 1)
95
- ? ""
96
- : firstSelectedOption
97
- ? getOptionLabel(firstSelectedOption)
98
- : "",
99
- selectedKeys,
100
- selectedOptions,
101
- filteredOptions: fieldState.allOptions,
102
- }));
86
+ const selectedOptions = options.filter((o) => selectedKeys.includes((0, Value_1.valueToKey)(getOptionValue(o))));
103
87
  selectionChanged && onSelect(selectedKeys.map(Value_1.keyToValue), selectedOptions);
104
88
  if (!multiselect) {
105
89
  // Close menu upon selection change only for Single selection mode
@@ -108,11 +92,7 @@ function ComboBoxBase(props) {
108
92
  }
109
93
  function onInputChange(value) {
110
94
  if (value !== fieldState.inputValue) {
111
- setFieldState((prevState) => ({
112
- ...prevState,
113
- inputValue: value,
114
- filteredOptions: fieldState.allOptions.filter((o) => contains(getOptionLabel(o), value)),
115
- }));
95
+ setFieldState((prevState) => ({ ...prevState, inputValue: value, searchValue: value }));
116
96
  }
117
97
  }
118
98
  async function maybeInitLoad() {
@@ -147,7 +127,7 @@ function ComboBoxBase(props) {
147
127
  ...otherProps,
148
128
  disabledKeys: Object.keys(disabledOptionsWithReasons),
149
129
  inputValue: fieldState.inputValue,
150
- items: fieldState.filteredOptions,
130
+ items: filteredOptions,
151
131
  isDisabled,
152
132
  isReadOnly,
153
133
  onInputChange,
@@ -172,59 +152,34 @@ function ComboBoxBase(props) {
172
152
  }
173
153
  },
174
154
  });
155
+ const selectedKeys = (0, react_1.useMemo)(() => {
156
+ return selectedOptions.map((o) => (0, Value_1.valueToKey)(getOptionValue(o)));
157
+ }, [selectedOptions, getOptionValue]);
175
158
  // @ts-ignore - `selectionManager.state` exists, but not according to the types
176
159
  state.selectionManager.state = (0, react_stately_1.useMultipleSelectionState)({
177
160
  selectionMode: multiselect ? "multiple" : "single",
178
161
  // Do not allow an empty selection if single select mode
179
162
  disallowEmptySelection: !multiselect,
180
- selectedKeys: fieldState.selectedKeys,
163
+ selectedKeys,
181
164
  onSelectionChange,
182
165
  });
183
- // Ensure we reset if the field's values change and the user is not actively selecting options.
166
+ // Reset inputValue when closed or selected changes
184
167
  (0, react_1.useEffect)(() => {
185
- if (!state.isOpen && !(0, utils_1.areArraysEqual)(values, fieldState.selectedKeys)) {
186
- setFieldState((prevState) => {
187
- var _a;
188
- const selectedOptions = prevState.allOptions.filter((o) => values === null || values === void 0 ? void 0 : values.includes(getOptionValue(o)));
189
- return {
190
- ...prevState,
191
- selectedKeys: (_a = selectedOptions === null || selectedOptions === void 0 ? void 0 : selectedOptions.map((o) => (0, Value_1.valueToKey)(getOptionValue(o)))) !== null && _a !== void 0 ? _a : [],
192
- inputValue: selectedOptions.length === 1
193
- ? getOptionLabel(selectedOptions[0])
194
- : multiselect && selectedOptions.length === 0
195
- ? nothingSelectedText
196
- : "",
197
- selectedOptions: selectedOptions,
198
- };
199
- });
168
+ if (state.isOpen && multiselect) {
169
+ // While the multiselect is open, let the user keep typing
170
+ setFieldState((prevState) => ({
171
+ ...prevState,
172
+ inputValue: "",
173
+ searchValue: "",
174
+ }));
200
175
  }
201
- },
202
- // TODO: validate this eslint-disable. It was automatically ignored as part of https://app.shortcut.com/homebound-team/story/40033/enable-react-hooks-exhaustive-deps-for-react-projects
203
- // eslint-disable-next-line react-hooks/exhaustive-deps
204
- [values]);
205
- // Re-sync fieldState.allOptions
206
- (0, react_1.useEffect)(() => {
207
- setFieldState((prevState) => {
208
- var _a;
209
- const selectedOptions = options.filter((o) => values === null || values === void 0 ? void 0 : values.includes(getOptionValue(o)));
210
- return {
176
+ else {
177
+ setFieldState((prevState) => ({
211
178
  ...prevState,
212
- selectedKeys: (_a = selectedOptions === null || selectedOptions === void 0 ? void 0 : selectedOptions.map((o) => (0, Value_1.valueToKey)(getOptionValue(o)))) !== null && _a !== void 0 ? _a : [],
213
- inputValue: selectedOptions.length === 1
214
- ? getOptionLabel(selectedOptions[0])
215
- : multiselect && selectedOptions.length === 0
216
- ? nothingSelectedText
217
- : "",
218
- selectedOptions: selectedOptions,
219
- filteredOptions: options,
220
- allOptions: options,
221
- };
222
- });
223
- },
224
- // We're primarily only re-setting `allOptions`, and so recalc selected as well, but we don't
225
- // want to depend on values/etc., b/c we'll defer to their useEffects to update their state
226
- // eslint-disable-next-line react-hooks/exhaustive-deps
227
- [options]);
179
+ inputValue: getInputValue(selectedOptions, getOptionLabel, multiselect, nothingSelectedText),
180
+ }));
181
+ }
182
+ }, [state.isOpen, selectedOptions, getOptionLabel, multiselect, nothingSelectedText]);
228
183
  // For the most part, the returned props contain `aria-*` and `id` attributes for accessibility purposes.
229
184
  const { buttonProps: triggerProps, inputProps, listBoxProps, labelProps, } = (0, react_aria_1.useComboBox)({
230
185
  ...comboBoxProps,
@@ -251,7 +206,7 @@ function ComboBoxBase(props) {
251
206
  // Ensures the menu never gets too small.
252
207
  minWidth: 200,
253
208
  };
254
- return ((0, jsx_runtime_1.jsxs)("div", { css: Css_1.Css.df.fdc.w100.maxw((0, Css_1.px)(550)).if(labelStyle === "left").maxw100.$, ref: comboBoxRef, children: [(0, jsx_runtime_1.jsx)(ComboBoxInput_1.ComboBoxInput, { ...otherProps, buttonProps: buttonProps, buttonRef: triggerRef, inputProps: inputProps, inputRef: inputRef, inputWrapRef: inputWrapRef, listBoxRef: listBoxRef, 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 }), state.isOpen && ((0, jsx_runtime_1.jsx)(internal_1.Popover, { triggerRef: triggerRef, popoverRef: popoverRef, positionProps: positionProps, onClose: () => state.close(), isOpen: state.isOpen, minWidth: 200, children: (0, jsx_runtime_1.jsx)(ListBox_1.ListBox, { ...listBoxProps, positionProps: positionProps, state: state, listBoxRef: listBoxRef, selectedOptions: fieldState.selectedOptions, getOptionLabel: getOptionLabel, getOptionValue: (o) => (0, Value_1.valueToKey)(getOptionValue(o)), contrast: contrast, horizontalLayout: labelStyle === "left", loading: fieldState.optionsLoading, disabledOptionsWithReasons: disabledOptionsWithReasons }) }))] }));
209
+ return ((0, jsx_runtime_1.jsxs)("div", { css: Css_1.Css.df.fdc.w100.maxw((0, Css_1.px)(550)).if(labelStyle === "left").maxw100.$, ref: comboBoxRef, children: [(0, jsx_runtime_1.jsx)(ComboBoxInput_1.ComboBoxInput, { ...otherProps, buttonProps: buttonProps, buttonRef: triggerRef, inputProps: inputProps, inputRef: inputRef, inputWrapRef: inputWrapRef, listBoxRef: listBoxRef, state: state, labelProps: labelProps, selectedOptions: selectedOptions, getOptionValue: getOptionValue, getOptionLabel: getOptionLabel, contrast: contrast, nothingSelectedText: nothingSelectedText, borderless: borderless, tooltip: (0, components_1.resolveTooltip)(disabled, undefined, readOnly), resetField: resetField }), state.isOpen && ((0, jsx_runtime_1.jsx)(internal_1.Popover, { triggerRef: triggerRef, popoverRef: popoverRef, positionProps: positionProps, onClose: () => state.close(), isOpen: state.isOpen, minWidth: 200, children: (0, jsx_runtime_1.jsx)(ListBox_1.ListBox, { ...listBoxProps, positionProps: positionProps, state: state, listBoxRef: listBoxRef, selectedOptions: selectedOptions, getOptionLabel: getOptionLabel, getOptionValue: (o) => (0, Value_1.valueToKey)(getOptionValue(o)), contrast: contrast, horizontalLayout: labelStyle === "left", loading: fieldState.optionsLoading, disabledOptionsWithReasons: disabledOptionsWithReasons }) }))] }));
255
210
  }
256
211
  exports.ComboBoxBase = ComboBoxBase;
257
212
  function getInputValue(selectedOptions, getOptionLabel, multiselect, nothingSelectedText) {
@@ -298,6 +253,3 @@ function disabledOptionToKeyedTuple(disabledOption) {
298
253
  }
299
254
  }
300
255
  exports.disabledOptionToKeyedTuple = disabledOptionToKeyedTuple;
301
- function asArray(arrayOrElement) {
302
- return Array.isArray(arrayOrElement) ? arrayOrElement : arrayOrElement ? [arrayOrElement] : [];
303
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/beam",
3
- "version": "2.320.1",
3
+ "version": "2.321.1",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",