@homebound/beam 2.365.1 → 2.366.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.
|
@@ -2,7 +2,7 @@ import { ReactNode } from "react";
|
|
|
2
2
|
import { Value } from "./";
|
|
3
3
|
import { ComboBoxBaseProps } from "./internal/ComboBoxBase";
|
|
4
4
|
import { HasIdAndName, Optional } from "../types";
|
|
5
|
-
export interface MultiSelectFieldProps<O, V extends Value> extends Exclude<ComboBoxBaseProps<O, V>, "unsetLabel"> {
|
|
5
|
+
export interface MultiSelectFieldProps<O, V extends Value> extends Exclude<ComboBoxBaseProps<O, V>, "unsetLabel" | "addNew"> {
|
|
6
6
|
/** Renders `opt` in the dropdown menu, defaults to the `getOptionLabel` prop. */
|
|
7
7
|
getOptionMenuLabel?: (opt: O) => string | ReactNode;
|
|
8
8
|
getOptionValue: (opt: O) => V;
|
|
@@ -5,7 +5,7 @@ import { BeamFocusableProps } from "../../interfaces";
|
|
|
5
5
|
/** Base props for either `SelectField` or `MultiSelectField`. */
|
|
6
6
|
export interface ComboBoxBaseProps<O, V extends Value> extends BeamFocusableProps, PresentationFieldProps {
|
|
7
7
|
/** Renders `opt` in the dropdown menu, defaults to the `getOptionLabel` prop. `isUnsetOpt` is only defined for single SelectField */
|
|
8
|
-
getOptionMenuLabel?: (opt: O, isUnsetOpt?: boolean) => string | ReactNode;
|
|
8
|
+
getOptionMenuLabel?: (opt: O, isUnsetOpt?: boolean, isAddNewOption?: boolean) => string | ReactNode;
|
|
9
9
|
getOptionValue: (opt: O) => V;
|
|
10
10
|
getOptionLabel: (opt: O) => string;
|
|
11
11
|
/** The current value; it can be `undefined`, even if `V` cannot be. */
|
|
@@ -53,6 +53,7 @@ export interface ComboBoxBaseProps<O, V extends Value> extends BeamFocusableProp
|
|
|
53
53
|
hideErrorMessage?: boolean;
|
|
54
54
|
multiline?: boolean;
|
|
55
55
|
onSearch?: (search: string) => void;
|
|
56
|
+
onAddNew?: (v: string) => void;
|
|
56
57
|
}
|
|
57
58
|
/**
|
|
58
59
|
* Provides a non-native select/dropdown widget that allows the user to type to filter the options.
|
|
@@ -74,9 +75,13 @@ export type OptionsOrLoad<O> = O[] | {
|
|
|
74
75
|
options: O[] | undefined;
|
|
75
76
|
};
|
|
76
77
|
/** Transforms/simplifies `optionsOrLoad` into just options, with unsetLabel maybe added. */
|
|
77
|
-
export declare function initializeOptions<O, V extends Value>(optionsOrLoad: OptionsOrLoad<O>, getOptionValue: (opt: O) => V, unsetLabel: string | undefined): O[];
|
|
78
|
+
export declare function initializeOptions<O, V extends Value>(optionsOrLoad: OptionsOrLoad<O>, getOptionValue: (opt: O) => V, unsetLabel: string | undefined, addNew: boolean): O[];
|
|
78
79
|
/** A marker option to automatically add an "Unset" option to the start of options. */
|
|
79
80
|
export declare const unsetOption: {};
|
|
81
|
+
export declare const addNewOption: {
|
|
82
|
+
id: string;
|
|
83
|
+
name: string;
|
|
84
|
+
};
|
|
80
85
|
export declare function disabledOptionToKeyedTuple(disabledOption: Value | {
|
|
81
86
|
value: Value;
|
|
82
87
|
reason: string;
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.disabledOptionToKeyedTuple = exports.unsetOption = exports.initializeOptions = exports.ComboBoxBase = void 0;
|
|
6
|
+
exports.disabledOptionToKeyedTuple = exports.addNewOption = exports.unsetOption = exports.initializeOptions = exports.ComboBoxBase = void 0;
|
|
7
7
|
const jsx_runtime_1 = require("@emotion/react/jsx-runtime");
|
|
8
8
|
const react_1 = require("react");
|
|
9
9
|
const react_aria_1 = require("react-aria");
|
|
@@ -30,27 +30,39 @@ const fast_deep_equal_1 = __importDefault(require("fast-deep-equal"));
|
|
|
30
30
|
function ComboBoxBase(props) {
|
|
31
31
|
var _a, _b, _c, _d, _e;
|
|
32
32
|
const { fieldProps } = (0, PresentationContext_1.usePresentationContext)();
|
|
33
|
-
const { disabled, readOnly, onSelect, options: propOptions, multiselect = false, values: propValues, nothingSelectedText = "", contrast, disabledOptions, borderless, unsetLabel, getOptionLabel: propOptionLabel, getOptionValue: propOptionValue, getOptionMenuLabel: propOptionMenuLabel, fullWidth = (_a = fieldProps === null || fieldProps === void 0 ? void 0 : fieldProps.fullWidth) !== null && _a !== void 0 ? _a : false, onSearch, ...otherProps } = props;
|
|
33
|
+
const { disabled, readOnly, onSelect, options: propOptions, multiselect = false, values: propValues, nothingSelectedText = "", contrast, disabledOptions, borderless, unsetLabel, getOptionLabel: propOptionLabel, getOptionValue: propOptionValue, getOptionMenuLabel: propOptionMenuLabel, fullWidth = (_a = fieldProps === null || fieldProps === void 0 ? void 0 : fieldProps.fullWidth) !== null && _a !== void 0 ? _a : false, onSearch, onAddNew, ...otherProps } = props;
|
|
34
34
|
const labelStyle = (_c = (_b = otherProps.labelStyle) !== null && _b !== void 0 ? _b : fieldProps === null || fieldProps === void 0 ? void 0 : fieldProps.labelStyle) !== null && _c !== void 0 ? _c : "above";
|
|
35
35
|
// Memoize the callback functions and handle the `unset` option if provided.
|
|
36
|
-
const getOptionLabel = (0, react_1.useCallback)((o) =>
|
|
36
|
+
const getOptionLabel = (0, react_1.useCallback)((o) => unsetLabel && o === exports.unsetOption
|
|
37
|
+
? unsetLabel
|
|
38
|
+
: onAddNew && o === exports.addNewOption
|
|
39
|
+
? exports.addNewOption.name
|
|
40
|
+
: propOptionLabel(o),
|
|
37
41
|
// propOptionLabel is basically always a lambda, so don't dep on it
|
|
38
42
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
39
43
|
[unsetLabel]);
|
|
40
|
-
const getOptionValue = (0, react_1.useCallback)((o) =>
|
|
44
|
+
const getOptionValue = (0, react_1.useCallback)((o) => unsetLabel && o === exports.unsetOption
|
|
45
|
+
? undefined
|
|
46
|
+
: onAddNew && o === exports.addNewOption
|
|
47
|
+
? exports.addNewOption.id
|
|
48
|
+
: propOptionValue(o),
|
|
41
49
|
// propOptionValue is basically always a lambda, so don't dep on it
|
|
42
50
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
43
51
|
[unsetLabel]);
|
|
44
|
-
const getOptionMenuLabel = (0, react_1.useCallback)((o) => propOptionMenuLabel
|
|
52
|
+
const getOptionMenuLabel = (0, react_1.useCallback)((o) => propOptionMenuLabel
|
|
53
|
+
? propOptionMenuLabel(o, Boolean(unsetLabel) && o === exports.unsetOption, Boolean(onAddNew) && o === exports.addNewOption)
|
|
54
|
+
: getOptionLabel(o),
|
|
45
55
|
// propOptionMenuLabel is basically always a lambda, so don't dep on it
|
|
46
56
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
47
57
|
[unsetLabel, getOptionLabel]);
|
|
48
58
|
// Call `initializeOptions` to prepend the `unset` option if the `unsetLabel` was provided.
|
|
49
|
-
const options = (0, react_1.useMemo)(() => initializeOptions(propOptions, getOptionValue, unsetLabel),
|
|
59
|
+
const options = (0, react_1.useMemo)(() => initializeOptions(propOptions, getOptionValue, unsetLabel, !!onAddNew),
|
|
50
60
|
// If the caller is using { current, load, options }, memoize on only `current` and `options` values.
|
|
51
61
|
// ...and don't bother on memoizing on getOptionValue b/c it's basically always a lambda
|
|
52
62
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
53
|
-
Array.isArray(propOptions)
|
|
63
|
+
Array.isArray(propOptions)
|
|
64
|
+
? [propOptions, unsetLabel, onAddNew]
|
|
65
|
+
: [propOptions.current, propOptions.options, unsetLabel, onAddNew]);
|
|
54
66
|
const values = (0, react_1.useMemo)(() => propValues !== null && propValues !== void 0 ? propValues : [], [propValues]);
|
|
55
67
|
const selectedOptionsRef = (0, react_1.useRef)(options.filter((o) => values.includes(getOptionValue(o))));
|
|
56
68
|
const selectedOptions = (0, react_1.useMemo)(() => {
|
|
@@ -77,7 +89,9 @@ function ComboBoxBase(props) {
|
|
|
77
89
|
});
|
|
78
90
|
const { searchValue } = fieldState;
|
|
79
91
|
const filteredOptions = (0, react_1.useMemo)(() => {
|
|
80
|
-
return !searchValue
|
|
92
|
+
return !searchValue
|
|
93
|
+
? options
|
|
94
|
+
: options.filter((o) => contains(getOptionLabel(o), searchValue) || o === exports.addNewOption);
|
|
81
95
|
}, [options, searchValue, getOptionLabel, contains]);
|
|
82
96
|
/** Resets field's input value and filtered options list for cases where the user exits the field without making changes (on Escape, or onBlur) */
|
|
83
97
|
function resetField() {
|
|
@@ -99,6 +113,11 @@ function ComboBoxBase(props) {
|
|
|
99
113
|
}
|
|
100
114
|
const selectedKeys = [...keys.values()];
|
|
101
115
|
const selectedOptions = options.filter((o) => selectedKeys.includes((0, Value_1.valueToKey)(getOptionValue(o))));
|
|
116
|
+
if (!multiselect && selectedOptions[0] === exports.addNewOption && onAddNew) {
|
|
117
|
+
onAddNew(fieldState.inputValue);
|
|
118
|
+
state.close();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
102
121
|
selectionChanged && onSelect(selectedKeys.map(Value_1.keyToValue), selectedOptions);
|
|
103
122
|
if (!multiselect) {
|
|
104
123
|
// Close menu upon selection change only for Single selection mode
|
|
@@ -243,7 +262,7 @@ function getInputValue(selectedOptions, getOptionLabel, multiselect, nothingSele
|
|
|
243
262
|
: "";
|
|
244
263
|
}
|
|
245
264
|
/** Transforms/simplifies `optionsOrLoad` into just options, with unsetLabel maybe added. */
|
|
246
|
-
function initializeOptions(optionsOrLoad, getOptionValue, unsetLabel) {
|
|
265
|
+
function initializeOptions(optionsOrLoad, getOptionValue, unsetLabel, addNew) {
|
|
247
266
|
const opts = [];
|
|
248
267
|
if (unsetLabel) {
|
|
249
268
|
opts.push(exports.unsetOption);
|
|
@@ -268,11 +287,15 @@ function initializeOptions(optionsOrLoad, getOptionValue, unsetLabel) {
|
|
|
268
287
|
});
|
|
269
288
|
}
|
|
270
289
|
}
|
|
290
|
+
if (addNew) {
|
|
291
|
+
opts.push(exports.addNewOption);
|
|
292
|
+
}
|
|
271
293
|
return opts;
|
|
272
294
|
}
|
|
273
295
|
exports.initializeOptions = initializeOptions;
|
|
274
296
|
/** A marker option to automatically add an "Unset" option to the start of options. */
|
|
275
297
|
exports.unsetOption = {};
|
|
298
|
+
exports.addNewOption = { id: "new", name: "Add New" };
|
|
276
299
|
function disabledOptionToKeyedTuple(disabledOption) {
|
|
277
300
|
if (typeof disabledOption === "object" && disabledOption !== null) {
|
|
278
301
|
return [(0, Value_1.valueToKey)(disabledOption.value), disabledOption.reason];
|