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