@trackunit/react-form-components 0.0.305 → 0.0.308
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/index.cjs.js +918 -919
- package/index.esm.js +918 -920
- package/package.json +1 -1
- package/src/components/Select/CreatableSelect.d.ts +32 -31
- package/src/components/Select/Select.d.ts +2 -2
- package/src/components/Select/useSelect.d.ts +2 -2
- package/src/components/SelectField/CreatableSelectField.d.ts +14 -0
- package/src/components/SelectField/FormFieldSelectAdapter.d.ts +44 -0
- package/src/components/SelectField/SelectField.d.ts +13 -0
- package/src/index.d.ts +2 -1
- package/src/SelectField/SelectField.d.ts +0 -50
package/index.esm.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { useNamespaceTranslation, registerTranslations } from '@trackunit/i18n-library-translation';
|
|
3
|
+
import { IconButton, Icon, Tooltip, Heading, Text, MenuItem, Tag, Spinner } from '@trackunit/react-components';
|
|
3
4
|
import * as React from 'react';
|
|
4
|
-
import React__default, {
|
|
5
|
+
import React__default, { useMemo, forwardRef, useCallback, cloneElement, useState, useEffect, useRef, useImperativeHandle } from 'react';
|
|
6
|
+
import { useCopyToClipboard } from 'react-use';
|
|
5
7
|
import { v4 } from 'uuid';
|
|
6
|
-
import { Tooltip, Text, MenuItem, Icon, Tag, Spinner, IconButton, Heading } from '@trackunit/react-components';
|
|
7
8
|
import { cvaMerge } from '@trackunit/css-class-variance-utilities';
|
|
9
|
+
import { format } from 'date-fns';
|
|
10
|
+
import parsePhoneNumberFromString, { isSupportedCountry, getCountries, getCountryCallingCode, isValidPhoneNumber, parsePhoneNumberFromString as parsePhoneNumberFromString$1 } from 'libphonenumber-js';
|
|
8
11
|
import ReactSelect, { components } from 'react-select';
|
|
9
12
|
export { default as ValueType } from 'react-select';
|
|
10
13
|
import ReactAsyncCreatableSelect from 'react-select/async-creatable';
|
|
11
14
|
import ReactCreatableSelect from 'react-select/creatable';
|
|
12
15
|
import ReactAsyncSelect from 'react-select/async';
|
|
13
|
-
import { useCopyToClipboard } from 'react-use';
|
|
14
|
-
import { format } from 'date-fns';
|
|
15
|
-
import parsePhoneNumberFromString, { isSupportedCountry, getCountries, getCountryCallingCode, isValidPhoneNumber, parsePhoneNumberFromString as parsePhoneNumberFromString$1 } from 'libphonenumber-js';
|
|
16
16
|
|
|
17
17
|
var defaultTranslations = {
|
|
18
18
|
"clearIndicator.icon.tooltip.clearAll": "Clear all",
|
|
@@ -69,6 +69,65 @@ const setupLibraryTranslations = () => {
|
|
|
69
69
|
registerTranslations(translations);
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
+
const cvaActionButton = cvaMerge(["drop-shadow-none", "rounded-md"], {
|
|
73
|
+
variants: {
|
|
74
|
+
size: {
|
|
75
|
+
small: ["w-6", "h-6"],
|
|
76
|
+
medium: ["w-8", "h-8"],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
defaultVariants: {
|
|
80
|
+
size: "medium",
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
const cvaActionContainer = cvaMerge(["flex", "items-center", "m-1"]);
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The ActionButton component is a wrapper over IconButton to perform an action when the onClick event is triggered.
|
|
87
|
+
*
|
|
88
|
+
* @param {ActionButtonProps} props - The props for the ActionButton component
|
|
89
|
+
* @returns {JSX.Element} ActionButton component
|
|
90
|
+
*/
|
|
91
|
+
const ActionButton = ({ type, value, dataTestId, iconSize, disabled, key, className }) => {
|
|
92
|
+
const [, copyToClipboard] = useCopyToClipboard();
|
|
93
|
+
const elementKey = useMemo(() => key !== null && key !== void 0 ? key : `ActionButton-${v4()}`, [key]);
|
|
94
|
+
const getIconName = () => {
|
|
95
|
+
switch (type) {
|
|
96
|
+
case "PHONE_NUMBER":
|
|
97
|
+
return "PhoneArrowUpRight";
|
|
98
|
+
case "WEB_ADDRESS":
|
|
99
|
+
return "ArrowTopRightOnSquare";
|
|
100
|
+
case "EMAIL":
|
|
101
|
+
return "Envelope";
|
|
102
|
+
case "COPY":
|
|
103
|
+
default:
|
|
104
|
+
return "ClipboardDocument";
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const ButtonAction = () => {
|
|
108
|
+
var _a, _b;
|
|
109
|
+
switch (type) {
|
|
110
|
+
case "EMAIL":
|
|
111
|
+
return window.open(`mailto:${value}`);
|
|
112
|
+
case "WEB_ADDRESS":
|
|
113
|
+
if (value) {
|
|
114
|
+
return window.open(value, "_blank", "noopener,noreferrer");
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
case "PHONE_NUMBER":
|
|
118
|
+
return window.open(`tel:${value}`);
|
|
119
|
+
case "COPY":
|
|
120
|
+
return copyToClipboard((_b = (_a = value === null || value === void 0 ? void 0 : value.current) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "");
|
|
121
|
+
default:
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
if (ButtonAction === null) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
return (jsx("div", { className: cvaActionContainer({ className }), children: jsx(IconButton, { className: cvaActionButton({ size: iconSize }), dataTestId: dataTestId || "testIconButtonId", disabled: disabled, icon: jsx(Icon, { name: getIconName(), size: "small" }), onClick: ButtonAction, size: "small", variant: "secondary" }) }, elementKey));
|
|
129
|
+
};
|
|
130
|
+
|
|
72
131
|
/******************************************************************************
|
|
73
132
|
Copyright (c) Microsoft Corporation.
|
|
74
133
|
|
|
@@ -106,95 +165,137 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
106
165
|
});
|
|
107
166
|
}
|
|
108
167
|
|
|
109
|
-
const
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
|
|
168
|
+
const cvaInputBase = cvaMerge([
|
|
169
|
+
"component-baseInput-shadow",
|
|
170
|
+
"component-baseInput-border",
|
|
171
|
+
"component-baseInput-background",
|
|
172
|
+
"border-slate-300",
|
|
173
|
+
"focus-within:ring-2",
|
|
174
|
+
"focus-within:ring-inset",
|
|
175
|
+
"focus-within:ring-primary-500",
|
|
176
|
+
"focus-within:border-slate-400",
|
|
177
|
+
"hover:border-slate-400",
|
|
178
|
+
"hover:bg-slate-50",
|
|
179
|
+
"transition",
|
|
180
|
+
]);
|
|
181
|
+
const cvaInputBaseDisabled = cvaMerge(["bg-slate-100", "hover:bg-slate-100", "hover:border-slate-300"]);
|
|
182
|
+
const cvaInputBaseInvalid = cvaMerge(["border-danger-600", "focus-within:ring-danger-600"]);
|
|
183
|
+
const cvaInput = cvaMerge(["relative", "flex", "h-10", cvaInputBase()], {
|
|
118
184
|
variants: {
|
|
185
|
+
size: {
|
|
186
|
+
small: ["h-8"],
|
|
187
|
+
medium: ["h-10"],
|
|
188
|
+
},
|
|
189
|
+
disabled: {
|
|
190
|
+
true: cvaInputBaseDisabled(),
|
|
191
|
+
false: "",
|
|
192
|
+
},
|
|
119
193
|
invalid: {
|
|
120
|
-
true:
|
|
194
|
+
true: cvaInputBaseInvalid(),
|
|
121
195
|
false: "",
|
|
122
196
|
},
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
const cvaInputField = cvaMerge([
|
|
200
|
+
"w-full",
|
|
201
|
+
"px-3",
|
|
202
|
+
"border-0",
|
|
203
|
+
"bg-transparent",
|
|
204
|
+
"text-base",
|
|
205
|
+
"text-slate-900",
|
|
206
|
+
"placeholder-slate-400",
|
|
207
|
+
"focus-visible:outline-none",
|
|
208
|
+
], {
|
|
209
|
+
variants: {
|
|
210
|
+
size: {
|
|
211
|
+
small: ["py-1"],
|
|
212
|
+
medium: ["py-2"],
|
|
213
|
+
},
|
|
123
214
|
disabled: {
|
|
124
|
-
true: "text-slate-
|
|
215
|
+
true: ["text-slate-700"],
|
|
125
216
|
false: "",
|
|
126
217
|
},
|
|
127
218
|
},
|
|
128
|
-
defaultVariants: {
|
|
129
|
-
invalid: false,
|
|
130
|
-
disabled: false,
|
|
131
|
-
},
|
|
132
219
|
});
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
220
|
+
const cvaInputAddon = cvaMerge([
|
|
221
|
+
"flex",
|
|
222
|
+
"justify-center",
|
|
223
|
+
"items-center",
|
|
224
|
+
"px-3",
|
|
225
|
+
"border-slate-300",
|
|
226
|
+
"text-slate-500",
|
|
227
|
+
"bg-slate-50",
|
|
228
|
+
]);
|
|
229
|
+
const cvaInputAddonBefore = cvaMerge([cvaInputAddon(), "border-r", "rounded-l-lg"]);
|
|
230
|
+
const cvaInputAddonAfter = cvaMerge([cvaInputAddon(), "border-l", "rounded-r-lg"]);
|
|
231
|
+
const cvaInputPrefix = cvaMerge([
|
|
232
|
+
"flex",
|
|
233
|
+
"justify-center",
|
|
234
|
+
"items-center",
|
|
235
|
+
"text-slate-400",
|
|
236
|
+
"hover:component-search-prefix",
|
|
237
|
+
"component-baseInput-prefix",
|
|
238
|
+
"component-search-borderless",
|
|
239
|
+
"px-3",
|
|
240
|
+
], {
|
|
146
241
|
variants: {
|
|
147
242
|
disabled: {
|
|
148
|
-
true: "
|
|
243
|
+
true: ["text-slate-500"],
|
|
149
244
|
false: "",
|
|
150
245
|
},
|
|
151
246
|
},
|
|
152
247
|
});
|
|
153
|
-
const
|
|
154
|
-
const cvaFormGroupContainerAfter = cvaMerge(["flex", "justify-between", "mt-1", "text-xs", "text-slate-500"], {
|
|
248
|
+
const cvaInputSuffix = cvaMerge(["flex", "justify-center", "items-center", "text-slate-400", "px-3"], {
|
|
155
249
|
variants: {
|
|
156
|
-
|
|
157
|
-
true: "text-
|
|
250
|
+
disabled: {
|
|
251
|
+
true: ["text-slate-500"],
|
|
158
252
|
false: "",
|
|
159
253
|
},
|
|
160
254
|
},
|
|
161
255
|
});
|
|
162
|
-
const cvaHelpAddon = cvaMerge(["ml-auto"]);
|
|
163
256
|
|
|
164
257
|
/**
|
|
165
|
-
*
|
|
166
|
-
*
|
|
258
|
+
* A base input component that can be used for text inputs, password inputs, etc.
|
|
259
|
+
* A reference to the input element is provided as the `ref` prop.
|
|
260
|
+
* Extends props from [React.InputHTMLAttributes](https://reactjs.org/docs/dom-elements.html#input)
|
|
167
261
|
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
262
|
+
* For specific input types make sure to use the corresponding input component.
|
|
263
|
+
* This is a base used by our other input components such as TextInput, NumberInput, PasswordInput, etc.
|
|
170
264
|
*/
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
265
|
+
const BaseInput = React.forwardRef((_a, ref) => {
|
|
266
|
+
var { className, isInvalid, dataTestId, prefix, suffix, addonBefore, addonAfter, actions, fieldSize = "medium", nonInteractive = false, showDefaultActions = false, type, inputClassName, addonBeforeClassName } = _a, rest = __rest(_a, ["className", "isInvalid", "dataTestId", "prefix", "suffix", "addonBefore", "addonAfter", "actions", "fieldSize", "nonInteractive", "showDefaultActions", "type", "inputClassName", "addonBeforeClassName"]);
|
|
267
|
+
const isReadOnlyWithPlaceholder = useMemo(() => !!rest.readOnly && !!rest.placeholder && !rest.value, [rest.readOnly, rest.placeholder, rest.value]);
|
|
268
|
+
const renderAsDisabled = rest.disabled || rest.readOnly;
|
|
269
|
+
const innerRef = React.useRef(null);
|
|
270
|
+
React.useImperativeHandle(ref, () => innerRef.current, []);
|
|
271
|
+
return (jsxs("div", { className: cvaInput({
|
|
272
|
+
disabled: renderAsDisabled,
|
|
273
|
+
invalid: isInvalid,
|
|
274
|
+
size: fieldSize,
|
|
275
|
+
className,
|
|
276
|
+
}), "data-testid": dataTestId && `${dataTestId}-container`, children: [addonBefore && addonBefore !== undefined ? (jsx("div", { className: cvaInputAddonBefore({ className: addonBeforeClassName }), "data-testid": dataTestId && `${dataTestId}-addonBefore`, children: addonBefore })) : null, prefix && addonBefore !== prefix ? (jsx("div", { className: cvaInputPrefix({
|
|
277
|
+
disabled: renderAsDisabled,
|
|
278
|
+
}), "data-testid": dataTestId && `${dataTestId}-prefix`, children: prefix })) : null, jsx("input", Object.assign({ className: cvaInputField({ size: fieldSize, disabled: renderAsDisabled, className: inputClassName }), "data-testid": dataTestId, ref: innerRef, type: isReadOnlyWithPlaceholder ? "text" : type }, rest, { readOnly: rest.readOnly || nonInteractive })), suffix && addonBefore !== suffix ? (jsx("div", { className: cvaInputSuffix({
|
|
279
|
+
disabled: renderAsDisabled,
|
|
280
|
+
}), "data-testid": dataTestId && `${dataTestId}-suffix`, children: suffix })) : null, rest.readOnly === true && showDefaultActions ? (jsx(ActionButton, { type: "COPY", value: innerRef }, "default-copy-action")) : null, actions && addonBefore !== actions ? actions : null, addonAfter && addonAfter !== undefined && addonBefore !== addonAfter ? (jsx("div", { className: cvaInputAddonAfter(), "data-testid": dataTestId && `${dataTestId}-addonAfter`, children: addonAfter })) : null] }));
|
|
281
|
+
});
|
|
174
282
|
|
|
175
|
-
const
|
|
176
|
-
"
|
|
177
|
-
"
|
|
178
|
-
"
|
|
179
|
-
"
|
|
180
|
-
"
|
|
181
|
-
"
|
|
182
|
-
"
|
|
183
|
-
"focus-within:ring-inset",
|
|
184
|
-
"focus-within:ring-primary-600",
|
|
185
|
-
"focus-within:border-slate-400",
|
|
186
|
-
"hover:border-slate-400",
|
|
187
|
-
"hover:bg-slate-50",
|
|
188
|
-
"bg-white",
|
|
189
|
-
"transition",
|
|
283
|
+
const cvaLabel = cvaMerge([
|
|
284
|
+
"text-base",
|
|
285
|
+
"text-slate-700",
|
|
286
|
+
"truncate",
|
|
287
|
+
"hover:text-slate-800",
|
|
288
|
+
"group-hover:text-slate-800",
|
|
289
|
+
"active:text-slate-800",
|
|
290
|
+
"group-active:text-slate-800",
|
|
190
291
|
], {
|
|
191
292
|
variants: {
|
|
192
293
|
invalid: {
|
|
193
|
-
true: "
|
|
294
|
+
true: "text-red-600 hover:text-red-700 active:text-red-700 group-hover:text-red-700 group-active:text-red-700",
|
|
194
295
|
false: "",
|
|
195
296
|
},
|
|
196
297
|
disabled: {
|
|
197
|
-
true: "
|
|
298
|
+
true: "text-slate-400 hover:text-slate-400 group-hover:text-slate-400",
|
|
198
299
|
false: "",
|
|
199
300
|
},
|
|
200
301
|
},
|
|
@@ -203,94 +304,15 @@ const cvaSelect = cvaMerge([
|
|
|
203
304
|
disabled: false,
|
|
204
305
|
},
|
|
205
306
|
});
|
|
206
|
-
const cvaSelectIcon = cvaMerge("mr-2 flex cursor-pointer items-center justify-center text-slate-400 hover:text-slate-500");
|
|
207
|
-
const cvaSelectPrefix = cvaMerge(["flex", "justify-center", "items-center", "text-slate-400", "pl-2"]);
|
|
208
|
-
const cvaSelectXIcon = cvaMerge([
|
|
209
|
-
"mr-2 flex cursor-pointer items-center justify-center text-slate-400 hover:text-slate-500",
|
|
210
|
-
"ml-1",
|
|
211
|
-
]);
|
|
212
|
-
const cvaSelectMenuList = cvaMerge(["min-w-min", "shadow-md", "rounded-lg", "z-20", "bg-white", "p-1", "border", "border-slate-300", "gap-1", "grid"], {
|
|
213
|
-
variants: {
|
|
214
|
-
menuIsOpen: {
|
|
215
|
-
true: "animate-fade-in-fast",
|
|
216
|
-
false: "animate-fade-out-fast",
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
});
|
|
220
|
-
const cvaSelectDynamicTagContainer = cvaMerge(["h-full", "flex", "gap-1", "items-center"], {
|
|
221
|
-
variants: {
|
|
222
|
-
visible: { true: "visible", false: "invisible" },
|
|
223
|
-
},
|
|
224
|
-
});
|
|
225
|
-
const cvaSelectCounter = cvaMerge(["overflow-hidden", "whitespace-nowrap"]);
|
|
226
|
-
const cvaSelectMenu = cvaMerge(["relative", "p-1", "grid", "gap-1"]);
|
|
227
307
|
|
|
228
308
|
/**
|
|
229
|
-
* The
|
|
309
|
+
* The CheckIcon component is used by the checkbox to display the checked state.
|
|
230
310
|
*
|
|
231
|
-
* @param {
|
|
232
|
-
* @returns {JSX.Element}
|
|
311
|
+
* @param {CheckIconProps} props - The props for the CheckIcon component
|
|
312
|
+
* @returns {JSX.Element} CheckIcon component
|
|
233
313
|
*/
|
|
234
|
-
const
|
|
235
|
-
return (
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* @param {MultiValue<Option> | SingleValue<Option>} arg option to check type
|
|
240
|
-
* @returns {arg is MultiValue<Option> } is Multivalue
|
|
241
|
-
*/
|
|
242
|
-
function isMultiValue(arg) {
|
|
243
|
-
return Array.isArray(arg);
|
|
244
|
-
}
|
|
245
|
-
function isGroupBase(arg) {
|
|
246
|
-
return arg.options !== undefined;
|
|
247
|
-
}
|
|
248
|
-
const isSelectedOption = (option, selected) => {
|
|
249
|
-
if (isGroupBase(option)) {
|
|
250
|
-
return false;
|
|
251
|
-
}
|
|
252
|
-
return isMultiValue(selected)
|
|
253
|
-
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
254
|
-
selected.some(v => v.value === option.value)
|
|
255
|
-
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
256
|
-
option.value === (selected === null || selected === void 0 ? void 0 : selected.value);
|
|
257
|
-
};
|
|
258
|
-
const removeSelectedFromGroups = (group, selected) => {
|
|
259
|
-
if (isGroupBase(group)) {
|
|
260
|
-
return Object.assign(Object.assign({}, group), { options: group.options.filter(option => !isSelectedOption(option, selected)) });
|
|
261
|
-
}
|
|
262
|
-
return group;
|
|
263
|
-
};
|
|
264
|
-
/**
|
|
265
|
-
* @template IsMulti
|
|
266
|
-
* @template Group
|
|
267
|
-
* @param {OptionsOrGroups<Option, Group> | undefined} options An array of options to select from
|
|
268
|
-
* @param {PropsValue<Option> | undefined} value Selected values
|
|
269
|
-
* @returns {OptionsOrGroups<Option, Group>} An array of ordered options with selected on top
|
|
270
|
-
*/
|
|
271
|
-
const getOrderedOptions = (options, value) => {
|
|
272
|
-
if (value && options) {
|
|
273
|
-
const orderedValues = isMultiValue(value)
|
|
274
|
-
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
275
|
-
[...value].sort((a, b) => a.label.localeCompare(b.label))
|
|
276
|
-
: [value];
|
|
277
|
-
const selectableOptions = options
|
|
278
|
-
.filter(option => !isSelectedOption(option, value))
|
|
279
|
-
.map(option => removeSelectedFromGroups(option, value));
|
|
280
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
281
|
-
return orderedValues.concat(selectableOptions) || [];
|
|
282
|
-
}
|
|
283
|
-
return options || [];
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* The CheckIcon component is used by the checkbox to display the checked state.
|
|
288
|
-
*
|
|
289
|
-
* @param {CheckIconProps} props - The props for the CheckIcon component
|
|
290
|
-
* @returns {JSX.Element} CheckIcon component
|
|
291
|
-
*/
|
|
292
|
-
const CheckIcon = ({ className, dataTestId }) => {
|
|
293
|
-
return (jsx("svg", { className: className, "data-testid": dataTestId, fill: "none", stroke: "currentColor", strokeWidth: "2", viewBox: "0 0 10 10", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { d: "m2 5.5 2 2L8.5 3", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
314
|
+
const CheckIcon = ({ className, dataTestId }) => {
|
|
315
|
+
return (jsx("svg", { className: className, "data-testid": dataTestId, fill: "none", stroke: "currentColor", strokeWidth: "2", viewBox: "0 0 10 10", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { d: "m2 5.5 2 2L8.5 3", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
294
316
|
};
|
|
295
317
|
|
|
296
318
|
const cvaCheckbox = cvaMerge([
|
|
@@ -471,662 +493,117 @@ const Checkbox = React.forwardRef((_a, ref) => {
|
|
|
471
493
|
});
|
|
472
494
|
|
|
473
495
|
/**
|
|
474
|
-
* A
|
|
496
|
+
* A wrapper around BaseInput with a pop-up day picker.
|
|
475
497
|
*
|
|
476
|
-
*
|
|
477
|
-
* @returns {JSX.Element} SingleSelectMenuItem
|
|
498
|
+
* NOTE: If shown with a label, please use the `DateField` component instead.
|
|
478
499
|
*/
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
500
|
+
const DateInput = forwardRef((_a, ref) => {
|
|
501
|
+
var { min, max, defaultValue, value } = _a, rest = __rest(_a, ["min", "max", "defaultValue", "value"]);
|
|
502
|
+
const formatDateToInputString = (date) => date instanceof Date ? format(date, "yyyy-MM-dd") : date;
|
|
503
|
+
// Chrome and Firefox need their default icon to have datepicker functionality.
|
|
504
|
+
const showIcon = !/Chrome/.test(navigator.userAgent) && !/Firefox/.test(navigator.userAgent);
|
|
505
|
+
return (jsx(BaseInput, Object.assign({ defaultValue: formatDateToInputString(defaultValue), max: formatDateToInputString(max), min: formatDateToInputString(min), ref: ref, suffix: showIcon && jsx(Icon, { dataTestId: "calendar", name: "Calendar", size: "medium", type: "solid" }), type: "date", value: formatDateToInputString(value) }, rest)));
|
|
506
|
+
});
|
|
507
|
+
|
|
482
508
|
/**
|
|
483
|
-
*
|
|
509
|
+
* The Label component is used for labels for input fields.
|
|
510
|
+
* This component is **not used directly**, but is part of the FormGroup and Field components.
|
|
484
511
|
*
|
|
485
|
-
* @param {
|
|
486
|
-
* @returns {JSX.Element}
|
|
512
|
+
* @param {LabelProps} props - The props for the Label component
|
|
513
|
+
* @returns {JSX.Element} Label component
|
|
487
514
|
*/
|
|
488
|
-
const
|
|
489
|
-
return (jsx(
|
|
490
|
-
e.stopPropagation();
|
|
491
|
-
onClick && onClick(e);
|
|
492
|
-
}, prefix: jsx(Checkbox, { checked: selected, disabled: disabled, onChange: () => null, onClick: e => {
|
|
493
|
-
e.stopPropagation();
|
|
494
|
-
}, readOnly: false }), selected: selected }));
|
|
515
|
+
const Label = ({ id, htmlFor, children, className, dataTestId, disabled, isInvalid, }) => {
|
|
516
|
+
return (jsx("label", { className: cvaLabel({ invalid: isInvalid, disabled, className }), "data-testid": dataTestId, htmlFor: htmlFor || "", id: id || "", children: children }));
|
|
495
517
|
};
|
|
496
518
|
|
|
519
|
+
const cvaFormGroup = cvaMerge(["component-formGroup-gap"], {
|
|
520
|
+
variants: {
|
|
521
|
+
disabled: {
|
|
522
|
+
true: "pointer-events-none",
|
|
523
|
+
false: "",
|
|
524
|
+
},
|
|
525
|
+
},
|
|
526
|
+
});
|
|
527
|
+
const cvaFormGroupContainerBefore = cvaMerge(["flex", "gap-1", "mb-1"]);
|
|
528
|
+
const cvaFormGroupContainerAfter = cvaMerge(["flex", "justify-between", "mt-1", "text-xs", "text-slate-500"], {
|
|
529
|
+
variants: {
|
|
530
|
+
invalid: {
|
|
531
|
+
true: "text-danger-500",
|
|
532
|
+
false: "",
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
});
|
|
536
|
+
const cvaHelpAddon = cvaMerge(["ml-auto"]);
|
|
537
|
+
|
|
497
538
|
/**
|
|
498
|
-
*
|
|
499
|
-
*
|
|
539
|
+
* The FormGroup component should be used to wrap any Input element that needs a label.
|
|
540
|
+
* Besides a label the component supplies an optional Tooltip, HelpText and HelpAddon support.
|
|
500
541
|
*
|
|
501
|
-
* @param {
|
|
502
|
-
* @returns {JSX.Element}
|
|
542
|
+
* @param {FormGroupProps} props - The props for the FormGroup component
|
|
543
|
+
* @returns {JSX.Element} FormGroup component
|
|
503
544
|
*/
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
const ref = React__default.useRef(null);
|
|
507
|
-
React__default.useLayoutEffect(() => {
|
|
508
|
-
var _a;
|
|
509
|
-
onWidthKnown && onWidthKnown({ width: ((_a = ref.current) === null || _a === void 0 ? void 0 : _a.offsetWidth) || 0 });
|
|
510
|
-
}, [ref, onWidthKnown]);
|
|
511
|
-
return (jsx(Tag, Object.assign({ ref: ref }, rest, { children: children })));
|
|
545
|
+
const FormGroup = ({ isInvalid, helpText, helpAddon, tip, className, dataTestId, label, htmlFor, children, tipIconProps, disabled = false, }) => {
|
|
546
|
+
return (jsxs("div", { className: cvaFormGroup({ disabled, className }), "data-testid": dataTestId, children: [jsxs("div", { className: cvaFormGroupContainerBefore(), children: [jsx(Label, { className: "component-formGroup-font", dataTestId: dataTestId && `${dataTestId}-label`, htmlFor: htmlFor, id: htmlFor + "-label", children: label }), tip && (jsx(Tooltip, { dataTestId: dataTestId && `${dataTestId}-tooltip`, iconProps: tipIconProps, label: tip, placement: "bottom" }))] }), children, jsxs("div", { className: cvaFormGroupContainerAfter({ invalid: isInvalid }), children: [helpText && jsx("span", { "data-testid": dataTestId && `${dataTestId}-helpText`, children: helpText }), helpAddon && (jsx("span", { className: cvaHelpAddon(), "data-testid": dataTestId && `${dataTestId}-helpAddon`, children: helpAddon }))] })] }));
|
|
512
547
|
};
|
|
513
548
|
|
|
514
549
|
/**
|
|
515
|
-
*
|
|
550
|
+
* The date field component is used for entering date values.
|
|
516
551
|
*
|
|
517
|
-
*
|
|
518
|
-
*
|
|
552
|
+
* _**Do use**_ the DateField for date input.
|
|
553
|
+
*
|
|
554
|
+
* _**Do not use**_ this fields for non-serialized dates. Use TextField instead.
|
|
519
555
|
*/
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
const
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
},
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
counter = counter + 1;
|
|
566
|
-
}
|
|
567
|
-
return null;
|
|
568
|
-
})
|
|
569
|
-
.filter(element => element !== null);
|
|
570
|
-
return (jsxs("div", { className: cvaSelectDynamicTagContainer({ visible: isReady }), ref: containerRef, style: {
|
|
571
|
-
width: `${width}`,
|
|
572
|
-
}, children: [renderedElements, postFix] }));
|
|
573
|
-
};
|
|
556
|
+
const DateField = forwardRef((_a, ref) => {
|
|
557
|
+
var { label, id, tip, helpText, errorMessage, helpAddon, isInvalid, className, defaultValue, dataTestId } = _a, rest = __rest(_a, ["label", "id", "tip", "helpText", "errorMessage", "helpAddon", "isInvalid", "className", "defaultValue", "dataTestId"]);
|
|
558
|
+
const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
|
|
559
|
+
const htmlForId = id ? id : "dateField-" + v4();
|
|
560
|
+
return (jsx(FormGroup, { dataTestId: dataTestId && `${dataTestId}-FormGroup`, disabled: rest.disabled, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, tip: tip, children: jsx(DateInput, Object.assign({ "aria-labelledby": htmlForId + "-label", defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, ref: ref }, rest, { className: className, dataTestId: dataTestId })) }));
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
const cvaDropZone = cvaMerge([
|
|
564
|
+
"flex",
|
|
565
|
+
"component-baseInput-background",
|
|
566
|
+
"justify-center",
|
|
567
|
+
"text-slate-500",
|
|
568
|
+
"rounded-sm",
|
|
569
|
+
"border-2",
|
|
570
|
+
"border-gray-200",
|
|
571
|
+
"border-dashed",
|
|
572
|
+
"hover:bg-slate-100",
|
|
573
|
+
"hover:border-solid",
|
|
574
|
+
"hover:border-primary-500",
|
|
575
|
+
], {
|
|
576
|
+
variants: {
|
|
577
|
+
size: {
|
|
578
|
+
small: ["p-2"],
|
|
579
|
+
medium: ["p-4"],
|
|
580
|
+
large: ["p-8"],
|
|
581
|
+
},
|
|
582
|
+
disabled: { true: ["bg-slate-100", "hover:bg-slate-100"], false: "" },
|
|
583
|
+
dragActive: { true: ["border-gray-200", "bg-slate-100"], false: "" },
|
|
584
|
+
dropComplete: {
|
|
585
|
+
true: ["border-solid", "border-primary-500", "bg-slate-100", "hover:border-solid", "hover:border-gray-200"],
|
|
586
|
+
false: "",
|
|
587
|
+
},
|
|
588
|
+
invalid: { true: ["border-danger-600", "text-danger-500"], false: "" },
|
|
589
|
+
},
|
|
590
|
+
});
|
|
591
|
+
const cvaDropZoneLabel = cvaMerge([
|
|
592
|
+
"h-full",
|
|
593
|
+
"pt-1",
|
|
594
|
+
"pb-1",
|
|
595
|
+
"gap-2",
|
|
596
|
+
"items-center",
|
|
597
|
+
"flex-col",
|
|
598
|
+
"flex",
|
|
599
|
+
"justify-center",
|
|
600
|
+
]);
|
|
574
601
|
|
|
575
602
|
/**
|
|
576
|
-
*
|
|
577
|
-
* This complex object includes all the compositional components that are used in react-select. If you wish to overwrite a component, pass in an object with the appropriate namespace.
|
|
603
|
+
* The Drop Zone can be used to drag and drop files
|
|
578
604
|
*
|
|
579
|
-
* @
|
|
580
|
-
* @
|
|
581
|
-
* @param {Partial<SelectComponents<Option, IsMulti, Group>> | undefined} componentsProps a custom component prop that you can to override defaults
|
|
582
|
-
* @param {boolean} disabled decide to override disabled variant
|
|
583
|
-
* @param {boolean} menuIsOpen menu is open state
|
|
584
|
-
* @param {React.MutableRefObject<boolean>} refMenuIsEnabled a flag to block menu from open
|
|
585
|
-
* @param {string} dataTestId a test id
|
|
586
|
-
* @param {number} maxSelectedDisplayCount a number of max display count
|
|
587
|
-
* @param {JSX.Element} dropdownIcon an custom dropdown icon
|
|
588
|
-
* @returns {Partial<SelectComponents<Option, boolean, GroupBase<Option>>> | undefined} components object to override react-select default components
|
|
589
|
-
*/
|
|
590
|
-
const useCustomComponents = (componentsProps, disabled, menuIsOpen, refMenuIsEnabled, dataTestId, maxSelectedDisplayCount, dropdownIcon) => {
|
|
591
|
-
const [t] = useTranslation();
|
|
592
|
-
// perhaps it should not be wrap in memo (causing some issues with opening and closing on mobiles)
|
|
593
|
-
const customComponents = React.useMemo(() => {
|
|
594
|
-
return Object.assign({ ValueContainer: props => {
|
|
595
|
-
if (props.isMulti && Array.isArray(props.children) && props.children.length > 0) {
|
|
596
|
-
const PLACEHOLDER_KEY = "placeholder";
|
|
597
|
-
const key = props && props.children && props.children[0] ? props.children[0].key : "";
|
|
598
|
-
const values = props && props.children ? props.children[0] : [];
|
|
599
|
-
const tags = key === PLACEHOLDER_KEY ? [] : values;
|
|
600
|
-
const searchInput = props && props.children && props.children[1];
|
|
601
|
-
return (jsx(components.ValueContainer, Object.assign({}, props, { isDisabled: props.selectProps.isDisabled, children: maxSelectedDisplayCount === undefined ? (jsx(TagsContainer, { disabled: disabled, items: tags
|
|
602
|
-
? tags.map(({ props: tagProps }) => {
|
|
603
|
-
return {
|
|
604
|
-
text: tagProps.children,
|
|
605
|
-
onClick: disabled
|
|
606
|
-
? undefined
|
|
607
|
-
: (e) => {
|
|
608
|
-
var _a, _b;
|
|
609
|
-
refMenuIsEnabled.current = false;
|
|
610
|
-
((_a = tagProps === null || tagProps === void 0 ? void 0 : tagProps.removeProps) === null || _a === void 0 ? void 0 : _a.onClick) && ((_b = tagProps === null || tagProps === void 0 ? void 0 : tagProps.removeProps) === null || _b === void 0 ? void 0 : _b.onClick(e));
|
|
611
|
-
},
|
|
612
|
-
disabled: disabled,
|
|
613
|
-
};
|
|
614
|
-
})
|
|
615
|
-
: [], postFix: searchInput, width: "100%" })) : (jsxs(Fragment, { children: [tags &&
|
|
616
|
-
tags.slice(0, maxSelectedDisplayCount).map(({ props: tagProps }) => {
|
|
617
|
-
var _a, _b;
|
|
618
|
-
return (jsx(Tag, { className: "inline-flex shrink-0", color: disabled ? "unknown" : "primary", dataTestId: tagProps.children ? `${(_a = tagProps.children) === null || _a === void 0 ? void 0 : _a.toString()}-tag` : undefined, onClose: e => {
|
|
619
|
-
var _a, _b;
|
|
620
|
-
e.stopPropagation();
|
|
621
|
-
refMenuIsEnabled.current = false;
|
|
622
|
-
((_a = tagProps === null || tagProps === void 0 ? void 0 : tagProps.removeProps) === null || _a === void 0 ? void 0 : _a.onClick) && ((_b = tagProps === null || tagProps === void 0 ? void 0 : tagProps.removeProps) === null || _b === void 0 ? void 0 : _b.onClick(e));
|
|
623
|
-
}, children: tagProps.children }, (_b = tagProps.children) === null || _b === void 0 ? void 0 : _b.toString()));
|
|
624
|
-
}), tags && tags.length > maxSelectedDisplayCount && (jsxs(Tag, { color: "neutral", dataTestId: "counter-tag", children: ["+", tags.length - maxSelectedDisplayCount] })), searchInput] })) })));
|
|
625
|
-
}
|
|
626
|
-
return (jsx(components.ValueContainer, Object.assign({}, props, { isDisabled: props.selectProps.isDisabled, children: props.children })));
|
|
627
|
-
}, LoadingIndicator: props => {
|
|
628
|
-
return jsx(Spinner, { className: "mt-1.5 mr-1", size: "small" });
|
|
629
|
-
}, DropdownIndicator: props => {
|
|
630
|
-
const icon = props.selectProps.menuIsOpen ? (jsx(Icon, { name: "ChevronUp", size: "medium" })) : (jsx(Icon, { name: "ChevronDown", size: "medium" }));
|
|
631
|
-
return props.selectProps.isLoading ? null : (jsx(components.DropdownIndicator, Object.assign({}, props, { children: jsx("div", { className: cvaSelectIcon(), children: dropdownIcon ? dropdownIcon : icon }) })));
|
|
632
|
-
}, IndicatorSeparator: () => null, ClearIndicator: props => {
|
|
633
|
-
if (disabled) {
|
|
634
|
-
return null;
|
|
635
|
-
}
|
|
636
|
-
return (jsx(components.ClearIndicator, Object.assign({}, props, { children: jsx("div", { className: cvaSelectXIcon(), "data-testid": dataTestId && `${dataTestId}-XMarkIcon`, onClick: props.clearValue, children: jsx(Icon, { name: "XCircle", size: "medium", title: t("clearIndicator.icon.tooltip.clearAll") }) }) })));
|
|
637
|
-
}, Control: props => {
|
|
638
|
-
return jsx(components.Control, Object.assign({}, props, { className: props.isDisabled ? "bg-slate-100" : "" }));
|
|
639
|
-
}, SingleValue: props => {
|
|
640
|
-
return (jsx(components.SingleValue, Object.assign({}, props, { className: props.isDisabled ? "text-slate-700" : "", children: jsx("div", { "data-testid": dataTestId + "-singleValue", children: props.children }) })));
|
|
641
|
-
}, Menu: props => {
|
|
642
|
-
return (jsx(components.Menu, Object.assign({}, props, { className: cvaSelectMenuList({ menuIsOpen: props.selectProps.menuIsOpen }) })));
|
|
643
|
-
}, MenuList: props => {
|
|
644
|
-
return (jsx(components.MenuList, Object.assign({}, props, { innerProps: Object.assign(Object.assign({}, props.innerProps), { onScroll: e => {
|
|
645
|
-
const listEl = e.currentTarget;
|
|
646
|
-
if (listEl.scrollTop + listEl.clientHeight >= listEl.scrollHeight) {
|
|
647
|
-
props.selectProps.onMenuScrollToBottom && props.selectProps.onMenuScrollToBottom(new TouchEvent(""));
|
|
648
|
-
}
|
|
649
|
-
} }), children: props.children })));
|
|
650
|
-
}, Option: props => {
|
|
651
|
-
const componentProps = {
|
|
652
|
-
label: props.label,
|
|
653
|
-
focused: props.isFocused,
|
|
654
|
-
selected: props.isSelected,
|
|
655
|
-
onClick: props.innerProps.onClick,
|
|
656
|
-
};
|
|
657
|
-
return (jsx(components.Option, Object.assign({}, props, { innerProps: Object.assign(Object.assign({}, props.innerProps), { role: "option", onClick: () => { } }), children: props.isMulti ? (jsx(MultiSelectMenuItem, Object.assign({}, componentProps, { dataTestId: typeof props.label === "string" ? props.label : undefined, disabled: disabled }))) : (jsx(SingleSelectMenuItem, Object.assign({}, componentProps, { dataTestId: typeof props.label === "string" ? props.label : undefined, disabled: disabled || props.isDisabled }))) })));
|
|
658
|
-
} }, componentsProps);
|
|
659
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
660
|
-
}, [componentsProps, disabled, maxSelectedDisplayCount]); // do not add dropdownIcon (it will cause issue with opening/closing list for selects with custom icon)
|
|
661
|
-
return customComponents;
|
|
662
|
-
};
|
|
663
|
-
|
|
664
|
-
/**
|
|
665
|
-
* @template IsMulti
|
|
666
|
-
* @template Group
|
|
667
|
-
* @param {React.RefObject<HTMLDivElement>} refContainer react ref to container element
|
|
668
|
-
* @param {React.RefObject<HTMLDivElement>} refPrefix react ref to prefix element
|
|
669
|
-
* @param {number | undefined} maxSelectedDisplayCount a number of max display count
|
|
670
|
-
* @param {StylesConfig<Option, IsMulti, Group> | undefined} styles a optional object to override styles of react-select
|
|
671
|
-
* @returns {StylesConfig<Option, boolean>} styles to override in select
|
|
672
|
-
*/
|
|
673
|
-
const useCustomStyles = (refContainer, refPrefix, maxSelectedDisplayCount, styles, disabled) => {
|
|
674
|
-
const customStyles = React.useMemo(() => {
|
|
675
|
-
return Object.assign({ control: base => {
|
|
676
|
-
return Object.assign(Object.assign({}, base), { border: "0", boxShadow: "0", "&:hover": {
|
|
677
|
-
border: "0",
|
|
678
|
-
}, marginRight: "2px", backgroundColor: "" });
|
|
679
|
-
}, singleValue: base => (Object.assign({}, base)), multiValue: base => (Object.assign({}, base)), multiValueLabel: base => (Object.assign({}, base)), indicatorsContainer: base => (Object.assign(Object.assign({}, base), (disabled && { display: "none" }))), indicatorSeparator: () => ({
|
|
680
|
-
width: "0px",
|
|
681
|
-
}), menu: base => {
|
|
682
|
-
return Object.assign(Object.assign({}, base), { width: "100%", marginTop: "4px", marginBottom: "18px", transition: "all 1s ease-in-out" });
|
|
683
|
-
}, input: base => (Object.assign(Object.assign({}, base), { marginLeft: "0px" })), placeholder: base => (Object.assign({}, base)), option: () => ({}), menuPortal: base => (Object.assign(Object.assign({}, base), { width: (refContainer === null || refContainer === void 0 ? void 0 : refContainer.current) ? `${refContainer.current.clientWidth}px` : base.width, transform: (refPrefix === null || refPrefix === void 0 ? void 0 : refPrefix.current) ? `translate(-${refPrefix.current.clientWidth + 2}px)` : "translate(-2px)", backgroundColor: "#ffffff", borderRadius: "var(--border-radius-lg)", zIndex: 20, borderColor: "rgb(var(--color-slate-300))", boxShadow: "var(--tw-ring-inset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)" })), menuList: base => {
|
|
684
|
-
return Object.assign(Object.assign({}, base), { position: "relative", padding: "var(--spacing-1)", display: "grid", gap: "var(--spacing-1)", width: "100%", borderRadius: "0px", boxShadow: "none", paddingTop: "0px" });
|
|
685
|
-
}, valueContainer: base => {
|
|
686
|
-
return Object.assign(Object.assign({}, base), { flexWrap: maxSelectedDisplayCount !== undefined ? "wrap" : "nowrap", gap: "0.25rem" });
|
|
687
|
-
}, container: base => (Object.assign(Object.assign({}, base), { border: "none", width: "calc(100% - 2px)", paddingLeft: "2px" })), dropdownIndicator: base => (Object.assign(Object.assign({}, base), { padding: "0px" })), clearIndicator: base => {
|
|
688
|
-
return Object.assign(Object.assign({}, base), { padding: "0px" });
|
|
689
|
-
} }, styles);
|
|
690
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
691
|
-
}, [refContainer, refPrefix]);
|
|
692
|
-
return { customStyles };
|
|
693
|
-
};
|
|
694
|
-
|
|
695
|
-
/**
|
|
696
|
-
* A hook used by selects to share the common code
|
|
697
|
-
*
|
|
698
|
-
* @param {SelectProps} props - The props for the Select component
|
|
699
|
-
* @returns {IUseSelect} Select component
|
|
700
|
-
*/
|
|
701
|
-
const useSelect = (_a) => {
|
|
702
|
-
var _b;
|
|
703
|
-
var { id, className, dataTestId = "select", prefix, async, dropdownIcon, maxMenuHeight = 200, label, hasError, disabled, isMulti, components, value, options, onChange, isLoading, classNamePrefix = "", onMenuOpen, onMenuClose, maxSelectedDisplayCount = undefined, isClearable = false, isSearchable = true, onMenuScrollToBottom, styles, filterOption, onInputChange } = _a, props = __rest(_a, ["id", "className", "dataTestId", "prefix", "async", "dropdownIcon", "maxMenuHeight", "label", "hasError", "disabled", "isMulti", "components", "value", "options", "onChange", "isLoading", "classNamePrefix", "onMenuOpen", "onMenuClose", "maxSelectedDisplayCount", "isClearable", "isSearchable", "onMenuScrollToBottom", "styles", "filterOption", "onInputChange"]);
|
|
704
|
-
const refContainer = React__default.useRef(null);
|
|
705
|
-
const refPrefix = React__default.useRef(null);
|
|
706
|
-
const { customStyles } = useCustomStyles(refContainer, refPrefix, maxSelectedDisplayCount, styles, disabled);
|
|
707
|
-
const [menuIsOpen, setMenuIsOpen] = React__default.useState((_b = props.menuIsOpen) !== null && _b !== void 0 ? _b : false);
|
|
708
|
-
const refMenuIsEnabled = React__default.useRef(true);
|
|
709
|
-
const customComponents = useCustomComponents(components, disabled || false, menuIsOpen, refMenuIsEnabled, dataTestId, maxSelectedDisplayCount, dropdownIcon);
|
|
710
|
-
const menuPlacement = "auto";
|
|
711
|
-
const openMenuHandler = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
712
|
-
onMenuOpen && onMenuOpen();
|
|
713
|
-
if (refMenuIsEnabled.current) {
|
|
714
|
-
setMenuIsOpen(true);
|
|
715
|
-
}
|
|
716
|
-
else {
|
|
717
|
-
refMenuIsEnabled.current = true;
|
|
718
|
-
}
|
|
719
|
-
});
|
|
720
|
-
const closeMenuHandler = () => {
|
|
721
|
-
setMenuIsOpen(false);
|
|
722
|
-
onMenuClose && onMenuClose();
|
|
723
|
-
};
|
|
724
|
-
const orderedOptions = React__default.useMemo(() => {
|
|
725
|
-
return disabled
|
|
726
|
-
? getOrderedOptions(options, value).map(option => {
|
|
727
|
-
return Object.assign(Object.assign({}, option), { disabled: true });
|
|
728
|
-
})
|
|
729
|
-
: getOrderedOptions(options, value);
|
|
730
|
-
}, [options, value, disabled]);
|
|
731
|
-
return {
|
|
732
|
-
refContainer,
|
|
733
|
-
refPrefix,
|
|
734
|
-
customStyles,
|
|
735
|
-
menuIsOpen,
|
|
736
|
-
customComponents,
|
|
737
|
-
menuPlacement,
|
|
738
|
-
openMenuHandler,
|
|
739
|
-
closeMenuHandler,
|
|
740
|
-
orderedOptions,
|
|
741
|
-
};
|
|
742
|
-
};
|
|
743
|
-
|
|
744
|
-
/**
|
|
745
|
-
* The CreatableSelect component.
|
|
746
|
-
*
|
|
747
|
-
* @param {CreateableSelectProps} props - The props for the CreatableSelect component
|
|
748
|
-
* @returns {JSX.Element} CreatableSelect component
|
|
749
|
-
*/
|
|
750
|
-
const CreatableSelect = (props) => {
|
|
751
|
-
const { id, dataTestId = "select", async, maxMenuHeight = 200, label, error, disabled, isMulti, value, hideLabel, description, info, options, onChange, isLoading, classNamePrefix = "", filterOption, prefix, onInputChange, placeholder, isClearable, } = props;
|
|
752
|
-
const hasError = !!error;
|
|
753
|
-
const { refPrefix, customStyles, menuIsOpen, customComponents, menuPlacement, openMenuHandler, closeMenuHandler, orderedOptions, } = useSelect(props);
|
|
754
|
-
const selectProps = {
|
|
755
|
-
value,
|
|
756
|
-
menuPlacement,
|
|
757
|
-
maxMenuHeight,
|
|
758
|
-
onChange,
|
|
759
|
-
"aria-label": label,
|
|
760
|
-
"data-testid": dataTestId,
|
|
761
|
-
components: customComponents,
|
|
762
|
-
styles: customStyles,
|
|
763
|
-
tabSelectsValue: false,
|
|
764
|
-
blurInputOnSelect: !isMulti,
|
|
765
|
-
menuPortalTarget: document.body,
|
|
766
|
-
isSearchable: disabled ? false : true,
|
|
767
|
-
menuShouldBlockScroll: true,
|
|
768
|
-
menuShouldScrollIntoView: true,
|
|
769
|
-
openMenuOnFocus: disabled ? false : true,
|
|
770
|
-
menuIsOpen: disabled ? false : menuIsOpen,
|
|
771
|
-
openMenuOnClick: true,
|
|
772
|
-
closeMenuOnSelect: false,
|
|
773
|
-
isMulti: isMulti,
|
|
774
|
-
classNamePrefix,
|
|
775
|
-
isLoading,
|
|
776
|
-
onInputChange,
|
|
777
|
-
placeholder,
|
|
778
|
-
isClearable,
|
|
779
|
-
id,
|
|
780
|
-
};
|
|
781
|
-
return (jsx(SelectContainer, { dataTestId: dataTestId, description: description, error: error, hasError: hasError, hideLabel: hideLabel, id: id, info: info, isDisabled: disabled, label: label, children: jsxs(Fragment, { children: [prefix !== undefined && (jsx("div", { className: cvaSelectPrefix(), "data-testid": dataTestId && `${dataTestId}-prefix`, ref: refPrefix, children: prefix })), async ? (jsx(ReactAsyncCreatableSelect, Object.assign({ "aria-label": label, components: customComponents, "data-testid": dataTestId, maxMenuHeight: maxMenuHeight }, props, { onMenuClose: () => {
|
|
782
|
-
closeMenuHandler();
|
|
783
|
-
}, onMenuOpen: () => {
|
|
784
|
-
!disabled && openMenuHandler();
|
|
785
|
-
} }))) : (jsx(ReactCreatableSelect, Object.assign({}, selectProps, { isMulti: isMulti, onMenuClose: () => {
|
|
786
|
-
closeMenuHandler();
|
|
787
|
-
}, onMenuOpen: () => {
|
|
788
|
-
!disabled && openMenuHandler();
|
|
789
|
-
}, options: filterOption ? orderedOptions : options })))] }) }));
|
|
790
|
-
};
|
|
791
|
-
|
|
792
|
-
/**
|
|
793
|
-
* Selects are input components used to choose a value from a set.
|
|
794
|
-
*
|
|
795
|
-
* @param {SelectProps} props - The props for the Select component
|
|
796
|
-
* @returns {JSX.Element} Select component
|
|
797
|
-
*/
|
|
798
|
-
const Select = (props) => {
|
|
799
|
-
const { className } = props, propsNoClassName = __rest(props, ["className"]);
|
|
800
|
-
const { id, dataTestId = "select", prefix, async, maxMenuHeight = 200, label, hasError, disabled, isMulti, value, options, onChange, isLoading, classNamePrefix = dataTestId ? dataTestId : "select", onMenuScrollToBottom, filterOption, onInputChange, isSearchable, isClearable = false, readOnly, openMenuOnClick = !disabled, openMenuOnFocus = !disabled, } = props;
|
|
801
|
-
const { refContainer, refPrefix, customStyles, menuIsOpen, customComponents, menuPlacement, openMenuHandler, closeMenuHandler, orderedOptions, } = useSelect(props);
|
|
802
|
-
const selectProps = {
|
|
803
|
-
value,
|
|
804
|
-
menuPlacement,
|
|
805
|
-
maxMenuHeight,
|
|
806
|
-
onChange,
|
|
807
|
-
"aria-label": label,
|
|
808
|
-
"data-testid": dataTestId,
|
|
809
|
-
components: customComponents,
|
|
810
|
-
styles: customStyles,
|
|
811
|
-
tabSelectsValue: false,
|
|
812
|
-
blurInputOnSelect: !isMulti,
|
|
813
|
-
menuPortalTarget: props.menuPortalTarget || document.body,
|
|
814
|
-
isSearchable: disabled || readOnly ? false : isSearchable,
|
|
815
|
-
menuShouldBlockScroll: true,
|
|
816
|
-
menuShouldScrollIntoView: true,
|
|
817
|
-
openMenuOnFocus,
|
|
818
|
-
menuIsOpen: !readOnly ? menuIsOpen : false,
|
|
819
|
-
openMenuOnClick,
|
|
820
|
-
closeMenuOnSelect: false,
|
|
821
|
-
isMulti,
|
|
822
|
-
classNamePrefix,
|
|
823
|
-
isLoading,
|
|
824
|
-
isClearable,
|
|
825
|
-
id,
|
|
826
|
-
onMenuScrollToBottom,
|
|
827
|
-
onInputChange,
|
|
828
|
-
};
|
|
829
|
-
return (jsxs("div", { className: cvaSelect({ invalid: hasError, disabled: disabled || readOnly, className }), "data-testid": dataTestId, ref: refContainer, children: [prefix !== undefined && (jsx("div", { className: cvaSelectPrefix(), "data-testid": dataTestId && `${dataTestId}-prefix`, ref: refPrefix, children: prefix })), async ? (jsx(ReactAsyncSelect, Object.assign({}, propsNoClassName, selectProps, async, { onMenuClose: closeMenuHandler, onMenuOpen: openMenuHandler }))) : (jsx(ReactSelect, Object.assign({}, propsNoClassName, selectProps, { hideSelectedOptions: false, isMulti: isMulti, onMenuClose: closeMenuHandler, onMenuOpen: openMenuHandler, options: filterOption ? orderedOptions : options })))] }));
|
|
830
|
-
};
|
|
831
|
-
Select.displayName = "Select";
|
|
832
|
-
|
|
833
|
-
/**
|
|
834
|
-
* The SelectField component is a Select component wrapped in the FromGroup component.
|
|
835
|
-
*
|
|
836
|
-
* This means that it can easily be added to any form alongside other Field components.
|
|
837
|
-
*
|
|
838
|
-
* This is done to make the field compatible with the React-hook-form library.
|
|
839
|
-
*
|
|
840
|
-
* @param {SelectFieldProps} props - The props for the SelectField component
|
|
841
|
-
* @returns {JSX.Element} SelectField component
|
|
842
|
-
*/
|
|
843
|
-
const SelectField = forwardRef((_a, ref) => {
|
|
844
|
-
var { className, dataTestId, helpText, helpAddon, tip, label, disabled, isInvalid, errorMessage, name, onBlur, options, value, defaultValue, id, onChange } = _a; __rest(_a, ["className", "dataTestId", "helpText", "helpAddon", "tip", "label", "disabled", "isInvalid", "errorMessage", "name", "onBlur", "options", "value", "defaultValue", "id", "onChange"]);
|
|
845
|
-
const [innerValue, setInnerValue] = useState(value || defaultValue);
|
|
846
|
-
const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
|
|
847
|
-
const htmlFor = useMemo(() => (id ? id : "selectField-" + v4()), [id]);
|
|
848
|
-
const innerRef = useRef(null);
|
|
849
|
-
useImperativeHandle(ref, () => innerRef.current, []);
|
|
850
|
-
const handleChange = useCallback((newValue) => {
|
|
851
|
-
setInnerValue(newValue === null || newValue === void 0 ? void 0 : newValue.value);
|
|
852
|
-
}, []);
|
|
853
|
-
useEffect(() => {
|
|
854
|
-
var _a;
|
|
855
|
-
if (!innerValue) {
|
|
856
|
-
return;
|
|
857
|
-
}
|
|
858
|
-
const event = new Event("change");
|
|
859
|
-
(_a = innerRef.current) === null || _a === void 0 ? void 0 : _a.dispatchEvent(event);
|
|
860
|
-
// The event as unknown as ChangeEvent<HTMLInputElement> assertion is needed because
|
|
861
|
-
// the event's type is inferred as Event, which doesn't have a target property, whereas ChangeEvent does.
|
|
862
|
-
onChange === null || onChange === void 0 ? void 0 : onChange(event);
|
|
863
|
-
}, [onChange, innerValue]);
|
|
864
|
-
const selectedOption = useMemo(() => options.find(option => option.value === value), [options, value]);
|
|
865
|
-
const defaultSelectedOption = options.find(option => option.value === defaultValue);
|
|
866
|
-
return (jsxs(FormGroup, { isInvalid: renderAsInvalid,
|
|
867
|
-
htmlFor,
|
|
868
|
-
className,
|
|
869
|
-
dataTestId,
|
|
870
|
-
helpText,
|
|
871
|
-
helpAddon,
|
|
872
|
-
tip,
|
|
873
|
-
label,
|
|
874
|
-
disabled, children: [jsx("select", { onChange, ref: innerRef, name, value: innerValue, hidden: true, children: options.map(option => {
|
|
875
|
-
return (jsx("option", { value: option.value, children: option.label }, option.value));
|
|
876
|
-
}) }), jsx(Select, { id,
|
|
877
|
-
isDisabled: disabled,
|
|
878
|
-
onBlur,
|
|
879
|
-
options,
|
|
880
|
-
onChange: handleChange,
|
|
881
|
-
value: selectedOption,
|
|
882
|
-
defaultValue: defaultSelectedOption })] }));
|
|
883
|
-
});
|
|
884
|
-
|
|
885
|
-
const cvaActionButton = cvaMerge(["drop-shadow-none", "rounded-md"], {
|
|
886
|
-
variants: {
|
|
887
|
-
size: {
|
|
888
|
-
small: ["w-6", "h-6"],
|
|
889
|
-
medium: ["w-8", "h-8"],
|
|
890
|
-
},
|
|
891
|
-
},
|
|
892
|
-
defaultVariants: {
|
|
893
|
-
size: "medium",
|
|
894
|
-
},
|
|
895
|
-
});
|
|
896
|
-
const cvaActionContainer = cvaMerge(["flex", "items-center", "m-1"]);
|
|
897
|
-
|
|
898
|
-
/**
|
|
899
|
-
* The ActionButton component is a wrapper over IconButton to perform an action when the onClick event is triggered.
|
|
900
|
-
*
|
|
901
|
-
* @param {ActionButtonProps} props - The props for the ActionButton component
|
|
902
|
-
* @returns {JSX.Element} ActionButton component
|
|
903
|
-
*/
|
|
904
|
-
const ActionButton = ({ type, value, dataTestId, iconSize, disabled, key, className }) => {
|
|
905
|
-
const [, copyToClipboard] = useCopyToClipboard();
|
|
906
|
-
const elementKey = useMemo(() => key !== null && key !== void 0 ? key : `ActionButton-${v4()}`, [key]);
|
|
907
|
-
const getIconName = () => {
|
|
908
|
-
switch (type) {
|
|
909
|
-
case "PHONE_NUMBER":
|
|
910
|
-
return "PhoneArrowUpRight";
|
|
911
|
-
case "WEB_ADDRESS":
|
|
912
|
-
return "ArrowTopRightOnSquare";
|
|
913
|
-
case "EMAIL":
|
|
914
|
-
return "Envelope";
|
|
915
|
-
case "COPY":
|
|
916
|
-
default:
|
|
917
|
-
return "ClipboardDocument";
|
|
918
|
-
}
|
|
919
|
-
};
|
|
920
|
-
const ButtonAction = () => {
|
|
921
|
-
var _a, _b;
|
|
922
|
-
switch (type) {
|
|
923
|
-
case "EMAIL":
|
|
924
|
-
return window.open(`mailto:${value}`);
|
|
925
|
-
case "WEB_ADDRESS":
|
|
926
|
-
if (value) {
|
|
927
|
-
return window.open(value, "_blank", "noopener,noreferrer");
|
|
928
|
-
}
|
|
929
|
-
return null;
|
|
930
|
-
case "PHONE_NUMBER":
|
|
931
|
-
return window.open(`tel:${value}`);
|
|
932
|
-
case "COPY":
|
|
933
|
-
return copyToClipboard((_b = (_a = value === null || value === void 0 ? void 0 : value.current) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "");
|
|
934
|
-
default:
|
|
935
|
-
return null;
|
|
936
|
-
}
|
|
937
|
-
};
|
|
938
|
-
if (ButtonAction === null) {
|
|
939
|
-
return null;
|
|
940
|
-
}
|
|
941
|
-
return (jsx("div", { className: cvaActionContainer({ className }), children: jsx(IconButton, { className: cvaActionButton({ size: iconSize }), dataTestId: dataTestId || "testIconButtonId", disabled: disabled, icon: jsx(Icon, { name: getIconName(), size: "small" }), onClick: ButtonAction, size: "small", variant: "secondary" }) }, elementKey));
|
|
942
|
-
};
|
|
943
|
-
|
|
944
|
-
const cvaInputBase = cvaMerge([
|
|
945
|
-
"component-baseInput-shadow",
|
|
946
|
-
"component-baseInput-border",
|
|
947
|
-
"component-baseInput-background",
|
|
948
|
-
"border-slate-300",
|
|
949
|
-
"focus-within:ring-2",
|
|
950
|
-
"focus-within:ring-inset",
|
|
951
|
-
"focus-within:ring-primary-500",
|
|
952
|
-
"focus-within:border-slate-400",
|
|
953
|
-
"hover:border-slate-400",
|
|
954
|
-
"hover:bg-slate-50",
|
|
955
|
-
"transition",
|
|
956
|
-
]);
|
|
957
|
-
const cvaInputBaseDisabled = cvaMerge(["bg-slate-100", "hover:bg-slate-100", "hover:border-slate-300"]);
|
|
958
|
-
const cvaInputBaseInvalid = cvaMerge(["border-danger-600", "focus-within:ring-danger-600"]);
|
|
959
|
-
const cvaInput = cvaMerge(["relative", "flex", "h-10", cvaInputBase()], {
|
|
960
|
-
variants: {
|
|
961
|
-
size: {
|
|
962
|
-
small: ["h-8"],
|
|
963
|
-
medium: ["h-10"],
|
|
964
|
-
},
|
|
965
|
-
disabled: {
|
|
966
|
-
true: cvaInputBaseDisabled(),
|
|
967
|
-
false: "",
|
|
968
|
-
},
|
|
969
|
-
invalid: {
|
|
970
|
-
true: cvaInputBaseInvalid(),
|
|
971
|
-
false: "",
|
|
972
|
-
},
|
|
973
|
-
},
|
|
974
|
-
});
|
|
975
|
-
const cvaInputField = cvaMerge([
|
|
976
|
-
"w-full",
|
|
977
|
-
"px-3",
|
|
978
|
-
"border-0",
|
|
979
|
-
"bg-transparent",
|
|
980
|
-
"text-base",
|
|
981
|
-
"text-slate-900",
|
|
982
|
-
"placeholder-slate-400",
|
|
983
|
-
"focus-visible:outline-none",
|
|
984
|
-
], {
|
|
985
|
-
variants: {
|
|
986
|
-
size: {
|
|
987
|
-
small: ["py-1"],
|
|
988
|
-
medium: ["py-2"],
|
|
989
|
-
},
|
|
990
|
-
disabled: {
|
|
991
|
-
true: ["text-slate-700"],
|
|
992
|
-
false: "",
|
|
993
|
-
},
|
|
994
|
-
},
|
|
995
|
-
});
|
|
996
|
-
const cvaInputAddon = cvaMerge([
|
|
997
|
-
"flex",
|
|
998
|
-
"justify-center",
|
|
999
|
-
"items-center",
|
|
1000
|
-
"px-3",
|
|
1001
|
-
"border-slate-300",
|
|
1002
|
-
"text-slate-500",
|
|
1003
|
-
"bg-slate-50",
|
|
1004
|
-
]);
|
|
1005
|
-
const cvaInputAddonBefore = cvaMerge([cvaInputAddon(), "border-r", "rounded-l-lg"]);
|
|
1006
|
-
const cvaInputAddonAfter = cvaMerge([cvaInputAddon(), "border-l", "rounded-r-lg"]);
|
|
1007
|
-
const cvaInputPrefix = cvaMerge([
|
|
1008
|
-
"flex",
|
|
1009
|
-
"justify-center",
|
|
1010
|
-
"items-center",
|
|
1011
|
-
"text-slate-400",
|
|
1012
|
-
"hover:component-search-prefix",
|
|
1013
|
-
"component-baseInput-prefix",
|
|
1014
|
-
"component-search-borderless",
|
|
1015
|
-
"px-3",
|
|
1016
|
-
], {
|
|
1017
|
-
variants: {
|
|
1018
|
-
disabled: {
|
|
1019
|
-
true: ["text-slate-500"],
|
|
1020
|
-
false: "",
|
|
1021
|
-
},
|
|
1022
|
-
},
|
|
1023
|
-
});
|
|
1024
|
-
const cvaInputSuffix = cvaMerge(["flex", "justify-center", "items-center", "text-slate-400", "px-3"], {
|
|
1025
|
-
variants: {
|
|
1026
|
-
disabled: {
|
|
1027
|
-
true: ["text-slate-500"],
|
|
1028
|
-
false: "",
|
|
1029
|
-
},
|
|
1030
|
-
},
|
|
1031
|
-
});
|
|
1032
|
-
|
|
1033
|
-
/**
|
|
1034
|
-
* A base input component that can be used for text inputs, password inputs, etc.
|
|
1035
|
-
* A reference to the input element is provided as the `ref` prop.
|
|
1036
|
-
* Extends props from [React.InputHTMLAttributes](https://reactjs.org/docs/dom-elements.html#input)
|
|
1037
|
-
*
|
|
1038
|
-
* For specific input types make sure to use the corresponding input component.
|
|
1039
|
-
* This is a base used by our other input components such as TextInput, NumberInput, PasswordInput, etc.
|
|
1040
|
-
*/
|
|
1041
|
-
const BaseInput = React.forwardRef((_a, ref) => {
|
|
1042
|
-
var { className, isInvalid, dataTestId, prefix, suffix, addonBefore, addonAfter, actions, fieldSize = "medium", nonInteractive = false, showDefaultActions = false, type, inputClassName, addonBeforeClassName } = _a, rest = __rest(_a, ["className", "isInvalid", "dataTestId", "prefix", "suffix", "addonBefore", "addonAfter", "actions", "fieldSize", "nonInteractive", "showDefaultActions", "type", "inputClassName", "addonBeforeClassName"]);
|
|
1043
|
-
const isReadOnlyWithPlaceholder = useMemo(() => !!rest.readOnly && !!rest.placeholder && !rest.value, [rest.readOnly, rest.placeholder, rest.value]);
|
|
1044
|
-
const renderAsDisabled = rest.disabled || rest.readOnly;
|
|
1045
|
-
const innerRef = React.useRef(null);
|
|
1046
|
-
React.useImperativeHandle(ref, () => innerRef.current, []);
|
|
1047
|
-
return (jsxs("div", { className: cvaInput({
|
|
1048
|
-
disabled: renderAsDisabled,
|
|
1049
|
-
invalid: isInvalid,
|
|
1050
|
-
size: fieldSize,
|
|
1051
|
-
className,
|
|
1052
|
-
}), "data-testid": dataTestId && `${dataTestId}-container`, children: [addonBefore && addonBefore !== undefined ? (jsx("div", { className: cvaInputAddonBefore({ className: addonBeforeClassName }), "data-testid": dataTestId && `${dataTestId}-addonBefore`, children: addonBefore })) : null, prefix && addonBefore !== prefix ? (jsx("div", { className: cvaInputPrefix({
|
|
1053
|
-
disabled: renderAsDisabled,
|
|
1054
|
-
}), "data-testid": dataTestId && `${dataTestId}-prefix`, children: prefix })) : null, jsx("input", Object.assign({ className: cvaInputField({ size: fieldSize, disabled: renderAsDisabled, className: inputClassName }), "data-testid": dataTestId, ref: innerRef, type: isReadOnlyWithPlaceholder ? "text" : type }, rest, { readOnly: rest.readOnly || nonInteractive })), suffix && addonBefore !== suffix ? (jsx("div", { className: cvaInputSuffix({
|
|
1055
|
-
disabled: renderAsDisabled,
|
|
1056
|
-
}), "data-testid": dataTestId && `${dataTestId}-suffix`, children: suffix })) : null, rest.readOnly === true && showDefaultActions ? (jsx(ActionButton, { type: "COPY", value: innerRef }, "default-copy-action")) : null, actions && addonBefore !== actions ? actions : null, addonAfter && addonAfter !== undefined && addonBefore !== addonAfter ? (jsx("div", { className: cvaInputAddonAfter(), "data-testid": dataTestId && `${dataTestId}-addonAfter`, children: addonAfter })) : null] }));
|
|
1057
|
-
});
|
|
1058
|
-
|
|
1059
|
-
/**
|
|
1060
|
-
* A wrapper around BaseInput with a pop-up day picker.
|
|
1061
|
-
*
|
|
1062
|
-
* NOTE: If shown with a label, please use the `DateField` component instead.
|
|
1063
|
-
*/
|
|
1064
|
-
const DateInput = forwardRef((_a, ref) => {
|
|
1065
|
-
var { min, max, defaultValue, value } = _a, rest = __rest(_a, ["min", "max", "defaultValue", "value"]);
|
|
1066
|
-
const formatDateToInputString = (date) => date instanceof Date ? format(date, "yyyy-MM-dd") : date;
|
|
1067
|
-
// Chrome and Firefox need their default icon to have datepicker functionality.
|
|
1068
|
-
const showIcon = !/Chrome/.test(navigator.userAgent) && !/Firefox/.test(navigator.userAgent);
|
|
1069
|
-
return (jsx(BaseInput, Object.assign({ defaultValue: formatDateToInputString(defaultValue), max: formatDateToInputString(max), min: formatDateToInputString(min), ref: ref, suffix: showIcon && jsx(Icon, { dataTestId: "calendar", name: "Calendar", size: "medium", type: "solid" }), type: "date", value: formatDateToInputString(value) }, rest)));
|
|
1070
|
-
});
|
|
1071
|
-
|
|
1072
|
-
/**
|
|
1073
|
-
* The date field component is used for entering date values.
|
|
1074
|
-
*
|
|
1075
|
-
* _**Do use**_ the DateField for date input.
|
|
1076
|
-
*
|
|
1077
|
-
* _**Do not use**_ this fields for non-serialized dates. Use TextField instead.
|
|
1078
|
-
*/
|
|
1079
|
-
const DateField = forwardRef((_a, ref) => {
|
|
1080
|
-
var { label, id, tip, helpText, errorMessage, helpAddon, isInvalid, className, defaultValue, dataTestId } = _a, rest = __rest(_a, ["label", "id", "tip", "helpText", "errorMessage", "helpAddon", "isInvalid", "className", "defaultValue", "dataTestId"]);
|
|
1081
|
-
const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
|
|
1082
|
-
const htmlForId = id ? id : "dateField-" + v4();
|
|
1083
|
-
return (jsx(FormGroup, { dataTestId: dataTestId && `${dataTestId}-FormGroup`, disabled: rest.disabled, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, tip: tip, children: jsx(DateInput, Object.assign({ "aria-labelledby": htmlForId + "-label", defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, ref: ref }, rest, { className: className, dataTestId: dataTestId })) }));
|
|
1084
|
-
});
|
|
1085
|
-
|
|
1086
|
-
const cvaDropZone = cvaMerge([
|
|
1087
|
-
"flex",
|
|
1088
|
-
"component-baseInput-background",
|
|
1089
|
-
"justify-center",
|
|
1090
|
-
"text-slate-500",
|
|
1091
|
-
"rounded-sm",
|
|
1092
|
-
"border-2",
|
|
1093
|
-
"border-gray-200",
|
|
1094
|
-
"border-dashed",
|
|
1095
|
-
"hover:bg-slate-100",
|
|
1096
|
-
"hover:border-solid",
|
|
1097
|
-
"hover:border-primary-500",
|
|
1098
|
-
], {
|
|
1099
|
-
variants: {
|
|
1100
|
-
size: {
|
|
1101
|
-
small: ["p-2"],
|
|
1102
|
-
medium: ["p-4"],
|
|
1103
|
-
large: ["p-8"],
|
|
1104
|
-
},
|
|
1105
|
-
disabled: { true: ["bg-slate-100", "hover:bg-slate-100"], false: "" },
|
|
1106
|
-
dragActive: { true: ["border-gray-200", "bg-slate-100"], false: "" },
|
|
1107
|
-
dropComplete: {
|
|
1108
|
-
true: ["border-solid", "border-primary-500", "bg-slate-100", "hover:border-solid", "hover:border-gray-200"],
|
|
1109
|
-
false: "",
|
|
1110
|
-
},
|
|
1111
|
-
invalid: { true: ["border-danger-600", "text-danger-500"], false: "" },
|
|
1112
|
-
},
|
|
1113
|
-
});
|
|
1114
|
-
const cvaDropZoneLabel = cvaMerge([
|
|
1115
|
-
"h-full",
|
|
1116
|
-
"pt-1",
|
|
1117
|
-
"pb-1",
|
|
1118
|
-
"gap-2",
|
|
1119
|
-
"items-center",
|
|
1120
|
-
"flex-col",
|
|
1121
|
-
"flex",
|
|
1122
|
-
"justify-center",
|
|
1123
|
-
]);
|
|
1124
|
-
|
|
1125
|
-
/**
|
|
1126
|
-
* The Drop Zone can be used to drag and drop files
|
|
1127
|
-
*
|
|
1128
|
-
* @param {DropZoneProps} props - The props for the DropZone component
|
|
1129
|
-
* @returns {JSX.Element} DropZone component
|
|
605
|
+
* @param {DropZoneProps} props - The props for the DropZone component
|
|
606
|
+
* @returns {JSX.Element} DropZone component
|
|
1130
607
|
*/
|
|
1131
608
|
const DropZone = (_a) => {
|
|
1132
609
|
var { className, dataTestId, filesSelected, label, size = "large", isInvalid = false, disabled = false } = _a, rest = __rest(_a, ["className", "dataTestId", "filesSelected", "label", "size", "isInvalid", "disabled"]);
|
|
@@ -1206,178 +683,634 @@ const EmailInput = React.forwardRef((_a, ref) => {
|
|
|
1206
683
|
});
|
|
1207
684
|
|
|
1208
685
|
/**
|
|
1209
|
-
* The EmailField component is used to enter email.
|
|
1210
|
-
* EmailField validates that user enters a valid email address.
|
|
1211
|
-
*
|
|
686
|
+
* The EmailField component is used to enter email.
|
|
687
|
+
* EmailField validates that user enters a valid email address.
|
|
688
|
+
*
|
|
689
|
+
*/
|
|
690
|
+
const EmailField = forwardRef((_a, ref) => {
|
|
691
|
+
var { label, id, tip, helpText, errorMessage, helpAddon, className, defaultValue, dataTestId, value, onChange, isInvalid = false } = _a, rest = __rest(_a, ["label", "id", "tip", "helpText", "errorMessage", "helpAddon", "className", "defaultValue", "dataTestId", "value", "onChange", "isInvalid"]);
|
|
692
|
+
const htmlForId = id ? id : "emailField-" + v4();
|
|
693
|
+
// Type guard to check if value is a string
|
|
694
|
+
function isString(inputValue) {
|
|
695
|
+
return typeof inputValue === "string";
|
|
696
|
+
}
|
|
697
|
+
const renderAsInvalid = !!errorMessage || (value && isString(value) && !validateEmailAddress(value)) || isInvalid;
|
|
698
|
+
const handleChange = useCallback((event) => {
|
|
699
|
+
if (onChange) {
|
|
700
|
+
onChange(event);
|
|
701
|
+
}
|
|
702
|
+
}, [onChange]);
|
|
703
|
+
return (jsx(FormGroup, { dataTestId: dataTestId && `${dataTestId}-FormGroup`, disabled: rest.disabled, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, tip: tip, children: jsx(EmailInput, Object.assign({ "aria-labelledby": htmlForId + "-label", defaultValue: defaultValue, disabled: renderAsInvalid, id: htmlForId, isInvalid: renderAsInvalid, onChange: handleChange, ref: ref, value: value }, rest, { className: className, dataTestId: dataTestId })) }));
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* A thin wrapper around the `BaseInput` component for number input fields.
|
|
708
|
+
*
|
|
709
|
+
* NOTE: If shown with a label, please use the `NumberField` component instead.
|
|
710
|
+
*/
|
|
711
|
+
const NumberInput = forwardRef((props, ref) => {
|
|
712
|
+
return jsx(BaseInput, Object.assign({ ref: ref, type: "number" }, props, { value: props.value }));
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* The number field component is used for entering numeric values and includes controls for incrementally increasing or decreasing the value.
|
|
717
|
+
*
|
|
718
|
+
* _**Do use**_ the NumberField when the controls to incrementally increase or decrease makes the task easier for the user.
|
|
719
|
+
*
|
|
720
|
+
* _**Do not use**_ this fields for non-serialized numbers. Use TextField instead.
|
|
721
|
+
*/
|
|
722
|
+
const NumberField = forwardRef((_a, ref) => {
|
|
723
|
+
var { label, id, tip, helpText, errorMessage, helpAddon, isInvalid, maxLength, className, value, dataTestId } = _a, rest = __rest(_a, ["label", "id", "tip", "helpText", "errorMessage", "helpAddon", "isInvalid", "maxLength", "className", "value", "dataTestId"]);
|
|
724
|
+
const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
|
|
725
|
+
const htmlForId = id ? id : "numberField-" + v4();
|
|
726
|
+
return (jsx(FormGroup, { dataTestId: dataTestId && `${dataTestId}-FormGroup`, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, tip: tip, children: jsx(NumberInput, Object.assign({ "aria-labelledby": htmlForId + "-label", id: htmlForId, isInvalid: renderAsInvalid, maxLength: maxLength, ref: ref, value: value }, rest, { className: className, dataTestId: dataTestId })) }));
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
const cvaOptionCardLabel = cvaMerge([
|
|
730
|
+
cvaInputBase(),
|
|
731
|
+
"text-center",
|
|
732
|
+
"component-baseInput-background",
|
|
733
|
+
"aspect-square",
|
|
734
|
+
"flex",
|
|
735
|
+
"flex-col",
|
|
736
|
+
"gap-2",
|
|
737
|
+
"justify-center",
|
|
738
|
+
"items-center",
|
|
739
|
+
"p-responsive-space",
|
|
740
|
+
"w-full",
|
|
741
|
+
"peer-checked:bg-primary-50",
|
|
742
|
+
"peer-checked:border-primary-600",
|
|
743
|
+
"peer-checked:border-2",
|
|
744
|
+
], {
|
|
745
|
+
variants: {
|
|
746
|
+
disabled: {
|
|
747
|
+
true: [cvaInputBaseDisabled(), "cursor-not-allowed"],
|
|
748
|
+
false: ["cursor-pointer"],
|
|
749
|
+
},
|
|
750
|
+
},
|
|
751
|
+
});
|
|
752
|
+
const cvaOptionCardContent = cvaMerge(["flex", "flex-col", "items-center"]);
|
|
753
|
+
const cvaOptionCardContainer = cvaMerge(["contents"]);
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* A card version of a radio button that includes an icon, headings and a description.
|
|
757
|
+
*/
|
|
758
|
+
const OptionCard = forwardRef((_a, ref) => {
|
|
759
|
+
var { icon, heading, subheading, description, disabled, id, value, className, dataTestId } = _a, rest = __rest(_a, ["icon", "heading", "subheading", "description", "disabled", "id", "value", "className", "dataTestId"]);
|
|
760
|
+
const htmlForId = id !== null && id !== void 0 ? id : "option-card-" + v4();
|
|
761
|
+
return (jsxs("div", { className: cvaOptionCardContainer(), "data-testid": dataTestId, children: [jsx("input", Object.assign({ className: "peer hidden", id: htmlForId, ref: ref, type: "radio", value: value }, rest)), jsxs("label", { className: cvaOptionCardLabel({ className, disabled }), htmlFor: htmlForId, children: [disabled && icon && cloneElement(icon, { className: `${icon.props.className} text-secondary-400` }), !disabled && icon, heading && (jsx(Heading, { subtle: disabled, variant: "secondary", children: heading })), (subheading || description) && (jsxs("div", { className: cvaOptionCardContent(), children: [subheading && (jsx(Text, { align: "center", subtle: disabled, type: "span", weight: "thick", children: subheading })), description && (jsx(Text, { align: "center", subtle: true, type: "span", children: description }))] }))] })] }));
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* A thin wrapper around the `BaseInput` component for password input fields.
|
|
766
|
+
*
|
|
767
|
+
* NOTE: If shown with a label, please use the `PasswordField` component instead.
|
|
768
|
+
*/
|
|
769
|
+
const PasswordInput = forwardRef((props, ref) => (jsx(BaseInput, Object.assign({ ref: ref }, props))));
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Password fields enter a password or other confidential information. Characters are masked as they are typed.
|
|
773
|
+
*
|
|
774
|
+
* _**Do use** when the user has to input a password or something that needs to be obfuscated_
|
|
775
|
+
*
|
|
776
|
+
* _**Do not use** to confirm user actions, such as deleting. Use a checkbox for such flows._
|
|
777
|
+
*/
|
|
778
|
+
const PasswordField = forwardRef((_a, ref) => {
|
|
779
|
+
var { id, label, tip, helpText, helpAddon, errorMessage, isInvalid, maxLength, onChange, className, value, dataTestId } = _a, rest = __rest(_a, ["id", "label", "tip", "helpText", "helpAddon", "errorMessage", "isInvalid", "maxLength", "onChange", "className", "value", "dataTestId"]);
|
|
780
|
+
const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
|
|
781
|
+
const htmlFor = id ? id : "passwordField-" + v4();
|
|
782
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
783
|
+
const handleChange = useCallback((event) => {
|
|
784
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(event);
|
|
785
|
+
}, [onChange]);
|
|
786
|
+
return (jsx(FormGroup, { dataTestId: dataTestId && `${dataTestId}-FormGroup`, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlFor, isInvalid: renderAsInvalid, label: label, tip: tip, children: jsx(PasswordInput, Object.assign({}, rest, { actions: jsx(Icon, { className: "absolute top-0 bottom-0 right-2 h-full content-center", color: "neutral", name: showPassword ? "EyeSlash" : "Eye", onClick: () => setShowPassword(prevState => !prevState), size: "small" }), "aria-labelledby": htmlFor + "-label", className: className, dataTestId: dataTestId, disabled: rest.readOnly, id: htmlFor, isInvalid: renderAsInvalid, maxLength: maxLength, onChange: handleChange, ref: ref, type: showPassword ? "text" : "password", value: value })) }));
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* @param phoneNumber - a phone number as a string
|
|
791
|
+
* @returns {boolean} true if the phone number starts with a plus sign
|
|
792
|
+
* @example checkIfPhoneNumberHasPlus("123456789") // false
|
|
793
|
+
* checkIfPhoneNumberHasPlus("+123456789") // true
|
|
794
|
+
*/
|
|
795
|
+
const checkIfPhoneNumberHasPlus = (phoneNumber) => {
|
|
796
|
+
return phoneNumber.startsWith("+");
|
|
797
|
+
};
|
|
798
|
+
/**
|
|
799
|
+
* @param phoneNumber - a phone number as a string
|
|
800
|
+
* @returns {string|number} the phone number with a plus sign in front of it
|
|
801
|
+
* @example getPhoneNumberWithPlus("123456789") // "+123456789"
|
|
802
|
+
*/
|
|
803
|
+
const getPhoneNumberWithPlus = (phoneNumber) => {
|
|
804
|
+
const stringPhoneNumber = phoneNumber.toString();
|
|
805
|
+
return checkIfPhoneNumberHasPlus(stringPhoneNumber) ? stringPhoneNumber : `+${stringPhoneNumber}`;
|
|
806
|
+
};
|
|
807
|
+
/**
|
|
808
|
+
* Generates a flag emoji based on the given country code.
|
|
809
|
+
*
|
|
810
|
+
* @param {string} countryCode - The two-letter country code (ISO 3166-1 alpha-2).
|
|
811
|
+
* @returns {string} - The corresponding flag emoji for the given country code.
|
|
812
|
+
* @example getPhoneNumberWithPlus("DK") // "🇩🇰"
|
|
813
|
+
*/
|
|
814
|
+
const countryCodeToFlagEmoji = (countryCode) => {
|
|
815
|
+
let code = countryCode;
|
|
816
|
+
if (countryCode.startsWith("+")) {
|
|
817
|
+
code = getCountryAbbreviation(countryCode.substring(1));
|
|
818
|
+
}
|
|
819
|
+
else if (!isNaN(Number(countryCode))) {
|
|
820
|
+
code = getCountryAbbreviation(countryCode);
|
|
821
|
+
}
|
|
822
|
+
return isSupportedCountry(code.toUpperCase())
|
|
823
|
+
? code.toUpperCase().replace(/./g, char => String.fromCodePoint(127397 + char.charCodeAt(0)))
|
|
824
|
+
: "";
|
|
825
|
+
};
|
|
826
|
+
/**
|
|
827
|
+
* Retrieves the ISO 3166-1 alpha-2 country code associated with a given international calling code.
|
|
828
|
+
*
|
|
829
|
+
* @param {string} callCode - The international calling code for a country.
|
|
830
|
+
* @returns {string} The abbreviation for a country or an empty string if no matching country is found.
|
|
831
|
+
* @example getCountryAbbreviation("45") // "DK"
|
|
832
|
+
* @example getCountryAbbreviation("+45") // "DK"
|
|
833
|
+
*/
|
|
834
|
+
const getCountryAbbreviation = (callCode) => {
|
|
835
|
+
let code = callCode;
|
|
836
|
+
if (callCode.startsWith("+")) {
|
|
837
|
+
code = callCode.substring(1);
|
|
838
|
+
}
|
|
839
|
+
return getCountries().find(c => getCountryCallingCode(c) === code) || "";
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* A custom hook for managing phone number input state and validation.
|
|
844
|
+
*
|
|
845
|
+
* @property {Function} getPhoneNumber - A function for get formatted phone number with country code and plus sign
|
|
846
|
+
*/
|
|
847
|
+
const usePhoneInput = () => {
|
|
848
|
+
const getPhoneNumber = ({ country, phone }) => {
|
|
849
|
+
if (country) {
|
|
850
|
+
return getPhoneNumberWithPlus(`${country}${phone || ""}`);
|
|
851
|
+
}
|
|
852
|
+
return phone || "";
|
|
853
|
+
};
|
|
854
|
+
return {
|
|
855
|
+
getPhoneNumber,
|
|
856
|
+
};
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
const cvaSelect = cvaMerge([
|
|
860
|
+
"relative",
|
|
861
|
+
"flex",
|
|
862
|
+
"shadow-sm",
|
|
863
|
+
"rounded-lg",
|
|
864
|
+
"border",
|
|
865
|
+
"border-slate-300",
|
|
866
|
+
"focus-within:ring-2",
|
|
867
|
+
"focus-within:ring-inset",
|
|
868
|
+
"focus-within:ring-primary-600",
|
|
869
|
+
"focus-within:border-slate-400",
|
|
870
|
+
"hover:border-slate-400",
|
|
871
|
+
"hover:bg-slate-50",
|
|
872
|
+
"bg-white",
|
|
873
|
+
"transition",
|
|
874
|
+
], {
|
|
875
|
+
variants: {
|
|
876
|
+
invalid: {
|
|
877
|
+
true: "border-red-600 text-red-600 focus-within:ring-red-600 hover:border-red-600",
|
|
878
|
+
false: "",
|
|
879
|
+
},
|
|
880
|
+
disabled: {
|
|
881
|
+
true: "!bg-slate-100",
|
|
882
|
+
false: "",
|
|
883
|
+
},
|
|
884
|
+
},
|
|
885
|
+
defaultVariants: {
|
|
886
|
+
invalid: false,
|
|
887
|
+
disabled: false,
|
|
888
|
+
},
|
|
889
|
+
});
|
|
890
|
+
const cvaSelectIcon = cvaMerge("mr-2 flex cursor-pointer items-center justify-center text-slate-400 hover:text-slate-500");
|
|
891
|
+
const cvaSelectPrefix = cvaMerge(["flex", "justify-center", "items-center", "text-slate-400", "pl-2"]);
|
|
892
|
+
const cvaSelectXIcon = cvaMerge([
|
|
893
|
+
"mr-2 flex cursor-pointer items-center justify-center text-slate-400 hover:text-slate-500",
|
|
894
|
+
"ml-1",
|
|
895
|
+
]);
|
|
896
|
+
const cvaSelectMenuList = cvaMerge(["min-w-min", "shadow-md", "rounded-lg", "z-20", "bg-white", "p-1", "border", "border-slate-300", "gap-1", "grid"], {
|
|
897
|
+
variants: {
|
|
898
|
+
menuIsOpen: {
|
|
899
|
+
true: "animate-fade-in-fast",
|
|
900
|
+
false: "animate-fade-out-fast",
|
|
901
|
+
},
|
|
902
|
+
},
|
|
903
|
+
});
|
|
904
|
+
const cvaSelectDynamicTagContainer = cvaMerge(["h-full", "flex", "gap-1", "items-center"], {
|
|
905
|
+
variants: {
|
|
906
|
+
visible: { true: "visible", false: "invisible" },
|
|
907
|
+
},
|
|
908
|
+
});
|
|
909
|
+
const cvaSelectCounter = cvaMerge(["overflow-hidden", "whitespace-nowrap"]);
|
|
910
|
+
const cvaSelectMenu = cvaMerge(["relative", "p-1", "grid", "gap-1"]);
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* @param {MultiValue<Option> | SingleValue<Option>} arg option to check type
|
|
914
|
+
* @returns {arg is MultiValue<Option> } is Multivalue
|
|
915
|
+
*/
|
|
916
|
+
function isMultiValue(arg) {
|
|
917
|
+
return Array.isArray(arg);
|
|
918
|
+
}
|
|
919
|
+
function isGroupBase(arg) {
|
|
920
|
+
return arg.options !== undefined;
|
|
921
|
+
}
|
|
922
|
+
const isSelectedOption = (option, selected) => {
|
|
923
|
+
if (isGroupBase(option)) {
|
|
924
|
+
return false;
|
|
925
|
+
}
|
|
926
|
+
return isMultiValue(selected)
|
|
927
|
+
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
928
|
+
selected.some(v => v.value === option.value)
|
|
929
|
+
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
930
|
+
option.value === (selected === null || selected === void 0 ? void 0 : selected.value);
|
|
931
|
+
};
|
|
932
|
+
const removeSelectedFromGroups = (group, selected) => {
|
|
933
|
+
if (isGroupBase(group)) {
|
|
934
|
+
return Object.assign(Object.assign({}, group), { options: group.options.filter(option => !isSelectedOption(option, selected)) });
|
|
935
|
+
}
|
|
936
|
+
return group;
|
|
937
|
+
};
|
|
938
|
+
/**
|
|
939
|
+
* @template IsMulti
|
|
940
|
+
* @template Group
|
|
941
|
+
* @param {OptionsOrGroups<Option, Group> | undefined} options An array of options to select from
|
|
942
|
+
* @param {PropsValue<Option> | undefined} value Selected values
|
|
943
|
+
* @returns {OptionsOrGroups<Option, Group>} An array of ordered options with selected on top
|
|
1212
944
|
*/
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
945
|
+
const getOrderedOptions = (options, value) => {
|
|
946
|
+
if (value && options) {
|
|
947
|
+
const orderedValues = isMultiValue(value)
|
|
948
|
+
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
949
|
+
[...value].sort((a, b) => a.label.localeCompare(b.label))
|
|
950
|
+
: [value];
|
|
951
|
+
const selectableOptions = options
|
|
952
|
+
.filter(option => !isSelectedOption(option, value))
|
|
953
|
+
.map(option => removeSelectedFromGroups(option, value));
|
|
954
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
955
|
+
return orderedValues.concat(selectableOptions) || [];
|
|
1219
956
|
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
if (onChange) {
|
|
1223
|
-
onChange(event);
|
|
1224
|
-
}
|
|
1225
|
-
}, [onChange]);
|
|
1226
|
-
return (jsx(FormGroup, { dataTestId: dataTestId && `${dataTestId}-FormGroup`, disabled: rest.disabled, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, tip: tip, children: jsx(EmailInput, Object.assign({ "aria-labelledby": htmlForId + "-label", defaultValue: defaultValue, disabled: renderAsInvalid, id: htmlForId, isInvalid: renderAsInvalid, onChange: handleChange, ref: ref, value: value }, rest, { className: className, dataTestId: dataTestId })) }));
|
|
1227
|
-
});
|
|
957
|
+
return options || [];
|
|
958
|
+
};
|
|
1228
959
|
|
|
1229
960
|
/**
|
|
1230
|
-
* A
|
|
961
|
+
* A single select menu item is a basic wrapper around Menu item designed to be used as a single value render in Select list
|
|
1231
962
|
*
|
|
1232
|
-
*
|
|
963
|
+
* @param {SelectMenuItemProps} props - The props for the SingleSelectMenuItem
|
|
964
|
+
* @returns {JSX.Element} SingleSelectMenuItem
|
|
1233
965
|
*/
|
|
1234
|
-
const
|
|
1235
|
-
return jsx(
|
|
1236
|
-
}
|
|
1237
|
-
|
|
966
|
+
const SingleSelectMenuItem = ({ label, icon, onClick, selected, dataTestId, focused, disabled, }) => {
|
|
967
|
+
return (jsx(MenuItem, { dataTestId: dataTestId, disabled: disabled, focused: focused, label: label, onClick: onClick, prefix: icon, selected: selected, suffix: selected ? jsx(Icon, { name: "Check", size: "small" }) : undefined }));
|
|
968
|
+
};
|
|
1238
969
|
/**
|
|
1239
|
-
*
|
|
1240
|
-
*
|
|
1241
|
-
* _**Do use**_ the NumberField when the controls to incrementally increase or decrease makes the task easier for the user.
|
|
970
|
+
* A multi select menu item is a basic wrapper around Menu item designed to be used as a multi value render in Select list
|
|
1242
971
|
*
|
|
1243
|
-
*
|
|
1244
|
-
|
|
1245
|
-
const NumberField = forwardRef((_a, ref) => {
|
|
1246
|
-
var { label, id, tip, helpText, errorMessage, helpAddon, isInvalid, maxLength, className, value, dataTestId } = _a, rest = __rest(_a, ["label", "id", "tip", "helpText", "errorMessage", "helpAddon", "isInvalid", "maxLength", "className", "value", "dataTestId"]);
|
|
1247
|
-
const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
|
|
1248
|
-
const htmlForId = id ? id : "numberField-" + v4();
|
|
1249
|
-
return (jsx(FormGroup, { dataTestId: dataTestId && `${dataTestId}-FormGroup`, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, tip: tip, children: jsx(NumberInput, Object.assign({ "aria-labelledby": htmlForId + "-label", id: htmlForId, isInvalid: renderAsInvalid, maxLength: maxLength, ref: ref, value: value }, rest, { className: className, dataTestId: dataTestId })) }));
|
|
1250
|
-
});
|
|
1251
|
-
|
|
1252
|
-
const cvaOptionCardLabel = cvaMerge([
|
|
1253
|
-
cvaInputBase(),
|
|
1254
|
-
"text-center",
|
|
1255
|
-
"component-baseInput-background",
|
|
1256
|
-
"aspect-square",
|
|
1257
|
-
"flex",
|
|
1258
|
-
"flex-col",
|
|
1259
|
-
"gap-2",
|
|
1260
|
-
"justify-center",
|
|
1261
|
-
"items-center",
|
|
1262
|
-
"p-responsive-space",
|
|
1263
|
-
"w-full",
|
|
1264
|
-
"peer-checked:bg-primary-50",
|
|
1265
|
-
"peer-checked:border-primary-600",
|
|
1266
|
-
"peer-checked:border-2",
|
|
1267
|
-
], {
|
|
1268
|
-
variants: {
|
|
1269
|
-
disabled: {
|
|
1270
|
-
true: [cvaInputBaseDisabled(), "cursor-not-allowed"],
|
|
1271
|
-
false: ["cursor-pointer"],
|
|
1272
|
-
},
|
|
1273
|
-
},
|
|
1274
|
-
});
|
|
1275
|
-
const cvaOptionCardContent = cvaMerge(["flex", "flex-col", "items-center"]);
|
|
1276
|
-
const cvaOptionCardContainer = cvaMerge(["contents"]);
|
|
1277
|
-
|
|
1278
|
-
/**
|
|
1279
|
-
* A card version of a radio button that includes an icon, headings and a description.
|
|
972
|
+
* @param {SelectMenuItemProps} props - The props for the MultiSelectMenuItem
|
|
973
|
+
* @returns {JSX.Element} multi select menu item
|
|
1280
974
|
*/
|
|
1281
|
-
const
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
})
|
|
975
|
+
const MultiSelectMenuItem = ({ label, onClick, selected, dataTestId, focused, disabled, }) => {
|
|
976
|
+
return (jsx(MenuItem, { dataTestId: dataTestId, disabled: disabled, focused: focused, label: label, onClick: e => {
|
|
977
|
+
e.stopPropagation();
|
|
978
|
+
onClick && onClick(e);
|
|
979
|
+
}, prefix: jsx(Checkbox, { checked: selected, disabled: disabled, onChange: () => null, onClick: e => {
|
|
980
|
+
e.stopPropagation();
|
|
981
|
+
}, readOnly: false }), selected: selected }));
|
|
982
|
+
};
|
|
1286
983
|
|
|
1287
984
|
/**
|
|
1288
|
-
*
|
|
985
|
+
* Extended Tag component with information about its own width.
|
|
986
|
+
* Used in the select component.
|
|
1289
987
|
*
|
|
1290
|
-
*
|
|
988
|
+
* @param {TagProps} props - The props for the tag component
|
|
989
|
+
* @returns {JSX.Element} TagWithWidth component
|
|
1291
990
|
*/
|
|
1292
|
-
const
|
|
991
|
+
const TagWithWidth = (_a) => {
|
|
992
|
+
var { onWidthKnown, children } = _a, rest = __rest(_a, ["onWidthKnown", "children"]);
|
|
993
|
+
const ref = React__default.useRef(null);
|
|
994
|
+
React__default.useLayoutEffect(() => {
|
|
995
|
+
var _a;
|
|
996
|
+
onWidthKnown && onWidthKnown({ width: ((_a = ref.current) === null || _a === void 0 ? void 0 : _a.offsetWidth) || 0 });
|
|
997
|
+
}, [ref, onWidthKnown]);
|
|
998
|
+
return (jsx(Tag, Object.assign({ ref: ref }, rest, { children: children })));
|
|
999
|
+
};
|
|
1293
1000
|
|
|
1294
1001
|
/**
|
|
1295
|
-
*
|
|
1296
|
-
*
|
|
1297
|
-
* _**Do use** when the user has to input a password or something that needs to be obfuscated_
|
|
1002
|
+
* TagsContainer component to display tags in limited space when children can't fit space it displays counter
|
|
1298
1003
|
*
|
|
1299
|
-
*
|
|
1004
|
+
* @param {TagsContainerProps} props - The props for the TagContainer
|
|
1005
|
+
* @returns {JSX.Element} TagsContainer
|
|
1300
1006
|
*/
|
|
1301
|
-
const
|
|
1302
|
-
|
|
1303
|
-
const
|
|
1304
|
-
const
|
|
1305
|
-
const
|
|
1306
|
-
const
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1007
|
+
const TagsContainer = ({ items, width = "100%", itemsGap = 5, postFix, disabled }) => {
|
|
1008
|
+
const [isReady, setIsReady] = React__default.useState(false);
|
|
1009
|
+
const [counterWidth, setCounterWidth] = React__default.useState(0);
|
|
1010
|
+
const containerRef = React__default.useRef(null);
|
|
1011
|
+
const availableWidth = React__default.useRef();
|
|
1012
|
+
const childrenWidth = React__default.useRef([]);
|
|
1013
|
+
const itemsCount = items.length;
|
|
1014
|
+
React__default.useLayoutEffect(() => {
|
|
1015
|
+
var _a;
|
|
1016
|
+
availableWidth.current = ((_a = containerRef === null || containerRef === void 0 ? void 0 : containerRef.current) === null || _a === void 0 ? void 0 : _a.offsetWidth) || 0;
|
|
1017
|
+
}, [containerRef]);
|
|
1018
|
+
const onWidthKnownHandler = ({ width: reportedWidth }) => {
|
|
1019
|
+
childrenWidth.current.push({ width: reportedWidth + itemsGap });
|
|
1020
|
+
if (childrenWidth.current.length === itemsCount) {
|
|
1021
|
+
setIsReady(true);
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
const requiredSpace = childrenWidth.current.reduce((previous, current) => {
|
|
1025
|
+
return previous + current.width;
|
|
1026
|
+
}, 0);
|
|
1027
|
+
let counter = 0;
|
|
1028
|
+
const availableSpace = ((availableWidth === null || availableWidth === void 0 ? void 0 : availableWidth.current) || 0) - counterWidth;
|
|
1029
|
+
const renderedElements = items
|
|
1030
|
+
.concat({ text: "", onClick: () => null, disabled: false }) // reserved element for a potential counter
|
|
1031
|
+
.map((item, index) => {
|
|
1032
|
+
const spaceNeeded = childrenWidth.current.slice(0, index + 1).reduce((previous, current) => {
|
|
1033
|
+
return previous + current.width;
|
|
1034
|
+
}, 0);
|
|
1035
|
+
const isLast = index === items.length;
|
|
1036
|
+
const counterRequired = requiredSpace > availableSpace && counter !== 0;
|
|
1037
|
+
if (isLast && counterRequired) {
|
|
1038
|
+
return (jsx(TagWithWidth, { color: "white", disabled: disabled, onWidthKnown: ({ width: reportedWidth }) => setCounterWidth(reportedWidth), children: jsxs("div", { className: cvaSelectCounter(), "data-testid": "select-counter", children: ["+", counter] }) }, item.text + index));
|
|
1039
|
+
}
|
|
1040
|
+
if (isLast) {
|
|
1041
|
+
return null;
|
|
1042
|
+
}
|
|
1043
|
+
const itemCanFit = spaceNeeded <= availableSpace;
|
|
1044
|
+
if (itemCanFit) {
|
|
1045
|
+
return (jsx(TagWithWidth, { className: "inline-flex shrink-0", color: item.disabled ? "unknown" : "primary", dataTestId: `${item.text}-tag`, disabled: disabled, onClose: e => {
|
|
1046
|
+
e.stopPropagation();
|
|
1047
|
+
item.onClick();
|
|
1048
|
+
}, onWidthKnown: onWidthKnownHandler, children: item.text }, item.text + index));
|
|
1049
|
+
}
|
|
1050
|
+
const fromTheItems = item.text !== "";
|
|
1051
|
+
if (fromTheItems) {
|
|
1052
|
+
counter = counter + 1;
|
|
1053
|
+
}
|
|
1054
|
+
return null;
|
|
1055
|
+
})
|
|
1056
|
+
.filter(element => element !== null);
|
|
1057
|
+
return (jsxs("div", { className: cvaSelectDynamicTagContainer({ visible: isReady }), ref: containerRef, style: {
|
|
1058
|
+
width: `${width}`,
|
|
1059
|
+
}, children: [renderedElements, postFix] }));
|
|
1060
|
+
};
|
|
1311
1061
|
|
|
1312
1062
|
/**
|
|
1313
|
-
*
|
|
1314
|
-
*
|
|
1315
|
-
*
|
|
1316
|
-
*
|
|
1063
|
+
* A hook to retrieve components override object.
|
|
1064
|
+
* This complex object includes all the compositional components that are used in react-select. If you wish to overwrite a component, pass in an object with the appropriate namespace.
|
|
1065
|
+
*
|
|
1066
|
+
* @template IsMulti
|
|
1067
|
+
* @template Group
|
|
1068
|
+
* @param {Partial<SelectComponents<Option, IsMulti, Group>> | undefined} componentsProps a custom component prop that you can to override defaults
|
|
1069
|
+
* @param {boolean} disabled decide to override disabled variant
|
|
1070
|
+
* @param {boolean} menuIsOpen menu is open state
|
|
1071
|
+
* @param {React.MutableRefObject<boolean>} refMenuIsEnabled a flag to block menu from open
|
|
1072
|
+
* @param {string} dataTestId a test id
|
|
1073
|
+
* @param {number} maxSelectedDisplayCount a number of max display count
|
|
1074
|
+
* @param {JSX.Element} dropdownIcon an custom dropdown icon
|
|
1075
|
+
* @returns {Partial<SelectComponents<Option, boolean, GroupBase<Option>>> | undefined} components object to override react-select default components
|
|
1317
1076
|
*/
|
|
1318
|
-
const
|
|
1319
|
-
|
|
1077
|
+
const useCustomComponents = (componentsProps, disabled, menuIsOpen, refMenuIsEnabled, dataTestId, maxSelectedDisplayCount, dropdownIcon) => {
|
|
1078
|
+
const [t] = useTranslation();
|
|
1079
|
+
// perhaps it should not be wrap in memo (causing some issues with opening and closing on mobiles)
|
|
1080
|
+
const customComponents = React.useMemo(() => {
|
|
1081
|
+
return Object.assign({ ValueContainer: props => {
|
|
1082
|
+
if (props.isMulti && Array.isArray(props.children) && props.children.length > 0) {
|
|
1083
|
+
const PLACEHOLDER_KEY = "placeholder";
|
|
1084
|
+
const key = props && props.children && props.children[0] ? props.children[0].key : "";
|
|
1085
|
+
const values = props && props.children ? props.children[0] : [];
|
|
1086
|
+
const tags = key === PLACEHOLDER_KEY ? [] : values;
|
|
1087
|
+
const searchInput = props && props.children && props.children[1];
|
|
1088
|
+
return (jsx(components.ValueContainer, Object.assign({}, props, { isDisabled: props.selectProps.isDisabled, children: maxSelectedDisplayCount === undefined ? (jsx(TagsContainer, { disabled: disabled, items: tags
|
|
1089
|
+
? tags.map(({ props: tagProps }) => {
|
|
1090
|
+
return {
|
|
1091
|
+
text: tagProps.children,
|
|
1092
|
+
onClick: disabled
|
|
1093
|
+
? undefined
|
|
1094
|
+
: (e) => {
|
|
1095
|
+
var _a, _b;
|
|
1096
|
+
refMenuIsEnabled.current = false;
|
|
1097
|
+
((_a = tagProps === null || tagProps === void 0 ? void 0 : tagProps.removeProps) === null || _a === void 0 ? void 0 : _a.onClick) && ((_b = tagProps === null || tagProps === void 0 ? void 0 : tagProps.removeProps) === null || _b === void 0 ? void 0 : _b.onClick(e));
|
|
1098
|
+
},
|
|
1099
|
+
disabled: disabled,
|
|
1100
|
+
};
|
|
1101
|
+
})
|
|
1102
|
+
: [], postFix: searchInput, width: "100%" })) : (jsxs(Fragment, { children: [tags &&
|
|
1103
|
+
tags.slice(0, maxSelectedDisplayCount).map(({ props: tagProps }) => {
|
|
1104
|
+
var _a, _b;
|
|
1105
|
+
return (jsx(Tag, { className: "inline-flex shrink-0", color: disabled ? "unknown" : "primary", dataTestId: tagProps.children ? `${(_a = tagProps.children) === null || _a === void 0 ? void 0 : _a.toString()}-tag` : undefined, onClose: e => {
|
|
1106
|
+
var _a, _b;
|
|
1107
|
+
e.stopPropagation();
|
|
1108
|
+
refMenuIsEnabled.current = false;
|
|
1109
|
+
((_a = tagProps === null || tagProps === void 0 ? void 0 : tagProps.removeProps) === null || _a === void 0 ? void 0 : _a.onClick) && ((_b = tagProps === null || tagProps === void 0 ? void 0 : tagProps.removeProps) === null || _b === void 0 ? void 0 : _b.onClick(e));
|
|
1110
|
+
}, children: tagProps.children }, (_b = tagProps.children) === null || _b === void 0 ? void 0 : _b.toString()));
|
|
1111
|
+
}), tags && tags.length > maxSelectedDisplayCount && (jsxs(Tag, { color: "neutral", dataTestId: "counter-tag", children: ["+", tags.length - maxSelectedDisplayCount] })), searchInput] })) })));
|
|
1112
|
+
}
|
|
1113
|
+
return (jsx(components.ValueContainer, Object.assign({}, props, { isDisabled: props.selectProps.isDisabled, children: props.children })));
|
|
1114
|
+
}, LoadingIndicator: props => {
|
|
1115
|
+
return jsx(Spinner, { className: "mt-1.5 mr-1", size: "small" });
|
|
1116
|
+
}, DropdownIndicator: props => {
|
|
1117
|
+
const icon = props.selectProps.menuIsOpen ? (jsx(Icon, { name: "ChevronUp", size: "medium" })) : (jsx(Icon, { name: "ChevronDown", size: "medium" }));
|
|
1118
|
+
return props.selectProps.isLoading ? null : (jsx(components.DropdownIndicator, Object.assign({}, props, { children: jsx("div", { className: cvaSelectIcon(), children: dropdownIcon ? dropdownIcon : icon }) })));
|
|
1119
|
+
}, IndicatorSeparator: () => null, ClearIndicator: props => {
|
|
1120
|
+
if (disabled) {
|
|
1121
|
+
return null;
|
|
1122
|
+
}
|
|
1123
|
+
return (jsx(components.ClearIndicator, Object.assign({}, props, { children: jsx("div", { className: cvaSelectXIcon(), "data-testid": dataTestId && `${dataTestId}-XMarkIcon`, onClick: props.clearValue, children: jsx(Icon, { name: "XCircle", size: "medium", title: t("clearIndicator.icon.tooltip.clearAll") }) }) })));
|
|
1124
|
+
}, Control: props => {
|
|
1125
|
+
return jsx(components.Control, Object.assign({}, props, { className: props.isDisabled ? "bg-slate-100" : "" }));
|
|
1126
|
+
}, SingleValue: props => {
|
|
1127
|
+
return (jsx(components.SingleValue, Object.assign({}, props, { className: props.isDisabled ? "text-slate-700" : "", children: jsx("div", { "data-testid": dataTestId + "-singleValue", children: props.children }) })));
|
|
1128
|
+
}, Menu: props => {
|
|
1129
|
+
return (jsx(components.Menu, Object.assign({}, props, { className: cvaSelectMenuList({ menuIsOpen: props.selectProps.menuIsOpen }) })));
|
|
1130
|
+
}, MenuList: props => {
|
|
1131
|
+
return (jsx(components.MenuList, Object.assign({}, props, { innerProps: Object.assign(Object.assign({}, props.innerProps), { onScroll: e => {
|
|
1132
|
+
const listEl = e.currentTarget;
|
|
1133
|
+
if (listEl.scrollTop + listEl.clientHeight >= listEl.scrollHeight) {
|
|
1134
|
+
props.selectProps.onMenuScrollToBottom && props.selectProps.onMenuScrollToBottom(new TouchEvent(""));
|
|
1135
|
+
}
|
|
1136
|
+
} }), children: props.children })));
|
|
1137
|
+
}, Option: props => {
|
|
1138
|
+
const componentProps = {
|
|
1139
|
+
label: props.label,
|
|
1140
|
+
focused: props.isFocused,
|
|
1141
|
+
selected: props.isSelected,
|
|
1142
|
+
onClick: props.innerProps.onClick,
|
|
1143
|
+
};
|
|
1144
|
+
return (jsx(components.Option, Object.assign({}, props, { innerProps: Object.assign(Object.assign({}, props.innerProps), { role: "option", onClick: () => { } }), children: props.isMulti ? (jsx(MultiSelectMenuItem, Object.assign({}, componentProps, { dataTestId: typeof props.label === "string" ? props.label : undefined, disabled: disabled }))) : (jsx(SingleSelectMenuItem, Object.assign({}, componentProps, { dataTestId: typeof props.label === "string" ? props.label : undefined, disabled: disabled || props.isDisabled }))) })));
|
|
1145
|
+
} }, componentsProps);
|
|
1146
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1147
|
+
}, [componentsProps, disabled, maxSelectedDisplayCount]); // do not add dropdownIcon (it will cause issue with opening/closing list for selects with custom icon)
|
|
1148
|
+
return customComponents;
|
|
1320
1149
|
};
|
|
1150
|
+
|
|
1321
1151
|
/**
|
|
1322
|
-
* @
|
|
1323
|
-
* @
|
|
1324
|
-
* @
|
|
1152
|
+
* @template IsMulti
|
|
1153
|
+
* @template Group
|
|
1154
|
+
* @param {React.RefObject<HTMLDivElement>} refContainer react ref to container element
|
|
1155
|
+
* @param {React.RefObject<HTMLDivElement>} refPrefix react ref to prefix element
|
|
1156
|
+
* @param {number | undefined} maxSelectedDisplayCount a number of max display count
|
|
1157
|
+
* @param {StylesConfig<Option, IsMulti, Group> | undefined} styles a optional object to override styles of react-select
|
|
1158
|
+
* @returns {StylesConfig<Option, boolean>} styles to override in select
|
|
1325
1159
|
*/
|
|
1326
|
-
const
|
|
1327
|
-
const
|
|
1328
|
-
|
|
1160
|
+
const useCustomStyles = (refContainer, refPrefix, maxSelectedDisplayCount, styles, disabled) => {
|
|
1161
|
+
const customStyles = React.useMemo(() => {
|
|
1162
|
+
return Object.assign({ control: base => {
|
|
1163
|
+
return Object.assign(Object.assign({}, base), { border: "0", boxShadow: "0", "&:hover": {
|
|
1164
|
+
border: "0",
|
|
1165
|
+
}, marginRight: "2px", backgroundColor: "" });
|
|
1166
|
+
}, singleValue: base => (Object.assign({}, base)), multiValue: base => (Object.assign({}, base)), multiValueLabel: base => (Object.assign({}, base)), indicatorsContainer: base => (Object.assign(Object.assign({}, base), (disabled && { display: "none" }))), indicatorSeparator: () => ({
|
|
1167
|
+
width: "0px",
|
|
1168
|
+
}), menu: base => {
|
|
1169
|
+
return Object.assign(Object.assign({}, base), { width: "100%", marginTop: "4px", marginBottom: "18px", transition: "all 1s ease-in-out" });
|
|
1170
|
+
}, input: base => (Object.assign(Object.assign({}, base), { marginLeft: "0px" })), placeholder: base => (Object.assign({}, base)), option: () => ({}), menuPortal: base => (Object.assign(Object.assign({}, base), { width: (refContainer === null || refContainer === void 0 ? void 0 : refContainer.current) ? `${refContainer.current.clientWidth}px` : base.width, transform: (refPrefix === null || refPrefix === void 0 ? void 0 : refPrefix.current) ? `translate(-${refPrefix.current.clientWidth + 2}px)` : "translate(-2px)", backgroundColor: "#ffffff", borderRadius: "var(--border-radius-lg)", zIndex: 20, borderColor: "rgb(var(--color-slate-300))", boxShadow: "var(--tw-ring-inset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)" })), menuList: base => {
|
|
1171
|
+
return Object.assign(Object.assign({}, base), { position: "relative", padding: "var(--spacing-1)", display: "grid", gap: "var(--spacing-1)", width: "100%", borderRadius: "0px", boxShadow: "none", paddingTop: "0px" });
|
|
1172
|
+
}, valueContainer: base => {
|
|
1173
|
+
return Object.assign(Object.assign({}, base), { flexWrap: maxSelectedDisplayCount !== undefined ? "wrap" : "nowrap", gap: "0.25rem" });
|
|
1174
|
+
}, container: base => (Object.assign(Object.assign({}, base), { border: "none", width: "calc(100% - 2px)", paddingLeft: "2px" })), dropdownIndicator: base => (Object.assign(Object.assign({}, base), { padding: "0px" })), clearIndicator: base => {
|
|
1175
|
+
return Object.assign(Object.assign({}, base), { padding: "0px" });
|
|
1176
|
+
} }, styles);
|
|
1177
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1178
|
+
}, [refContainer, refPrefix]);
|
|
1179
|
+
return { customStyles };
|
|
1329
1180
|
};
|
|
1181
|
+
|
|
1330
1182
|
/**
|
|
1331
|
-
*
|
|
1183
|
+
* A hook used by selects to share the common code
|
|
1332
1184
|
*
|
|
1333
|
-
* @param {
|
|
1334
|
-
* @returns {
|
|
1335
|
-
* @example getPhoneNumberWithPlus("DK") // "🇩🇰"
|
|
1185
|
+
* @param {SelectProps} props - The props for the Select component
|
|
1186
|
+
* @returns {IUseSelect} Select component
|
|
1336
1187
|
*/
|
|
1337
|
-
const
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1188
|
+
const useSelect = (_a) => {
|
|
1189
|
+
var _b;
|
|
1190
|
+
var { id, className, dataTestId = "select", prefix, async, dropdownIcon, maxMenuHeight = 200, label, hasError, disabled, isMulti, components, value, options, onChange, isLoading, classNamePrefix = "", onMenuOpen, onMenuClose, maxSelectedDisplayCount = undefined, isClearable = false, isSearchable = true, onMenuScrollToBottom, styles, filterOption, onInputChange } = _a, props = __rest(_a, ["id", "className", "dataTestId", "prefix", "async", "dropdownIcon", "maxMenuHeight", "label", "hasError", "disabled", "isMulti", "components", "value", "options", "onChange", "isLoading", "classNamePrefix", "onMenuOpen", "onMenuClose", "maxSelectedDisplayCount", "isClearable", "isSearchable", "onMenuScrollToBottom", "styles", "filterOption", "onInputChange"]);
|
|
1191
|
+
const refContainer = React__default.useRef(null);
|
|
1192
|
+
const refPrefix = React__default.useRef(null);
|
|
1193
|
+
const { customStyles } = useCustomStyles(refContainer, refPrefix, maxSelectedDisplayCount, styles, disabled);
|
|
1194
|
+
const [menuIsOpen, setMenuIsOpen] = React__default.useState((_b = props.menuIsOpen) !== null && _b !== void 0 ? _b : false);
|
|
1195
|
+
const refMenuIsEnabled = React__default.useRef(true);
|
|
1196
|
+
const customComponents = useCustomComponents(components, disabled || false, menuIsOpen, refMenuIsEnabled, dataTestId, maxSelectedDisplayCount, dropdownIcon);
|
|
1197
|
+
const menuPlacement = "auto";
|
|
1198
|
+
const openMenuHandler = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1199
|
+
onMenuOpen && onMenuOpen();
|
|
1200
|
+
if (refMenuIsEnabled.current) {
|
|
1201
|
+
setMenuIsOpen(true);
|
|
1202
|
+
}
|
|
1203
|
+
else {
|
|
1204
|
+
refMenuIsEnabled.current = true;
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
const closeMenuHandler = () => {
|
|
1208
|
+
setMenuIsOpen(false);
|
|
1209
|
+
onMenuClose && onMenuClose();
|
|
1210
|
+
};
|
|
1211
|
+
const orderedOptions = React__default.useMemo(() => {
|
|
1212
|
+
return disabled
|
|
1213
|
+
? getOrderedOptions(options, value).map(option => {
|
|
1214
|
+
return Object.assign(Object.assign({}, option), { disabled: true });
|
|
1215
|
+
})
|
|
1216
|
+
: getOrderedOptions(options, value);
|
|
1217
|
+
}, [options, value, disabled]);
|
|
1218
|
+
return {
|
|
1219
|
+
refContainer,
|
|
1220
|
+
refPrefix,
|
|
1221
|
+
customStyles,
|
|
1222
|
+
menuIsOpen,
|
|
1223
|
+
customComponents,
|
|
1224
|
+
menuPlacement,
|
|
1225
|
+
openMenuHandler,
|
|
1226
|
+
closeMenuHandler,
|
|
1227
|
+
orderedOptions,
|
|
1228
|
+
};
|
|
1348
1229
|
};
|
|
1230
|
+
|
|
1349
1231
|
/**
|
|
1350
|
-
*
|
|
1232
|
+
* CreatableSelects are input components used to choose a value from a set.
|
|
1351
1233
|
*
|
|
1352
|
-
* @param {
|
|
1353
|
-
* @returns {
|
|
1354
|
-
* @example getCountryAbbreviation("45") // "DK"
|
|
1355
|
-
* @example getCountryAbbreviation("+45") // "DK"
|
|
1234
|
+
* @param {CreatableSelectProps} props - The props for the CreatableSelect component
|
|
1235
|
+
* @returns {JSX.Element} CreatableSelect component
|
|
1356
1236
|
*/
|
|
1357
|
-
const
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1237
|
+
const CreatableSelect = (props) => {
|
|
1238
|
+
const { className } = props, propsNoClassName = __rest(props, ["className"]);
|
|
1239
|
+
const { id, dataTestId = "creatableSelect", prefix, async, maxMenuHeight = 200, label, hasError, disabled, isMulti, value, options, onChange, isLoading, classNamePrefix = dataTestId !== null && dataTestId !== void 0 ? dataTestId : "creatableSelect", onMenuScrollToBottom, filterOption, onInputChange, isSearchable, isClearable = false, readOnly, allowCreateWhileLoading, onCreateOption, } = propsNoClassName;
|
|
1240
|
+
const { refContainer, refPrefix, customStyles, menuIsOpen, customComponents, menuPlacement, openMenuHandler, closeMenuHandler, orderedOptions, } = useSelect(props);
|
|
1241
|
+
const creatableSelectProps = {
|
|
1242
|
+
value,
|
|
1243
|
+
menuPlacement,
|
|
1244
|
+
maxMenuHeight,
|
|
1245
|
+
onChange,
|
|
1246
|
+
"aria-label": label,
|
|
1247
|
+
"data-testid": dataTestId,
|
|
1248
|
+
components: customComponents,
|
|
1249
|
+
styles: customStyles,
|
|
1250
|
+
tabSelectsValue: false,
|
|
1251
|
+
blurInputOnSelect: !isMulti,
|
|
1252
|
+
menuPortalTarget: props.menuPortalTarget || document.body,
|
|
1253
|
+
isSearchable: disabled || readOnly ? false : isSearchable,
|
|
1254
|
+
menuShouldBlockScroll: true,
|
|
1255
|
+
menuShouldScrollIntoView: true,
|
|
1256
|
+
openMenuOnFocus: true,
|
|
1257
|
+
menuIsOpen: !readOnly ? menuIsOpen : false,
|
|
1258
|
+
openMenuOnClick: true,
|
|
1259
|
+
closeMenuOnSelect: false,
|
|
1260
|
+
isMulti,
|
|
1261
|
+
classNamePrefix,
|
|
1262
|
+
isLoading,
|
|
1263
|
+
isClearable,
|
|
1264
|
+
id,
|
|
1265
|
+
onMenuScrollToBottom,
|
|
1266
|
+
onInputChange,
|
|
1267
|
+
allowCreateWhileLoading,
|
|
1268
|
+
onCreateOption,
|
|
1269
|
+
};
|
|
1270
|
+
return (jsxs("div", { className: cvaSelect({ invalid: hasError, disabled: disabled || readOnly, className }), "data-testid": dataTestId, ref: refContainer, children: [prefix !== undefined && (jsx("div", { className: cvaSelectPrefix(), "data-testid": dataTestId && `${dataTestId}-prefix`, ref: refPrefix, children: prefix })), async ? (jsx(ReactAsyncCreatableSelect, Object.assign({}, propsNoClassName, creatableSelectProps, async, { onMenuClose: closeMenuHandler, onMenuOpen: openMenuHandler }))) : (jsx(ReactCreatableSelect, Object.assign({}, propsNoClassName, creatableSelectProps, { hideSelectedOptions: false, isMulti: isMulti, onMenuClose: closeMenuHandler, onMenuOpen: openMenuHandler, options: filterOption ? orderedOptions : options })))] }));
|
|
1363
1271
|
};
|
|
1272
|
+
CreatableSelect.displayName = "CreatableSelect";
|
|
1364
1273
|
|
|
1365
1274
|
/**
|
|
1366
|
-
*
|
|
1275
|
+
* Selects are input components used to choose a value from a set.
|
|
1367
1276
|
*
|
|
1368
|
-
*
|
|
1277
|
+
* @param {SelectProps} props - The props for the Select component
|
|
1278
|
+
* @returns {JSX.Element} Select component
|
|
1369
1279
|
*/
|
|
1370
|
-
const
|
|
1371
|
-
const
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1280
|
+
const Select = (props) => {
|
|
1281
|
+
const { className } = props, propsNoClassName = __rest(props, ["className"]);
|
|
1282
|
+
const { id, dataTestId = "select", prefix, async, maxMenuHeight = 200, label, hasError, disabled, isMulti, value, options, onChange, isLoading, classNamePrefix = dataTestId !== null && dataTestId !== void 0 ? dataTestId : "select", onMenuScrollToBottom, filterOption, onInputChange, isSearchable, isClearable = false, readOnly, openMenuOnClick = !disabled, openMenuOnFocus = !disabled, } = props;
|
|
1283
|
+
const { refContainer, refPrefix, customStyles, menuIsOpen, customComponents, menuPlacement, openMenuHandler, closeMenuHandler, orderedOptions, } = useSelect(props);
|
|
1284
|
+
const selectProps = {
|
|
1285
|
+
value,
|
|
1286
|
+
menuPlacement,
|
|
1287
|
+
maxMenuHeight,
|
|
1288
|
+
onChange,
|
|
1289
|
+
"aria-label": label,
|
|
1290
|
+
"data-testid": dataTestId,
|
|
1291
|
+
components: customComponents,
|
|
1292
|
+
styles: customStyles,
|
|
1293
|
+
tabSelectsValue: false,
|
|
1294
|
+
blurInputOnSelect: !isMulti,
|
|
1295
|
+
menuPortalTarget: props.menuPortalTarget || document.body,
|
|
1296
|
+
isSearchable: disabled || readOnly ? false : isSearchable,
|
|
1297
|
+
menuShouldBlockScroll: true,
|
|
1298
|
+
menuShouldScrollIntoView: true,
|
|
1299
|
+
openMenuOnFocus,
|
|
1300
|
+
menuIsOpen: !readOnly ? menuIsOpen : false,
|
|
1301
|
+
openMenuOnClick,
|
|
1302
|
+
closeMenuOnSelect: false,
|
|
1303
|
+
isMulti,
|
|
1304
|
+
classNamePrefix,
|
|
1305
|
+
isLoading,
|
|
1306
|
+
isClearable,
|
|
1307
|
+
id,
|
|
1308
|
+
onMenuScrollToBottom,
|
|
1309
|
+
onInputChange,
|
|
1379
1310
|
};
|
|
1311
|
+
return (jsxs("div", { className: cvaSelect({ invalid: hasError, disabled: disabled || readOnly, className }), "data-testid": dataTestId, ref: refContainer, children: [prefix !== undefined && (jsx("div", { className: cvaSelectPrefix(), "data-testid": dataTestId && `${dataTestId}-prefix`, ref: refPrefix, children: prefix })), async ? (jsx(ReactAsyncSelect, Object.assign({}, propsNoClassName, selectProps, async, { onMenuClose: closeMenuHandler, onMenuOpen: openMenuHandler }))) : (jsx(ReactSelect, Object.assign({}, propsNoClassName, selectProps, { hideSelectedOptions: false, isMulti: isMulti, onMenuClose: closeMenuHandler, onMenuOpen: openMenuHandler, options: filterOption ? orderedOptions : options })))] }));
|
|
1380
1312
|
};
|
|
1313
|
+
Select.displayName = "Select";
|
|
1381
1314
|
|
|
1382
1315
|
const COUNTRY_CODES_NOT_SUPPORTED_BY_BACKEND = ["+53", "+98", "+211", "+247", "+249", "+383", "+850", "+963"];
|
|
1383
1316
|
const ADDITIONAL_COUNTRY_CODES_SUPPORTED_BY_BACKEND = [
|
|
@@ -2000,6 +1933,71 @@ const Search = forwardRef((_a, ref) => {
|
|
|
2000
1933
|
}, children: jsx(Icon, { name: "XMark", size: "small" }) })) : undefined, value: value })));
|
|
2001
1934
|
});
|
|
2002
1935
|
|
|
1936
|
+
const FormFieldSelectAdapter = forwardRef((_a, ref) => {
|
|
1937
|
+
var { className, dataTestId, helpText, helpAddon, tip, label, disabled, isInvalid, errorMessage, name, onBlur, options, value, defaultValue, id, onChange, children } = _a, rest = __rest(_a, ["className", "dataTestId", "helpText", "helpAddon", "tip", "label", "disabled", "isInvalid", "errorMessage", "name", "onBlur", "options", "value", "defaultValue", "id", "onChange", "children"]);
|
|
1938
|
+
const [innerValue, setInnerValue] = useState(value || defaultValue);
|
|
1939
|
+
const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
|
|
1940
|
+
const htmlFor = useMemo(() => (id ? id : "selectField-" + v4()), [id]);
|
|
1941
|
+
const innerRef = useRef(null);
|
|
1942
|
+
useImperativeHandle(ref, () => innerRef.current, []);
|
|
1943
|
+
const handleChange = useCallback((newValue) => {
|
|
1944
|
+
setInnerValue(newValue === null || newValue === void 0 ? void 0 : newValue.value);
|
|
1945
|
+
}, []);
|
|
1946
|
+
useEffect(() => {
|
|
1947
|
+
var _a;
|
|
1948
|
+
if (!innerValue) {
|
|
1949
|
+
return;
|
|
1950
|
+
}
|
|
1951
|
+
const event = new Event("change");
|
|
1952
|
+
(_a = innerRef.current) === null || _a === void 0 ? void 0 : _a.dispatchEvent(event);
|
|
1953
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(event);
|
|
1954
|
+
}, [onChange, innerValue]);
|
|
1955
|
+
const selectedOption = options.find(option => { var _a; return option.value === ((_a = innerRef === null || innerRef === void 0 ? void 0 : innerRef.current) === null || _a === void 0 ? void 0 : _a.value); });
|
|
1956
|
+
const defaultSelectedOption = options.find(option => option.value === defaultValue);
|
|
1957
|
+
return (jsxs(FormGroup, { isInvalid: renderAsInvalid,
|
|
1958
|
+
htmlFor,
|
|
1959
|
+
className,
|
|
1960
|
+
dataTestId,
|
|
1961
|
+
helpText,
|
|
1962
|
+
helpAddon,
|
|
1963
|
+
tip,
|
|
1964
|
+
label,
|
|
1965
|
+
disabled, children: [jsx("select", { onChange, ref: innerRef, name, value: innerValue, hidden: true, children: options.map(option => {
|
|
1966
|
+
return (jsx("option", { value: option.value, children: option.label }, option.value));
|
|
1967
|
+
}) }), children(Object.assign({ id, isDisabled: disabled, onBlur,
|
|
1968
|
+
options, onChange: handleChange, value: selectedOption, defaultValue: defaultSelectedOption }, rest))] }));
|
|
1969
|
+
});
|
|
1970
|
+
|
|
1971
|
+
/**
|
|
1972
|
+
* The CreatableSelectField component is a CreatableSelect component wrapped in the FromGroup component.
|
|
1973
|
+
*
|
|
1974
|
+
* This means that it can easily be added to any form alongside other Field components.
|
|
1975
|
+
*
|
|
1976
|
+
* This is done to make the field compatible with the React-hook-form library.
|
|
1977
|
+
*
|
|
1978
|
+
* @param {SelectFieldProps & CreatableSelectProps} props - The props for the CreatableSelectField component
|
|
1979
|
+
* @returns {JSX.Element} CreatableSelectField component
|
|
1980
|
+
*/
|
|
1981
|
+
const CreatableSelectField = (_a) => {
|
|
1982
|
+
var { allowCreateWhileLoading, onCreateOption } = _a, props = __rest(_a, ["allowCreateWhileLoading", "onCreateOption"]);
|
|
1983
|
+
const creatableSelectOnlyProps = { allowCreateWhileLoading, onCreateOption };
|
|
1984
|
+
return (jsx(FormFieldSelectAdapter, Object.assign({}, props, { children: convertedProps => jsx(CreatableSelect, Object.assign({}, convertedProps, creatableSelectOnlyProps)) })));
|
|
1985
|
+
};
|
|
1986
|
+
|
|
1987
|
+
/**
|
|
1988
|
+
* The SelectField component is a Select component wrapped in the FromGroup component.
|
|
1989
|
+
*
|
|
1990
|
+
* This means that it can easily be added to any form alongside other Field components.
|
|
1991
|
+
*
|
|
1992
|
+
* This is done to make the field compatible with the React-hook-form library.
|
|
1993
|
+
*
|
|
1994
|
+
* @param {SelectFieldProps} props - The props for the SelectField component
|
|
1995
|
+
* @returns {JSX.Element} SelectField component
|
|
1996
|
+
*/
|
|
1997
|
+
const SelectField = (props) => {
|
|
1998
|
+
return jsx(FormFieldSelectAdapter, Object.assign({}, props, { children: convertedProps => jsx(Select, Object.assign({}, convertedProps)) }));
|
|
1999
|
+
};
|
|
2000
|
+
|
|
2003
2001
|
const cvaTextArea = cvaMerge([
|
|
2004
2002
|
cvaInputBase(),
|
|
2005
2003
|
"block",
|
|
@@ -2271,4 +2269,4 @@ const UrlField = forwardRef((_a, ref) => {
|
|
|
2271
2269
|
*/
|
|
2272
2270
|
setupLibraryTranslations();
|
|
2273
2271
|
|
|
2274
|
-
export { ActionButton, BaseInput, Checkbox, CreatableSelect, DateField, DateInput, DropZone, EMAIL_REGEX, EmailField, EmailInput, FormGroup, Label, MultiSelectMenuItem, NumberField, NumberInput, OptionCard, PasswordField, PasswordInput, PhoneField, PhoneInput, RadioGroup, RadioItem, Schedule, ScheduleVariant, Search, Select, SelectField, SingleSelectMenuItem, TextArea, TextAreaField, TextField, TextInput, TimeRange, TimeRangeField, Toggle, UploadField, UploadInput, UrlField, UrlInput, checkIfPhoneNumberHasPlus, countryCodeToFlagEmoji, cvaInput, cvaInputAddon, cvaInputAddonAfter, cvaInputAddonBefore, cvaInputBase, cvaInputBaseDisabled, cvaInputBaseInvalid, cvaInputField, cvaInputPrefix, cvaInputSuffix, cvaSelect, cvaSelectCounter, cvaSelectDynamicTagContainer, cvaSelectIcon, cvaSelectMenu, cvaSelectMenuList, cvaSelectPrefix, cvaSelectXIcon, getCountryAbbreviation, getOrderedOptions, getPhoneNumberWithPlus, isInvalidCountryCode, isInvalidPhoneNumber, isMultiValue, parseSchedule, serializeSchedule, useCustomComponents, usePhoneInput, validateEmailAddress, validatePhoneNumber, weekDay };
|
|
2272
|
+
export { ActionButton, BaseInput, Checkbox, CreatableSelect, CreatableSelectField, DateField, DateInput, DropZone, EMAIL_REGEX, EmailField, EmailInput, FormGroup, Label, MultiSelectMenuItem, NumberField, NumberInput, OptionCard, PasswordField, PasswordInput, PhoneField, PhoneInput, RadioGroup, RadioItem, Schedule, ScheduleVariant, Search, Select, SelectField, SingleSelectMenuItem, TextArea, TextAreaField, TextField, TextInput, TimeRange, TimeRangeField, Toggle, UploadField, UploadInput, UrlField, UrlInput, checkIfPhoneNumberHasPlus, countryCodeToFlagEmoji, cvaInput, cvaInputAddon, cvaInputAddonAfter, cvaInputAddonBefore, cvaInputBase, cvaInputBaseDisabled, cvaInputBaseInvalid, cvaInputField, cvaInputPrefix, cvaInputSuffix, cvaSelect, cvaSelectCounter, cvaSelectDynamicTagContainer, cvaSelectIcon, cvaSelectMenu, cvaSelectMenuList, cvaSelectPrefix, cvaSelectXIcon, getCountryAbbreviation, getOrderedOptions, getPhoneNumberWithPlus, isInvalidCountryCode, isInvalidPhoneNumber, isMultiValue, parseSchedule, serializeSchedule, useCustomComponents, usePhoneInput, validateEmailAddress, validatePhoneNumber, weekDay };
|