@trackunit/react-form-components 1.8.64 → 1.8.66
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 +1511 -1438
- package/index.esm.js +1509 -1439
- package/package.json +2 -2
- package/src/components/BaseInput/BaseInput.d.ts +2 -2
- package/src/components/BaseInput/BaseInput.variants.d.ts +10 -3
- package/src/components/{Select/Select.d.ts → BaseSelect/BaseSelect.d.ts} +3 -2
- package/src/components/{Select → BaseSelect}/index.d.ts +2 -2
- package/src/components/MultiSelectField/FormFieldSelectAdapterMulti.d.ts +59 -0
- package/src/components/MultiSelectField/MultiSelectField.d.ts +10 -0
- package/src/components/MultiSelectField/index.d.ts +1 -0
- package/src/components/SelectField/CreatableSelectField.d.ts +1 -1
- package/src/components/SelectField/FormFieldSelectAdapter.d.ts +1 -1
- package/src/index.d.ts +2 -1
- /package/src/components/{Select/Select.variants.d.ts → BaseSelect/BaseSelect.variants.d.ts} +0 -0
- /package/src/components/{Select → BaseSelect}/CreatableSelect.d.ts +0 -0
- /package/src/components/{Select → BaseSelect}/SelectMenuItem/SelectMenuItem.d.ts +0 -0
- /package/src/components/{Select → BaseSelect}/TagWithWidth.d.ts +0 -0
- /package/src/components/{Select → BaseSelect}/TagsContainer.d.ts +0 -0
- /package/src/components/{Select → BaseSelect}/useCustomComponents.d.ts +0 -0
- /package/src/components/{Select → BaseSelect}/useCustomStyles.d.ts +0 -0
- /package/src/components/{Select → BaseSelect}/useSelect.d.ts +0 -0
package/index.cjs.js
CHANGED
|
@@ -10,12 +10,12 @@ var cssClassVarianceUtilities = require('@trackunit/css-class-variance-utilities
|
|
|
10
10
|
var stringTs = require('string-ts');
|
|
11
11
|
var usehooksTs = require('usehooks-ts');
|
|
12
12
|
var parsePhoneNumberFromString = require('libphonenumber-js');
|
|
13
|
-
var sharedUtils = require('@trackunit/shared-utils');
|
|
14
|
-
var reactHookForm = require('react-hook-form');
|
|
15
13
|
var ReactSelect = require('react-select');
|
|
14
|
+
var ReactAsyncSelect = require('react-select/async');
|
|
16
15
|
var ReactAsyncCreatableSelect = require('react-select/async-creatable');
|
|
17
16
|
var ReactCreatableSelect = require('react-select/creatable');
|
|
18
|
-
var
|
|
17
|
+
var sharedUtils = require('@trackunit/shared-utils');
|
|
18
|
+
var reactHookForm = require('react-hook-form');
|
|
19
19
|
var tailwindMerge = require('tailwind-merge');
|
|
20
20
|
var zod = require('zod');
|
|
21
21
|
|
|
@@ -108,10 +108,28 @@ const cvaInputBase = cssClassVarianceUtilities.cvaMerge([
|
|
|
108
108
|
"hover:bg-neutral-50",
|
|
109
109
|
"transition",
|
|
110
110
|
]);
|
|
111
|
-
const cvaInputBaseDisabled = cssClassVarianceUtilities.cvaMerge([
|
|
111
|
+
const cvaInputBaseDisabled = cssClassVarianceUtilities.cvaMerge([
|
|
112
|
+
"bg-neutral-100",
|
|
113
|
+
"hover:bg-neutral-100",
|
|
114
|
+
"hover:border-neutral-300",
|
|
115
|
+
"text-neutral-400",
|
|
116
|
+
]);
|
|
112
117
|
const cvaInputBaseInvalid = cssClassVarianceUtilities.cvaMerge(["border-danger-600"]);
|
|
118
|
+
const cvaInputBaseReadOnly = cssClassVarianceUtilities.cvaMerge(["text-neutral-500"]);
|
|
119
|
+
const cvaInputBaseSize = cssClassVarianceUtilities.cvaMerge("", {
|
|
120
|
+
variants: {
|
|
121
|
+
size: {
|
|
122
|
+
small: ["h-7", "text-xs"],
|
|
123
|
+
medium: ["h-8", "text-sm"],
|
|
124
|
+
large: ["h-10", "text-sm"],
|
|
125
|
+
},
|
|
126
|
+
defaultVariants: {
|
|
127
|
+
size: "medium",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
});
|
|
113
131
|
const cvaInput$1 = cssClassVarianceUtilities.cvaMerge([
|
|
114
|
-
"overflow-
|
|
132
|
+
"overflow-clip",
|
|
115
133
|
"grid",
|
|
116
134
|
// ! The layout of the grid is critical to the functioning of the cvaInputItemPlacementManager 👇
|
|
117
135
|
// The min restriction of the middle column (--spacing-20) is
|
|
@@ -120,17 +138,22 @@ const cvaInput$1 = cssClassVarianceUtilities.cvaMerge([
|
|
|
120
138
|
"grid-rows-1",
|
|
121
139
|
cvaInputBase(),
|
|
122
140
|
"focus-within:outline-native",
|
|
141
|
+
"text-neutral-900",
|
|
123
142
|
], {
|
|
124
143
|
variants: {
|
|
125
144
|
size: {
|
|
126
|
-
small:
|
|
127
|
-
medium:
|
|
128
|
-
large:
|
|
145
|
+
small: cvaInputBaseSize({ size: "small" }),
|
|
146
|
+
medium: cvaInputBaseSize({ size: "medium" }),
|
|
147
|
+
large: cvaInputBaseSize({ size: "large" }),
|
|
129
148
|
},
|
|
130
149
|
disabled: {
|
|
131
150
|
true: cvaInputBaseDisabled(),
|
|
132
151
|
false: [""],
|
|
133
152
|
},
|
|
153
|
+
readOnly: {
|
|
154
|
+
true: cvaInputBaseReadOnly(),
|
|
155
|
+
false: [""],
|
|
156
|
+
},
|
|
134
157
|
invalid: {
|
|
135
158
|
true: cvaInputBaseInvalid(),
|
|
136
159
|
false: [""],
|
|
@@ -146,6 +169,11 @@ const cvaInput$1 = cssClassVarianceUtilities.cvaMerge([
|
|
|
146
169
|
isWarning: true,
|
|
147
170
|
className: ["border-danger-600"], // Ensures that 'invalid' takes precedence
|
|
148
171
|
},
|
|
172
|
+
{
|
|
173
|
+
disabled: true,
|
|
174
|
+
readOnly: true,
|
|
175
|
+
className: "text-neutral-400",
|
|
176
|
+
},
|
|
149
177
|
],
|
|
150
178
|
defaultVariants: {
|
|
151
179
|
size: "medium",
|
|
@@ -172,42 +200,19 @@ const cvaInputItemPlacementManager = cssClassVarianceUtilities.cvaMerge([], {
|
|
|
172
200
|
},
|
|
173
201
|
});
|
|
174
202
|
const cvaAccessoriesContainer = cssClassVarianceUtilities.cvaMerge(["grid", "h-full", "w-min", "auto-cols-min", "grid-flow-col"]);
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
"bg-transparent",
|
|
180
|
-
"text-neutral-900",
|
|
181
|
-
"placeholder-neutral-400",
|
|
182
|
-
"text-sm",
|
|
183
|
-
"truncate",
|
|
184
|
-
], {
|
|
203
|
+
/**
|
|
204
|
+
* Text size and color is handled by cvaInput
|
|
205
|
+
*/
|
|
206
|
+
const cvaInputElement = cssClassVarianceUtilities.cvaMerge(["w-full", "px-3", "border-0", "bg-transparent", "placeholder-neutral-400", "truncate"], {
|
|
185
207
|
variants: {
|
|
186
208
|
size: {
|
|
187
|
-
small: ["py-0.5"
|
|
209
|
+
small: ["py-0.5"],
|
|
188
210
|
medium: ["py-1"],
|
|
189
211
|
large: ["py-2"],
|
|
190
212
|
},
|
|
191
|
-
disabled: {
|
|
192
|
-
true: ["text-neutral-400"],
|
|
193
|
-
false: [""],
|
|
194
|
-
},
|
|
195
|
-
readOnly: {
|
|
196
|
-
true: ["text-neutral-500"],
|
|
197
|
-
false: [""],
|
|
198
|
-
},
|
|
199
213
|
},
|
|
200
|
-
compoundVariants: [
|
|
201
|
-
{
|
|
202
|
-
disabled: true,
|
|
203
|
-
readOnly: true,
|
|
204
|
-
className: "text-neutral-400",
|
|
205
|
-
},
|
|
206
|
-
],
|
|
207
214
|
defaultVariants: {
|
|
208
215
|
size: "medium",
|
|
209
|
-
disabled: false,
|
|
210
|
-
readOnly: false,
|
|
211
216
|
},
|
|
212
217
|
});
|
|
213
218
|
const cvaInputAddon = cssClassVarianceUtilities.cvaMerge([
|
|
@@ -371,7 +376,7 @@ const GenericActionsRenderer = ({ genericAction, disabled, fieldSize, innerRef,
|
|
|
371
376
|
*/
|
|
372
377
|
const InputLockReasonTooltip = ({ reasons, kind }) => {
|
|
373
378
|
const [t] = useTranslation();
|
|
374
|
-
if (
|
|
379
|
+
if (reasons === undefined || reasons.length === 0) {
|
|
375
380
|
return (jsxRuntime.jsx(reactComponents.Tooltip, { label: t("field.notEditable.tooltip"), children: jsxRuntime.jsx(reactComponents.Icon, { name: kind === "disabled" ? "QuestionMarkCircle" : "LockClosed", size: "small" }) }));
|
|
376
381
|
}
|
|
377
382
|
return (jsxRuntime.jsx(reactComponents.Tooltip, { label: jsxRuntime.jsx("ul", { className: typeof reasons === "string" ? "list-none !pl-0" : "list-disc", children: typeof reasons === "string" ? jsxRuntime.jsx("li", { children: reasons }) : reasons.map(reason => jsxRuntime.jsx("li", { children: reason }, reason)) }), placement: "top", children: jsxRuntime.jsx(reactComponents.Icon, { name: "LockClosed", size: "small" }) }));
|
|
@@ -429,7 +434,7 @@ const SuffixRenderer = ({ suffix, isInvalid, isWarning, dataTestId, disabled, })
|
|
|
429
434
|
* For specific input types make sure to use the corresponding input component.
|
|
430
435
|
* This is a base used by our other input components such as TextBaseInput, NumberBaseInput, PasswordBaseInput, etc.
|
|
431
436
|
*/
|
|
432
|
-
const BaseInput = ({ className, isInvalid, dataTestId, prefix, suffix, addonBefore, addonAfter, actions, fieldSize = "medium", nonInteractive = false, inputClassName, placeholder, isWarning, type, genericAction, style, ref, ...rest }) => {
|
|
437
|
+
const BaseInput = ({ className, isInvalid = false, dataTestId, prefix, suffix, addonBefore, addonAfter, actions, fieldSize = "medium", nonInteractive = false, inputClassName, placeholder, isWarning = false, type, genericAction, style, ref, required = false, ...rest }) => {
|
|
433
438
|
// Derive final flags
|
|
434
439
|
const renderAsDisabled = Boolean(rest.disabled);
|
|
435
440
|
const renderAsReadonly = Boolean(rest.readOnly);
|
|
@@ -463,19 +468,18 @@ const BaseInput = ({ className, isInvalid, dataTestId, prefix, suffix, addonBefo
|
|
|
463
468
|
react.useImperativeHandle(ref, () => innerRef.current, []);
|
|
464
469
|
return (jsxRuntime.jsxs("div", { className: cvaInput$1({
|
|
465
470
|
disabled: renderAsDisabled,
|
|
471
|
+
readOnly: renderAsReadonly,
|
|
466
472
|
invalid: isInvalid,
|
|
467
473
|
isWarning,
|
|
468
474
|
size: fieldSize,
|
|
469
475
|
className,
|
|
470
|
-
}), "data-testid": dataTestId ? `${dataTestId}-container` : undefined, style: style, children: [jsxRuntime.jsxs("div", { className: cvaAccessoriesContainer({ className: cvaInputItemPlacementManager({ position: "before" }) }), "data-testid": dataTestId ? `${dataTestId}-before-container` : undefined, ref: beforeContainerRef, children: [jsxRuntime.jsx(AddonRenderer, { addon: addonBefore, dataTestId: dataTestId, fieldSize: fieldSize, position: "before" }), jsxRuntime.jsx(PrefixRenderer, { dataTestId: dataTestId, disabled: renderAsDisabled, prefix: prefix, type: type })] }), jsxRuntime.jsx("input", { "aria-required":
|
|
471
|
-
readOnly: renderAsReadonly,
|
|
476
|
+
}), "data-testid": dataTestId ? `${dataTestId}-container` : undefined, style: style, children: [jsxRuntime.jsxs("div", { className: cvaAccessoriesContainer({ className: cvaInputItemPlacementManager({ position: "before" }) }), "data-testid": dataTestId ? `${dataTestId}-before-container` : undefined, ref: beforeContainerRef, children: [jsxRuntime.jsx(AddonRenderer, { addon: addonBefore, dataTestId: dataTestId, fieldSize: fieldSize, position: "before" }), jsxRuntime.jsx(PrefixRenderer, { dataTestId: dataTestId, disabled: renderAsDisabled, prefix: prefix, type: type })] }), jsxRuntime.jsx("input", { "aria-required": required, className: cvaInputElement({
|
|
472
477
|
size: fieldSize,
|
|
473
|
-
disabled: renderAsDisabled,
|
|
474
478
|
className: cvaInputItemPlacementManager({ position: "span", className: inputClassName }),
|
|
475
|
-
}), "data-testid": dataTestId, placeholder: renderAsDisabled ? undefined : placeholder, ref: innerRef, style: {
|
|
479
|
+
}), "data-testid": dataTestId, placeholder: renderAsDisabled ? undefined : placeholder, ref: innerRef, required: required, style: {
|
|
476
480
|
paddingLeft: `calc(var(--before-width, 0px) + ${uiDesignTokens.themeSpacing[2]})`,
|
|
477
481
|
paddingRight: `calc(var(--after-width, 0px) + ${uiDesignTokens.themeSpacing[2]})`,
|
|
478
|
-
}, type: type, ...rest, disabled: renderAsDisabled, readOnly: renderAsReadonly || nonInteractive }), jsxRuntime.jsxs("div", { className: cvaAccessoriesContainer({ className: cvaInputItemPlacementManager({ position: "after" }) }), "data-testid": dataTestId ? `${dataTestId}-after-container` : undefined, ref: afterContainerRef, children: [jsxRuntime.jsx(LockReasonRenderer, { dataTestId: dataTestId + "-disabled", lockReason: rest.disabled }), jsxRuntime.jsx(LockReasonRenderer, { dataTestId: dataTestId + "-readonly", lockReason: rest.readOnly && !rest.disabled ? rest.readOnly : undefined }), jsxRuntime.jsx(GenericActionsRenderer, { fieldSize: fieldSize, genericAction: genericAction, innerRef: innerRef }), jsxRuntime.jsx(SuffixRenderer, { dataTestId: dataTestId, disabled: renderAsDisabled, isInvalid: isInvalid, isWarning: isWarning, suffix: suffix }), actions, jsxRuntime.jsx(AddonRenderer, { addon: addonAfter, dataTestId: dataTestId, fieldSize: fieldSize, position: "after" })] })] }));
|
|
482
|
+
}, type: type, ...rest, disabled: renderAsDisabled, readOnly: renderAsReadonly || nonInteractive }), jsxRuntime.jsxs("div", { className: cvaAccessoriesContainer({ className: cvaInputItemPlacementManager({ position: "after" }) }), "data-testid": dataTestId ? `${dataTestId}-after-container` : undefined, ref: afterContainerRef, children: [jsxRuntime.jsx(LockReasonRenderer, { dataTestId: dataTestId + "-disabled", lockReason: rest.disabled }), jsxRuntime.jsx(LockReasonRenderer, { dataTestId: dataTestId + "-readonly", lockReason: Boolean(rest.readOnly) && !Boolean(rest.disabled) ? rest.readOnly : undefined }), jsxRuntime.jsx(GenericActionsRenderer, { fieldSize: fieldSize, genericAction: genericAction, innerRef: innerRef }), jsxRuntime.jsx(SuffixRenderer, { dataTestId: dataTestId, disabled: renderAsDisabled, isInvalid: isInvalid, isWarning: isWarning, suffix: suffix }), actions, jsxRuntime.jsx(AddonRenderer, { addon: addonAfter, dataTestId: dataTestId, fieldSize: fieldSize, position: "after" })] })] }));
|
|
479
483
|
};
|
|
480
484
|
BaseInput.displayName = "BaseInput";
|
|
481
485
|
|
|
@@ -698,6 +702,103 @@ const TextAreaBaseInput = ({ id, name, value, rows, disabled, placeholder, readO
|
|
|
698
702
|
*/
|
|
699
703
|
const TextBaseInput = ({ ref, ...rest }) => jsxRuntime.jsx(BaseInput, { ref: ref, type: "text", ...rest });
|
|
700
704
|
|
|
705
|
+
const cvaSelect = cssClassVarianceUtilities.cvaMerge([
|
|
706
|
+
"relative",
|
|
707
|
+
"flex",
|
|
708
|
+
"shadow-sm",
|
|
709
|
+
"rounded-lg",
|
|
710
|
+
"border-neutral-300",
|
|
711
|
+
"hover:border-neutral-400",
|
|
712
|
+
"hover:bg-neutral-50",
|
|
713
|
+
"bg-white",
|
|
714
|
+
"transition",
|
|
715
|
+
"text-sm",
|
|
716
|
+
"min-h-0",
|
|
717
|
+
], {
|
|
718
|
+
variants: {
|
|
719
|
+
fieldSize: {
|
|
720
|
+
small: ["h-7", "text-xs"],
|
|
721
|
+
medium: ["h-8"],
|
|
722
|
+
large: ["h-10"],
|
|
723
|
+
},
|
|
724
|
+
invalid: {
|
|
725
|
+
true: "border border-red-600 text-red-600 hover:border-red-600",
|
|
726
|
+
false: "",
|
|
727
|
+
},
|
|
728
|
+
disabled: {
|
|
729
|
+
true: "!bg-neutral-100 hover:border-neutral-300",
|
|
730
|
+
false: "",
|
|
731
|
+
},
|
|
732
|
+
},
|
|
733
|
+
defaultVariants: {
|
|
734
|
+
invalid: false,
|
|
735
|
+
disabled: false,
|
|
736
|
+
},
|
|
737
|
+
});
|
|
738
|
+
const cvaSelectControl = cssClassVarianceUtilities.cvaMerge([], {
|
|
739
|
+
variants: {
|
|
740
|
+
isDisabled: {
|
|
741
|
+
true: "!bg-neutral-100",
|
|
742
|
+
false: "",
|
|
743
|
+
},
|
|
744
|
+
prefix: {
|
|
745
|
+
true: ["ps-7"],
|
|
746
|
+
false: "",
|
|
747
|
+
},
|
|
748
|
+
invalid: {
|
|
749
|
+
true: "!border-0",
|
|
750
|
+
false: "",
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
defaultVariants: {
|
|
754
|
+
isDisabled: false,
|
|
755
|
+
prefix: false,
|
|
756
|
+
invalid: false,
|
|
757
|
+
},
|
|
758
|
+
});
|
|
759
|
+
const cvaSelectIcon = cssClassVarianceUtilities.cvaMerge([
|
|
760
|
+
"mr-2",
|
|
761
|
+
"flex",
|
|
762
|
+
"cursor-pointer",
|
|
763
|
+
"items-center",
|
|
764
|
+
"justify-center",
|
|
765
|
+
"text-neutral-400",
|
|
766
|
+
"hover:text-neutral-500",
|
|
767
|
+
]);
|
|
768
|
+
const cvaSelectPrefixSuffix = cssClassVarianceUtilities.cvaMerge(["flex", "justify-center", "items-center", "text-neutral-400", "absolute", "inset-y-0"], {
|
|
769
|
+
variants: {
|
|
770
|
+
kind: {
|
|
771
|
+
prefix: ["pl-3", "left-0"],
|
|
772
|
+
suffix: ["pr-3", "right-0"],
|
|
773
|
+
},
|
|
774
|
+
},
|
|
775
|
+
});
|
|
776
|
+
const cvaSelectXIcon = cssClassVarianceUtilities.cvaMerge([
|
|
777
|
+
"mr-2",
|
|
778
|
+
"flex",
|
|
779
|
+
"cursor-pointer",
|
|
780
|
+
"items-center",
|
|
781
|
+
"justify-center",
|
|
782
|
+
"text-neutral-400",
|
|
783
|
+
"hover:text-neutral-500",
|
|
784
|
+
"ml-1",
|
|
785
|
+
]);
|
|
786
|
+
const cvaSelectMenuList = cssClassVarianceUtilities.cvaMerge([], {
|
|
787
|
+
variants: {
|
|
788
|
+
menuIsOpen: {
|
|
789
|
+
true: "animate-fade-in-fast",
|
|
790
|
+
false: "animate-fade-out-fast",
|
|
791
|
+
},
|
|
792
|
+
},
|
|
793
|
+
});
|
|
794
|
+
const cvaSelectDynamicTagContainer = cssClassVarianceUtilities.cvaMerge(["h-full", "flex", "gap-1", "items-center"], {
|
|
795
|
+
variants: {
|
|
796
|
+
visible: { true: "visible", false: "invisible" },
|
|
797
|
+
},
|
|
798
|
+
});
|
|
799
|
+
const cvaSelectCounter = cssClassVarianceUtilities.cvaMerge(["overflow-hidden", "whitespace-nowrap"]);
|
|
800
|
+
const cvaSelectMenu = cssClassVarianceUtilities.cvaMerge(["relative", "p-1", "grid", "gap-1"]);
|
|
801
|
+
|
|
701
802
|
/**
|
|
702
803
|
* Shared CVA for binary control items: Checkbox, RadioItem, ToggleSwitchOption
|
|
703
804
|
*/
|
|
@@ -924,800 +1025,1438 @@ const Checkbox = ({ className, dataTestId = "checkbox", onChange, checked = fals
|
|
|
924
1025
|
Checkbox.displayName = "Checkbox";
|
|
925
1026
|
|
|
926
1027
|
/**
|
|
927
|
-
*
|
|
928
|
-
* This component is **not used directly**, but is part of the FormGroup and Field components.
|
|
1028
|
+
* A single select menu item is a basic wrapper around Menu item designed to be used as a single value render in Select list
|
|
929
1029
|
*
|
|
930
|
-
* @param {
|
|
931
|
-
* @returns {ReactElement}
|
|
1030
|
+
* @param {SelectMenuItemProps} props - The props for the SingleSelectMenuItem
|
|
1031
|
+
* @returns {ReactElement} SingleSelectMenuItem
|
|
932
1032
|
*/
|
|
933
|
-
const
|
|
934
|
-
return (jsxRuntime.jsx(
|
|
1033
|
+
const SingleSelectMenuItem = ({ label, icon, onClick, selected, focused, dataTestId, disabled, optionLabelDescription, optionPrefix, fieldSize, }) => {
|
|
1034
|
+
return (jsxRuntime.jsx(reactComponents.MenuItem, { dataTestId: dataTestId, disabled: disabled, fieldSize: fieldSize, focused: focused, label: label, onClick: onClick, optionLabelDescription: optionLabelDescription, optionPrefix: react.isValidElement(optionPrefix)
|
|
1035
|
+
? react.cloneElement(optionPrefix, {
|
|
1036
|
+
className: "mr-1 flex items-center",
|
|
1037
|
+
size: "medium",
|
|
1038
|
+
})
|
|
1039
|
+
: optionPrefix, prefix: icon, selected: selected, suffix: selected ? jsxRuntime.jsx(reactComponents.Icon, { className: "block text-blue-600", name: "Check", size: "medium" }) : undefined }));
|
|
935
1040
|
};
|
|
936
|
-
|
|
937
|
-
const cvaFormGroup = cssClassVarianceUtilities.cvaMerge(["component-formGroup-gap", "group", "form-group"]);
|
|
938
|
-
const cvaFormGroupContainerBefore = cssClassVarianceUtilities.cvaMerge(["flex", "mb-1", "items-center"]);
|
|
939
|
-
const cvaFormGroupContainerAfter = cssClassVarianceUtilities.cvaMerge(["flex", "justify-between", "mt-1", "text-xs", "text-neutral-500"], {
|
|
940
|
-
variants: {
|
|
941
|
-
invalid: {
|
|
942
|
-
true: "text-danger-500",
|
|
943
|
-
false: "",
|
|
944
|
-
},
|
|
945
|
-
isWarning: {
|
|
946
|
-
true: "text-default-500 ",
|
|
947
|
-
false: "",
|
|
948
|
-
},
|
|
949
|
-
},
|
|
950
|
-
compoundVariants: [
|
|
951
|
-
{
|
|
952
|
-
invalid: true,
|
|
953
|
-
isWarning: true,
|
|
954
|
-
className: "text-danger-500 ", // Ensures that 'invalid' takes precedence
|
|
955
|
-
},
|
|
956
|
-
],
|
|
957
|
-
});
|
|
958
|
-
const cvaHelpAddon = cssClassVarianceUtilities.cvaMerge(["ml-auto"]);
|
|
959
|
-
|
|
960
1041
|
/**
|
|
961
|
-
*
|
|
962
|
-
* Besides a label the component supplies an optional Tooltip, HelpText and HelpAddon support.
|
|
1042
|
+
* A multi select menu item is a basic wrapper around Menu item designed to be used as a multi value render in Select list
|
|
963
1043
|
*
|
|
964
|
-
* @param {
|
|
965
|
-
* @returns {ReactElement}
|
|
1044
|
+
* @param {SelectMenuItemProps} props - The props for the MultiSelectMenuItem
|
|
1045
|
+
* @returns {ReactElement} multi select menu item
|
|
966
1046
|
*/
|
|
967
|
-
const
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1047
|
+
const MultiSelectMenuItem = ({ label, onClick, selected, focused, dataTestId, disabled, optionLabelDescription, optionPrefix, fieldSize, }) => {
|
|
1048
|
+
return (jsxRuntime.jsx(reactComponents.MenuItem, { dataTestId: dataTestId, disabled: disabled, fieldSize: fieldSize, focused: focused, label: label, onClick: e => {
|
|
1049
|
+
e.stopPropagation();
|
|
1050
|
+
onClick && onClick(e);
|
|
1051
|
+
}, optionLabelDescription: optionLabelDescription, optionPrefix: react.isValidElement(optionPrefix)
|
|
1052
|
+
? react.cloneElement(optionPrefix, {
|
|
1053
|
+
className: "mr-1 flex items-center",
|
|
1054
|
+
size: "medium",
|
|
1055
|
+
})
|
|
1056
|
+
: optionPrefix, prefix: jsxRuntime.jsx(Checkbox, { checked: selected, className: "gap-x-0", disabled: disabled, onChange: () => null, onClick: e => {
|
|
1057
|
+
e.stopPropagation();
|
|
1058
|
+
}, readOnly: false }), selected: selected }));
|
|
974
1059
|
};
|
|
975
1060
|
|
|
976
1061
|
/**
|
|
977
|
-
*
|
|
1062
|
+
* Extended Tag component with information about its own width.
|
|
1063
|
+
* Used in the select component.
|
|
978
1064
|
*
|
|
979
|
-
*
|
|
1065
|
+
* @param {TagProps} props - The props for the tag component
|
|
1066
|
+
* @returns {ReactElement} TagWithWidth component
|
|
980
1067
|
*/
|
|
981
|
-
const
|
|
982
|
-
const
|
|
983
|
-
|
|
1068
|
+
const TagWithWidth = ({ onWidthKnown, children, ...rest }) => {
|
|
1069
|
+
const ref = react.useRef(null);
|
|
1070
|
+
react.useLayoutEffect(() => {
|
|
1071
|
+
onWidthKnown && onWidthKnown({ width: ref.current?.offsetWidth || 0 });
|
|
1072
|
+
}, [ref, onWidthKnown]);
|
|
1073
|
+
return (jsxRuntime.jsx(reactComponents.Tag, { ref: ref, ...rest, icon: react.isValidElement(rest.icon) ? react.cloneElement(rest.icon, { size: "small" }) : rest.icon, children: children }));
|
|
984
1074
|
};
|
|
985
|
-
CheckboxField.displayName = "CheckboxField";
|
|
986
1075
|
|
|
987
1076
|
/**
|
|
1077
|
+
* TagsContainer component to display tags in limited space when children can't fit space it displays counter
|
|
988
1078
|
*
|
|
989
|
-
* @param
|
|
990
|
-
* @returns {
|
|
991
|
-
*/
|
|
992
|
-
const isString = (inputValue) => {
|
|
993
|
-
return typeof inputValue === "string";
|
|
994
|
-
};
|
|
995
|
-
/**
|
|
996
|
-
*
|
|
997
|
-
* @param inputValue - value to check if it is a number
|
|
998
|
-
* @returns {boolean} - true if value is a number
|
|
999
|
-
*/
|
|
1000
|
-
const isNumber = (inputValue) => {
|
|
1001
|
-
return typeof inputValue === "number";
|
|
1002
|
-
};
|
|
1003
|
-
|
|
1004
|
-
/**
|
|
1005
|
-
* Validates a url
|
|
1079
|
+
* @param {TagsContainerProps} props - The props for the TagContainer
|
|
1080
|
+
* @returns {ReactElement} TagsContainer
|
|
1006
1081
|
*/
|
|
1007
|
-
const
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1082
|
+
const TagsContainer = ({ items, width = "100%", itemsGap = 6, postFix, preFix, disabled, }) => {
|
|
1083
|
+
const containerRef = react.useRef(null);
|
|
1084
|
+
const [isReady, setIsReady] = react.useState(false);
|
|
1085
|
+
const [counterWidth, setCounterWidth] = react.useState(0);
|
|
1086
|
+
const [availableSpaceWidth, setAvailableSpaceWidth] = react.useState(0);
|
|
1087
|
+
const [childrenWidths, setChildrenWidths] = react.useState([]);
|
|
1088
|
+
const itemsCount = items.length;
|
|
1089
|
+
const dimensions = reactComponents.useResize();
|
|
1090
|
+
const { width: windowWidth } = reactComponents.useDebounce(dimensions, 100);
|
|
1091
|
+
react.useEffect(() => {
|
|
1092
|
+
const containerWidth = containerRef.current?.offsetWidth || 0;
|
|
1093
|
+
setAvailableSpaceWidth(containerWidth);
|
|
1094
|
+
}, [windowWidth]);
|
|
1095
|
+
const onWidthKnownHandler = react.useCallback(({ width: reportedWidth }) => {
|
|
1096
|
+
setChildrenWidths(prev => {
|
|
1097
|
+
const next = [...prev, { width: reportedWidth + itemsGap }];
|
|
1098
|
+
if (next.length === itemsCount) {
|
|
1099
|
+
setIsReady(true);
|
|
1100
|
+
}
|
|
1101
|
+
return next;
|
|
1102
|
+
});
|
|
1103
|
+
}, [itemsCount, itemsGap]);
|
|
1104
|
+
const renderedElements = react.useMemo(() => {
|
|
1105
|
+
const requiredSpace = childrenWidths.reduce((previous, current) => {
|
|
1106
|
+
return previous + current.width;
|
|
1107
|
+
}, 0);
|
|
1108
|
+
const availableSpace = availableSpaceWidth - counterWidth;
|
|
1109
|
+
const { elements } = items
|
|
1110
|
+
.concat({ text: "", onClick: () => null, disabled: false })
|
|
1111
|
+
.reduce((acc, item, index) => {
|
|
1112
|
+
const spaceNeeded = childrenWidths.slice(0, index + 1).reduce((previous, current) => {
|
|
1113
|
+
return previous + current.width;
|
|
1114
|
+
}, 0);
|
|
1115
|
+
const isLast = index === items.length;
|
|
1116
|
+
const counterRequired = requiredSpace > availableSpace && acc.counter !== 0;
|
|
1117
|
+
if (isLast && counterRequired) {
|
|
1118
|
+
return {
|
|
1119
|
+
...acc,
|
|
1120
|
+
elements: [
|
|
1121
|
+
...acc.elements,
|
|
1122
|
+
jsxRuntime.jsx(TagWithWidth, { color: "white", disabled: disabled, icon: item.Icon, onWidthKnown: ({ width: reportedWidth }) => setCounterWidth(reportedWidth), children: jsxRuntime.jsxs("div", { className: cvaSelectCounter(), "data-testid": "select-counter", children: ["+", acc.counter] }) }, item.text + index),
|
|
1123
|
+
],
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
if (isLast) {
|
|
1127
|
+
return acc;
|
|
1128
|
+
}
|
|
1129
|
+
const itemCanFit = spaceNeeded <= availableSpace;
|
|
1130
|
+
if (itemCanFit) {
|
|
1131
|
+
return {
|
|
1132
|
+
...acc,
|
|
1133
|
+
elements: [
|
|
1134
|
+
...acc.elements,
|
|
1135
|
+
jsxRuntime.jsx(TagWithWidth, { className: "inline-flex shrink-0", color: item.disabled ? "neutral" : "white", dataTestId: `${item.text}-tag`, disabled: disabled, icon: item.Icon, onClose: e => {
|
|
1136
|
+
e.stopPropagation();
|
|
1137
|
+
item.onClick();
|
|
1138
|
+
}, onWidthKnown: onWidthKnownHandler, children: item.text }, item.text + index),
|
|
1139
|
+
],
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
return {
|
|
1143
|
+
elements: acc.elements,
|
|
1144
|
+
counter: item.text !== "" ? acc.counter + 1 : acc.counter,
|
|
1145
|
+
};
|
|
1146
|
+
}, { elements: [], counter: 0 });
|
|
1147
|
+
return elements;
|
|
1148
|
+
}, [items, availableSpaceWidth, counterWidth, disabled, onWidthKnownHandler, childrenWidths]);
|
|
1149
|
+
return (jsxRuntime.jsxs("div", { className: cvaSelectDynamicTagContainer({ visible: isReady || !!preFix }), ref: containerRef, style: {
|
|
1150
|
+
width: `${width}`,
|
|
1151
|
+
}, children: [preFix, renderedElements, postFix] }));
|
|
1018
1152
|
};
|
|
1019
1153
|
|
|
1020
|
-
const cvaInputColorField = cssClassVarianceUtilities.cvaMerge([
|
|
1021
|
-
"ml-3",
|
|
1022
|
-
"h-4",
|
|
1023
|
-
"w-4",
|
|
1024
|
-
"self-center",
|
|
1025
|
-
"bg-inherit",
|
|
1026
|
-
"disabled:opacity-50",
|
|
1027
|
-
"disabled:pointer-events-none",
|
|
1028
|
-
"rounded-[4px]",
|
|
1029
|
-
], {
|
|
1030
|
-
variants: {
|
|
1031
|
-
readOnly: {
|
|
1032
|
-
true: "pointer-events-none",
|
|
1033
|
-
false: "",
|
|
1034
|
-
},
|
|
1035
|
-
},
|
|
1036
|
-
compoundVariants: [
|
|
1037
|
-
{
|
|
1038
|
-
readOnly: true,
|
|
1039
|
-
},
|
|
1040
|
-
],
|
|
1041
|
-
defaultVariants: {
|
|
1042
|
-
readOnly: false,
|
|
1043
|
-
},
|
|
1044
|
-
});
|
|
1045
|
-
|
|
1046
1154
|
/**
|
|
1047
|
-
*
|
|
1155
|
+
* A hook to retrieve components override object.
|
|
1156
|
+
* 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.
|
|
1048
1157
|
*
|
|
1049
|
-
* @
|
|
1050
|
-
* @
|
|
1158
|
+
* @template IsMulti
|
|
1159
|
+
* @template Group
|
|
1160
|
+
* @param {Partial<SelectComponents<Option, IsMulti, Group>> | undefined} componentsProps a custom component prop that you can to override defaults
|
|
1161
|
+
* @param {boolean} disabled decide to override disabled variant
|
|
1162
|
+
* @param {boolean} menuIsOpen menu is open state
|
|
1163
|
+
* @param {string} dataTestId a test id
|
|
1164
|
+
* @param {number} maxSelectedDisplayCount a number of max display count
|
|
1165
|
+
* @param {boolean} hasError decide to override hasError variant
|
|
1166
|
+
* @param {ReactNode} prefix a prefix element
|
|
1167
|
+
* @returns {Partial<SelectComponents<Option, boolean, GroupBase<Option>>> | undefined} components object to override react-select default components
|
|
1051
1168
|
*/
|
|
1052
|
-
const
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1169
|
+
const useCustomComponents = ({ componentsProps, disabled, readOnly, setMenuIsEnabled, dataTestId, maxSelectedDisplayCount, prefix, hasError, fieldSize, getOptionLabelDescription, getOptionPrefix, }) => {
|
|
1170
|
+
const [t] = useTranslation();
|
|
1171
|
+
// perhaps it should not be wrap in memo (causing some issues with opening and closing on mobiles)
|
|
1172
|
+
const customComponents = react.useMemo(() => {
|
|
1173
|
+
return {
|
|
1174
|
+
ValueContainer: props => {
|
|
1175
|
+
if (props.isMulti && Array.isArray(props.children) && props.children.length > 0) {
|
|
1176
|
+
const PLACEHOLDER_KEY = "placeholder";
|
|
1177
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1178
|
+
const key = props && props.children && props.children[0] ? props.children[0]?.key : "";
|
|
1179
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1180
|
+
const values = props && props.children ? props.children[0] : [];
|
|
1181
|
+
const tags = key === PLACEHOLDER_KEY ? [] : values;
|
|
1182
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1183
|
+
const searchInput = props && props.children && props.children[1];
|
|
1184
|
+
const placeholderElement = Array.isArray(props.children)
|
|
1185
|
+
? props.children.find(child => child && child.key === PLACEHOLDER_KEY)
|
|
1186
|
+
: null;
|
|
1187
|
+
return (jsxRuntime.jsx(ReactSelect.components.ValueContainer, { ...props, isDisabled: props.selectProps.isDisabled, children: maxSelectedDisplayCount === undefined ? (jsxRuntime.jsx(TagsContainer, { disabled: disabled, items: tags
|
|
1188
|
+
? tags.map(({ props: tagProps }) => {
|
|
1189
|
+
const optionPrefix = tagProps.data && getOptionPrefix ? getOptionPrefix(tagProps.data) : null;
|
|
1190
|
+
return {
|
|
1191
|
+
text: tagProps.children,
|
|
1192
|
+
onClick: disabled
|
|
1193
|
+
? undefined
|
|
1194
|
+
: (e) => {
|
|
1195
|
+
setMenuIsEnabled(false);
|
|
1196
|
+
tagProps.removeProps.onClick && tagProps.removeProps.onClick(e);
|
|
1197
|
+
},
|
|
1198
|
+
disabled: disabled,
|
|
1199
|
+
Icon: optionPrefix,
|
|
1200
|
+
};
|
|
1201
|
+
})
|
|
1202
|
+
: [], postFix: searchInput, preFix: placeholderElement ? jsxRuntime.jsx("span", { className: "absolute", children: placeholderElement }) : null, width: "100%" })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [tags
|
|
1203
|
+
? tags.slice(0, maxSelectedDisplayCount).map(({ props: tagProps }) => {
|
|
1204
|
+
return (jsxRuntime.jsx(reactComponents.Tag, { className: "inline-flex shrink-0", color: disabled ? "unknown" : "primary", dataTestId: tagProps.children ? `${tagProps.children.toString()}-tag` : undefined, onClose: e => {
|
|
1205
|
+
e.stopPropagation();
|
|
1206
|
+
setMenuIsEnabled(false);
|
|
1207
|
+
tagProps.removeProps.onClick && tagProps.removeProps.onClick(e);
|
|
1208
|
+
}, children: tagProps.children }, tagProps.children?.toString()));
|
|
1209
|
+
})
|
|
1210
|
+
: null, tags && tags.length > maxSelectedDisplayCount ? (jsxRuntime.jsxs(reactComponents.Tag, { color: "neutral", dataTestId: "counter-tag", children: ["+", tags.length - maxSelectedDisplayCount] })) : null, searchInput, placeholderElement] })) }));
|
|
1211
|
+
}
|
|
1212
|
+
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(ReactSelect.components.ValueContainer, { ...props, isDisabled: props.selectProps.isDisabled, children: props.children }) }));
|
|
1213
|
+
},
|
|
1214
|
+
LoadingIndicator: () => {
|
|
1215
|
+
return jsxRuntime.jsx(reactComponents.Spinner, { centering: "vertically", className: "mr-2", size: "small" });
|
|
1216
|
+
},
|
|
1217
|
+
DropdownIndicator: props => {
|
|
1218
|
+
const icon = props.selectProps.menuIsOpen ? (jsxRuntime.jsx(reactComponents.Icon, { name: "ChevronUp", size: "medium" })) : (jsxRuntime.jsx(reactComponents.Icon, { name: "ChevronDown", size: "medium" }));
|
|
1219
|
+
return props.selectProps.isLoading || props.selectProps.isDisabled || readOnly ? null : (jsxRuntime.jsx(ReactSelect.components.DropdownIndicator, { ...props, children: jsxRuntime.jsx("div", { className: cvaSelectIcon(), children: icon }) }));
|
|
1220
|
+
},
|
|
1221
|
+
IndicatorSeparator: () => null,
|
|
1222
|
+
ClearIndicator: props => {
|
|
1223
|
+
if (disabled) {
|
|
1224
|
+
return null;
|
|
1225
|
+
}
|
|
1226
|
+
return (jsxRuntime.jsx(ReactSelect.components.ClearIndicator, { ...props, innerProps: {
|
|
1227
|
+
...props.innerProps,
|
|
1228
|
+
onMouseDown: e => {
|
|
1229
|
+
e.preventDefault();
|
|
1230
|
+
},
|
|
1231
|
+
}, children: jsxRuntime.jsx("div", { className: cvaSelectXIcon(), "data-testid": dataTestId ? `${dataTestId}-XMarkIcon` : null, onClick: props.clearValue, children: jsxRuntime.jsx(reactComponents.Icon, { ariaLabel: t("clearIndicator.icon.tooltip.clearAll"), name: "XCircle", size: "medium" }) }) }));
|
|
1232
|
+
},
|
|
1233
|
+
Control: props => {
|
|
1234
|
+
return (jsxRuntime.jsx(ReactSelect.components.Control, { ...props, className: cvaSelectControl({
|
|
1235
|
+
isDisabled: props.isDisabled,
|
|
1236
|
+
prefix: prefix ? true : false,
|
|
1237
|
+
invalid: hasError,
|
|
1238
|
+
}) }));
|
|
1239
|
+
},
|
|
1240
|
+
SingleValue: props => {
|
|
1241
|
+
const optionPrefix = getOptionPrefix ? getOptionPrefix(props.data) : null;
|
|
1242
|
+
return (jsxRuntime.jsx(ReactSelect.components.SingleValue, { ...props, className: props.isDisabled ? "text-neutral-700" : "", children: jsxRuntime.jsxs("div", { className: "flex items-center gap-1", "data-testid": dataTestId + "-singleValue", children: [optionPrefix !== null ? optionPrefix : null, props.children, getOptionLabelDescription && getOptionLabelDescription(props.data) ? (jsxRuntime.jsxs("span", { className: "ml-1 text-neutral-400", children: ["(", getOptionLabelDescription(props.data), ")"] })) : null] }) }));
|
|
1243
|
+
},
|
|
1244
|
+
Menu: props => {
|
|
1245
|
+
return (jsxRuntime.jsx(ReactSelect.components.Menu, { ...props, className: cvaSelectMenuList({ menuIsOpen: props.selectProps.menuIsOpen }) }));
|
|
1246
|
+
},
|
|
1247
|
+
Placeholder: props => {
|
|
1248
|
+
return (jsxRuntime.jsx(ReactSelect.components.Placeholder, { ...props, className: "!text-neutral-400", children: props.children }));
|
|
1249
|
+
},
|
|
1250
|
+
MenuList: props => {
|
|
1251
|
+
return (jsxRuntime.jsx(ReactSelect.components.MenuList, { ...props, innerProps: {
|
|
1252
|
+
...props.innerProps,
|
|
1253
|
+
onScroll: e => {
|
|
1254
|
+
const listEl = e.currentTarget;
|
|
1255
|
+
if (listEl.scrollTop + listEl.clientHeight >= listEl.scrollHeight &&
|
|
1256
|
+
props.selectProps.onMenuScrollToBottom) {
|
|
1257
|
+
/Firefox/.test(navigator.userAgent)
|
|
1258
|
+
? props.selectProps.onMenuScrollToBottom(new WheelEvent("scroll"))
|
|
1259
|
+
: props.selectProps.onMenuScrollToBottom(new TouchEvent(""));
|
|
1260
|
+
}
|
|
1261
|
+
},
|
|
1262
|
+
}, children: props.children }));
|
|
1263
|
+
},
|
|
1264
|
+
Option: props => {
|
|
1265
|
+
const componentProps = {
|
|
1266
|
+
label: props.label,
|
|
1267
|
+
focused: props.isFocused,
|
|
1268
|
+
selected: props.isSelected,
|
|
1269
|
+
onClick: props.innerProps.onClick,
|
|
1270
|
+
};
|
|
1271
|
+
return (jsxRuntime.jsx(ReactSelect.components.Option, { ...props, innerProps: {
|
|
1272
|
+
...props.innerProps,
|
|
1273
|
+
role: "option",
|
|
1274
|
+
onClick: () => { },
|
|
1275
|
+
}, children: props.isMulti ? (jsxRuntime.jsx(MultiSelectMenuItem, { ...componentProps, dataTestId: typeof props.label === "string" ? props.label : undefined, disabled: disabled, fieldSize: fieldSize, optionLabelDescription: getOptionLabelDescription?.(props.data), optionPrefix: getOptionPrefix?.(props.data) })) : (jsxRuntime.jsx(SingleSelectMenuItem, { ...componentProps, dataTestId: typeof props.label === "string" ? props.label : undefined, disabled: disabled || props.isDisabled, fieldSize: fieldSize, optionLabelDescription: getOptionLabelDescription?.(props.data), optionPrefix: getOptionPrefix?.(props.data) })) }));
|
|
1276
|
+
},
|
|
1277
|
+
...componentsProps,
|
|
1278
|
+
};
|
|
1279
|
+
}, [
|
|
1280
|
+
componentsProps,
|
|
1281
|
+
maxSelectedDisplayCount,
|
|
1282
|
+
disabled,
|
|
1283
|
+
setMenuIsEnabled,
|
|
1284
|
+
readOnly,
|
|
1285
|
+
dataTestId,
|
|
1286
|
+
t,
|
|
1287
|
+
prefix,
|
|
1288
|
+
hasError,
|
|
1289
|
+
getOptionLabelDescription,
|
|
1290
|
+
fieldSize,
|
|
1291
|
+
getOptionPrefix,
|
|
1292
|
+
]);
|
|
1293
|
+
return customComponents;
|
|
1055
1294
|
};
|
|
1295
|
+
|
|
1056
1296
|
/**
|
|
1057
|
-
*
|
|
1058
|
-
*
|
|
1059
|
-
*
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
const renderAsReadonly = Boolean(rest.readOnly);
|
|
1064
|
-
const htmlForId = react.useMemo(() => (id ? id : "colorField-" + sharedUtils.uuidv4()), [id]);
|
|
1065
|
-
const innerRef = react.useRef(null);
|
|
1066
|
-
react.useImperativeHandle(ref, () => innerRef.current, []);
|
|
1067
|
-
const [t] = useTranslation();
|
|
1068
|
-
// Internal state for color value
|
|
1069
|
-
const [innerValue, setInnerValue] = react.useState(propValue || defaultValue || "");
|
|
1070
|
-
const [renderAsInvalid, setRenderAsInvalid] = react.useState(!!errorMessage || (innerValue && typeof innerValue === "string" && !isValidHEXColor(innerValue)) || isInvalid);
|
|
1071
|
-
const errorType = react.useMemo(() => validateColorCode(innerValue, rest.required), [rest.required, innerValue]);
|
|
1072
|
-
const error = react.useMemo(() => (errorType ? t(`colorField.error.${errorType}`) : errorMessage), [errorType, errorMessage, t]);
|
|
1073
|
-
const handleInputChange = react.useCallback((event) => {
|
|
1074
|
-
const newValue = event.target.value;
|
|
1075
|
-
setInnerValue(newValue);
|
|
1076
|
-
if (onChange) {
|
|
1077
|
-
onChange(event);
|
|
1078
|
-
}
|
|
1079
|
-
}, [onChange]);
|
|
1080
|
-
const handleBlur = react.useCallback(event => {
|
|
1081
|
-
const newValue = event.target.value;
|
|
1082
|
-
setInnerValue(newValue);
|
|
1083
|
-
setRenderAsInvalid(!!errorType);
|
|
1084
|
-
onBlur?.(event);
|
|
1085
|
-
}, [errorType, onBlur]);
|
|
1086
|
-
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(renderAsDisabled || renderAsReadonly) : false, tip: tip, children: jsxRuntime.jsxs("div", { className: cvaInput$1({
|
|
1087
|
-
size: fieldSize,
|
|
1088
|
-
disabled: renderAsDisabled,
|
|
1089
|
-
invalid: renderAsInvalid,
|
|
1090
|
-
className,
|
|
1091
|
-
}), "data-testid": dataTestId ? `${dataTestId}-container` : undefined, children: [jsxRuntime.jsx("input", { "aria-labelledby": htmlForId + "-label", className: cvaInputColorField({ readOnly: renderAsReadonly }), "data-testid": dataTestId, defaultValue: defaultValue, disabled: renderAsDisabled, id: htmlForId, onBlur: handleBlur, onChange: handleInputChange, readOnly: renderAsReadonly, ref: innerRef, type: "color", value: innerValue }), jsxRuntime.jsx("input", { "aria-labelledby": htmlForId + "-label-text", className: cvaInputField({
|
|
1092
|
-
readOnly: renderAsReadonly,
|
|
1093
|
-
disabled: renderAsDisabled,
|
|
1094
|
-
className: "px-1 focus-visible:outline-none",
|
|
1095
|
-
}), "data-testid": dataTestId ? `${dataTestId}-textField` : undefined, disabled: renderAsDisabled, onBlur: handleBlur, onChange: handleInputChange, readOnly: renderAsReadonly, type: "text", value: innerValue }), jsxRuntime.jsx(GenericActionsRenderer, { disabled: renderAsDisabled || renderAsReadonly, fieldSize: fieldSize, genericAction: "edit", innerRef: innerRef, tooltipLabel: t("colorField.tooltip") })] }) }));
|
|
1096
|
-
});
|
|
1097
|
-
ColorField.displayName = "ColorField";
|
|
1098
|
-
|
|
1099
|
-
/**
|
|
1100
|
-
* The date field component is used for entering date values.
|
|
1101
|
-
*
|
|
1102
|
-
* _**Do use**_ the DateField for date input.
|
|
1103
|
-
*
|
|
1104
|
-
* _**Do not use**_ this fields for non-serialized dates. Use TextField instead.
|
|
1297
|
+
* @template IsMulti
|
|
1298
|
+
* @template Group
|
|
1299
|
+
* @param {RefObject<HTMLDivElement | null>} refContainer react ref to container element
|
|
1300
|
+
* @param {number | undefined} maxSelectedDisplayCount a number of max display count
|
|
1301
|
+
* @param {StylesConfig<Option, IsMulti, Group> | undefined} styles a optional object to override styles of react-select
|
|
1302
|
+
* @returns {StylesConfig<Option, boolean>} styles to override in select
|
|
1105
1303
|
*/
|
|
1106
|
-
const
|
|
1107
|
-
const
|
|
1108
|
-
|
|
1109
|
-
|
|
1304
|
+
const useCustomStyles = ({ refContainer, maxSelectedDisplayCount, styles, disabled, fieldSize, }) => {
|
|
1305
|
+
const customStyles = react.useMemo(() => {
|
|
1306
|
+
return {
|
|
1307
|
+
control: base => {
|
|
1308
|
+
return {
|
|
1309
|
+
...base,
|
|
1310
|
+
minHeight: fieldSize === "small" ? "28px" : fieldSize === "large" ? "40px" : "32px",
|
|
1311
|
+
borderRadius: "var(--border-radius-lg)",
|
|
1312
|
+
backgroundColor: "inherit",
|
|
1313
|
+
};
|
|
1314
|
+
},
|
|
1315
|
+
singleValue: base => ({
|
|
1316
|
+
...base,
|
|
1317
|
+
}),
|
|
1318
|
+
multiValue: base => ({
|
|
1319
|
+
...base,
|
|
1320
|
+
}),
|
|
1321
|
+
multiValueLabel: base => ({
|
|
1322
|
+
...base,
|
|
1323
|
+
}),
|
|
1324
|
+
indicatorsContainer: base => ({
|
|
1325
|
+
...base,
|
|
1326
|
+
...(disabled && { display: "none" }),
|
|
1327
|
+
}),
|
|
1328
|
+
indicatorSeparator: () => ({
|
|
1329
|
+
width: "0px",
|
|
1330
|
+
}),
|
|
1331
|
+
menu: base => {
|
|
1332
|
+
return {
|
|
1333
|
+
...base,
|
|
1334
|
+
width: "100%",
|
|
1335
|
+
marginTop: "4px",
|
|
1336
|
+
marginBottom: "18px",
|
|
1337
|
+
transition: "all 1s ease-in-out",
|
|
1338
|
+
};
|
|
1339
|
+
},
|
|
1340
|
+
input: base => ({
|
|
1341
|
+
...base,
|
|
1342
|
+
marginLeft: "0px",
|
|
1343
|
+
}),
|
|
1344
|
+
placeholder: base => ({
|
|
1345
|
+
...base,
|
|
1346
|
+
}),
|
|
1347
|
+
option: () => ({}),
|
|
1348
|
+
menuPortal: base => ({
|
|
1349
|
+
...base,
|
|
1350
|
+
width: refContainer.current ? `${refContainer.current.clientWidth}px` : base.width,
|
|
1351
|
+
backgroundColor: "#ffffff",
|
|
1352
|
+
borderRadius: "var(--border-radius-lg)",
|
|
1353
|
+
zIndex: "var(--z-overlay)",
|
|
1354
|
+
borderColor: "rgb(var(--color-neutral-300))",
|
|
1355
|
+
boxShadow: "var(--tw-ring-inset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)",
|
|
1356
|
+
}),
|
|
1357
|
+
menuList: base => {
|
|
1358
|
+
return {
|
|
1359
|
+
...base,
|
|
1360
|
+
position: "relative",
|
|
1361
|
+
padding: "var(--spacing-1)",
|
|
1362
|
+
display: "grid",
|
|
1363
|
+
gap: "var(--spacing-1)",
|
|
1364
|
+
width: "100%",
|
|
1365
|
+
borderRadius: "0px",
|
|
1366
|
+
boxShadow: "none",
|
|
1367
|
+
paddingTop: "0px",
|
|
1368
|
+
};
|
|
1369
|
+
},
|
|
1370
|
+
valueContainer: base => {
|
|
1371
|
+
return {
|
|
1372
|
+
...base,
|
|
1373
|
+
paddingBlock: 0,
|
|
1374
|
+
flexWrap: maxSelectedDisplayCount !== undefined ? "wrap" : "nowrap",
|
|
1375
|
+
gap: "0.25rem",
|
|
1376
|
+
};
|
|
1377
|
+
},
|
|
1378
|
+
container: base => ({
|
|
1379
|
+
...base,
|
|
1380
|
+
width: "100%",
|
|
1381
|
+
}),
|
|
1382
|
+
dropdownIndicator: base => ({
|
|
1383
|
+
...base,
|
|
1384
|
+
padding: "0px",
|
|
1385
|
+
}),
|
|
1386
|
+
clearIndicator: base => {
|
|
1387
|
+
return {
|
|
1388
|
+
...base,
|
|
1389
|
+
padding: "0px",
|
|
1390
|
+
};
|
|
1391
|
+
},
|
|
1392
|
+
...styles,
|
|
1393
|
+
};
|
|
1394
|
+
}, [refContainer, disabled, fieldSize, maxSelectedDisplayCount, styles]);
|
|
1395
|
+
return { customStyles };
|
|
1110
1396
|
};
|
|
1111
|
-
DateField.displayName = "DateField";
|
|
1112
|
-
|
|
1113
|
-
const cvaDropZone = cssClassVarianceUtilities.cvaMerge([
|
|
1114
|
-
"flex",
|
|
1115
|
-
"component-baseInput-background",
|
|
1116
|
-
"justify-center",
|
|
1117
|
-
"text-neutral-500",
|
|
1118
|
-
"rounded-lg",
|
|
1119
|
-
"border-2",
|
|
1120
|
-
"border-neutral-200",
|
|
1121
|
-
"border-dashed",
|
|
1122
|
-
"hover:bg-neutral-100",
|
|
1123
|
-
"hover:border-solid",
|
|
1124
|
-
"hover:border-primary-500",
|
|
1125
|
-
], {
|
|
1126
|
-
variants: {
|
|
1127
|
-
size: {
|
|
1128
|
-
small: ["p-2"],
|
|
1129
|
-
medium: ["p-4"],
|
|
1130
|
-
large: ["p-8"],
|
|
1131
|
-
},
|
|
1132
|
-
disabled: { true: ["bg-neutral-100", "hover:bg-neutral-100"], false: "" },
|
|
1133
|
-
dragActive: { true: ["border-neutral-200", "bg-neutral-100"], false: "" },
|
|
1134
|
-
dropComplete: {
|
|
1135
|
-
true: [
|
|
1136
|
-
"border-solid",
|
|
1137
|
-
"border-primary-500",
|
|
1138
|
-
"bg-neutral-100",
|
|
1139
|
-
"hover:border-solid",
|
|
1140
|
-
"hover:border-neutral-200",
|
|
1141
|
-
],
|
|
1142
|
-
false: "",
|
|
1143
|
-
},
|
|
1144
|
-
invalid: { true: ["border-danger-600", "text-danger-500"], false: "" },
|
|
1145
|
-
},
|
|
1146
|
-
});
|
|
1147
|
-
const cvaDropZoneLabel = cssClassVarianceUtilities.cvaMerge([
|
|
1148
|
-
"h-full",
|
|
1149
|
-
"pt-1",
|
|
1150
|
-
"pb-1",
|
|
1151
|
-
"gap-2",
|
|
1152
|
-
"items-center",
|
|
1153
|
-
"flex-col",
|
|
1154
|
-
"flex",
|
|
1155
|
-
"justify-center",
|
|
1156
|
-
]);
|
|
1157
|
-
const cvaDropZoneIconBackground = cssClassVarianceUtilities.cvaMerge(["relative", "flex", "items-center", "justify-center", "rounded-full", "p-3"], {
|
|
1158
|
-
variants: {
|
|
1159
|
-
invalid: { true: ["bg-red-100"], false: ["bg-neutral-200"] },
|
|
1160
|
-
},
|
|
1161
|
-
});
|
|
1162
1397
|
|
|
1163
1398
|
/**
|
|
1399
|
+
* A hook used by selects to share the common code
|
|
1164
1400
|
*
|
|
1165
|
-
*
|
|
1401
|
+
* @param {SelectProps} props - The props for the Select component
|
|
1402
|
+
* @returns {UseSelectProps} Select component
|
|
1166
1403
|
*/
|
|
1167
|
-
const
|
|
1168
|
-
|
|
1169
|
-
|
|
1404
|
+
const useSelect = ({ id, className, dataTestId = "select", prefix, async, maxMenuHeight = 200, label, hasError, disabled, isMulti, components, value, options, onChange, isLoading, classNamePrefix = "", onMenuOpen, onMenuClose, maxSelectedDisplayCount = undefined, isClearable = false, isSearchable = true, onMenuScrollToBottom, styles, filterOption, onInputChange, getOptionLabelDescription, getOptionPrefix, fieldSize = "medium", ...props }) => {
|
|
1405
|
+
const refContainer = react.useRef(document.createElement("div"));
|
|
1406
|
+
const { customStyles } = useCustomStyles({
|
|
1407
|
+
refContainer,
|
|
1408
|
+
maxSelectedDisplayCount,
|
|
1409
|
+
styles,
|
|
1410
|
+
disabled: Boolean(disabled),
|
|
1411
|
+
fieldSize,
|
|
1412
|
+
});
|
|
1413
|
+
const [menuIsOpen, setMenuIsOpen] = react.useState(props.menuIsOpen ?? false);
|
|
1414
|
+
const [menuIsEnabled, setMenuIsEnabled] = react.useState(true);
|
|
1415
|
+
const customComponents = useCustomComponents({
|
|
1416
|
+
componentsProps: components,
|
|
1417
|
+
disabled: Boolean(disabled),
|
|
1418
|
+
readOnly: Boolean(props.readOnly),
|
|
1419
|
+
setMenuIsEnabled,
|
|
1420
|
+
dataTestId,
|
|
1421
|
+
maxSelectedDisplayCount,
|
|
1422
|
+
prefix,
|
|
1423
|
+
hasError,
|
|
1424
|
+
fieldSize,
|
|
1425
|
+
getOptionLabelDescription,
|
|
1426
|
+
getOptionPrefix,
|
|
1427
|
+
});
|
|
1428
|
+
const menuPlacement = "auto";
|
|
1429
|
+
const openMenuHandler = async () => {
|
|
1430
|
+
onMenuOpen?.();
|
|
1431
|
+
if (menuIsEnabled) {
|
|
1432
|
+
setMenuIsOpen(true);
|
|
1433
|
+
}
|
|
1434
|
+
else {
|
|
1435
|
+
setMenuIsEnabled(true);
|
|
1436
|
+
}
|
|
1437
|
+
};
|
|
1438
|
+
const closeMenuHandler = () => {
|
|
1439
|
+
setMenuIsOpen(false);
|
|
1440
|
+
onMenuClose && onMenuClose();
|
|
1441
|
+
};
|
|
1442
|
+
return {
|
|
1443
|
+
refContainer,
|
|
1444
|
+
customStyles,
|
|
1445
|
+
menuIsOpen,
|
|
1446
|
+
customComponents,
|
|
1447
|
+
menuPlacement,
|
|
1448
|
+
openMenuHandler,
|
|
1449
|
+
closeMenuHandler,
|
|
1450
|
+
};
|
|
1451
|
+
};
|
|
1170
1452
|
|
|
1453
|
+
// This is here to ensure the bundled react-components can expose the react-select for jest in external iris apps.
|
|
1454
|
+
const ReactSyncSelect = ReactSelect.default || ReactSelect;
|
|
1171
1455
|
/**
|
|
1172
|
-
*
|
|
1456
|
+
* Selects are input components used to choose a value from a set.
|
|
1173
1457
|
*
|
|
1174
|
-
* @param {
|
|
1175
|
-
* @returns {ReactElement}
|
|
1458
|
+
* @param {SelectProps} props - The props for the Select component
|
|
1459
|
+
* @returns {ReactElement} Select component
|
|
1176
1460
|
*/
|
|
1177
|
-
const
|
|
1178
|
-
const
|
|
1179
|
-
const
|
|
1180
|
-
const
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1461
|
+
const BaseSelect = (props) => {
|
|
1462
|
+
const { id, dataTestId = "select", prefix, async, maxMenuHeight = 200, label, hasError, disabled, isMulti, menuPosition = "absolute", value, options, onChange, isLoading, classNamePrefix = "select", onMenuScrollToBottom, onInputChange, isSearchable, isClearable = false, readOnly, fieldSize = "medium", openMenuOnClick = !disabled, openMenuOnFocus = false, hideSelectedOptions = false, } = props;
|
|
1463
|
+
const { refContainer, customStyles, menuIsOpen, customComponents, menuPlacement, openMenuHandler, closeMenuHandler } = useSelect(props);
|
|
1464
|
+
const reactSelectProps = react.useMemo(() => ({
|
|
1465
|
+
value,
|
|
1466
|
+
menuPlacement,
|
|
1467
|
+
maxMenuHeight,
|
|
1468
|
+
onChange,
|
|
1469
|
+
"aria-label": label,
|
|
1470
|
+
"data-testid": dataTestId,
|
|
1471
|
+
components: customComponents,
|
|
1472
|
+
styles: customStyles,
|
|
1473
|
+
tabSelectsValue: false,
|
|
1474
|
+
blurInputOnSelect: false,
|
|
1475
|
+
// This configuration allows for more flexible positioning control of the dropdown.
|
|
1476
|
+
// Setting menuPortalTarget to 'null' specifies that the dropdown should be rendered within
|
|
1477
|
+
// the parent element instead of 'document.body'.
|
|
1478
|
+
menuPortalTarget: props.menuPortalTarget !== undefined ? props.menuPortalTarget : document.body,
|
|
1479
|
+
isSearchable: disabled || readOnly ? false : isSearchable,
|
|
1480
|
+
menuShouldBlockScroll: true,
|
|
1481
|
+
menuShouldScrollIntoView: true,
|
|
1482
|
+
openMenuOnFocus,
|
|
1483
|
+
menuIsOpen: !readOnly ? menuIsOpen : false,
|
|
1484
|
+
openMenuOnClick,
|
|
1485
|
+
closeMenuOnSelect: !isMulti,
|
|
1486
|
+
isMulti,
|
|
1487
|
+
classNamePrefix,
|
|
1488
|
+
isLoading,
|
|
1489
|
+
isClearable,
|
|
1490
|
+
id,
|
|
1491
|
+
onMenuScrollToBottom,
|
|
1492
|
+
onInputChange,
|
|
1493
|
+
hideSelectedOptions,
|
|
1494
|
+
isDisabled: Boolean(disabled),
|
|
1495
|
+
}), [
|
|
1496
|
+
classNamePrefix,
|
|
1497
|
+
customComponents,
|
|
1498
|
+
customStyles,
|
|
1499
|
+
dataTestId,
|
|
1500
|
+
disabled,
|
|
1501
|
+
hideSelectedOptions,
|
|
1502
|
+
id,
|
|
1503
|
+
isClearable,
|
|
1504
|
+
isLoading,
|
|
1505
|
+
isMulti,
|
|
1506
|
+
isSearchable,
|
|
1507
|
+
label,
|
|
1508
|
+
maxMenuHeight,
|
|
1509
|
+
menuIsOpen,
|
|
1510
|
+
menuPlacement,
|
|
1511
|
+
onChange,
|
|
1512
|
+
onInputChange,
|
|
1513
|
+
onMenuScrollToBottom,
|
|
1514
|
+
openMenuOnClick,
|
|
1515
|
+
openMenuOnFocus,
|
|
1516
|
+
props.menuPortalTarget,
|
|
1517
|
+
readOnly,
|
|
1518
|
+
value,
|
|
1519
|
+
]);
|
|
1520
|
+
const renderAsDisabled = Boolean(props.disabled) || props.readOnly;
|
|
1521
|
+
return (jsxRuntime.jsxs("div", { className: cvaSelect({
|
|
1522
|
+
invalid: hasError,
|
|
1523
|
+
fieldSize: fieldSize,
|
|
1524
|
+
disabled: renderAsDisabled,
|
|
1525
|
+
className: props.className,
|
|
1526
|
+
}), "data-testid": dataTestId, ref: refContainer, children: [prefix !== undefined ? (jsxRuntime.jsx("div", { className: cvaSelectPrefixSuffix({ kind: "prefix" }), "data-testid": dataTestId ? `${dataTestId}-prefix` : null, children: prefix })) : null, async ? (jsxRuntime.jsx(ReactAsyncSelect, { ...props, ...reactSelectProps, ...async, menuPosition: menuPosition, onMenuClose: closeMenuHandler, onMenuOpen: openMenuHandler, placeholder: renderAsDisabled ? null : props.placeholder })) : (jsxRuntime.jsx(ReactSyncSelect, { ...props, ...reactSelectProps, isMulti: isMulti, menuPosition: menuPosition, onMenuClose: closeMenuHandler, onMenuOpen: openMenuHandler, options: options, placeholder: renderAsDisabled ? null : props.placeholder })), typeof props.disabled === "object" ? (jsxRuntime.jsx("div", { className: cvaSelectPrefixSuffix({ kind: "suffix" }), "data-testid": dataTestId ? `${dataTestId}-locked` : null, children: jsxRuntime.jsx(InputLockReasonTooltip, { ...props.disabled }) })) : null] }));
|
|
1226
1527
|
};
|
|
1528
|
+
BaseSelect.displayName = "BaseSelect";
|
|
1227
1529
|
|
|
1228
|
-
// Doing the same check as we do on the backend
|
|
1229
|
-
// Using OWASP pattern from: https://owasp.org/www-community/OWASP_Validation_Regex_Repository
|
|
1230
|
-
const EMAIL_REGEX = /^[a-zA-Z0-9_+&*-]+(?:\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,7}$/;
|
|
1231
1530
|
/**
|
|
1232
|
-
*
|
|
1233
|
-
*
|
|
1234
|
-
* @
|
|
1235
|
-
* @
|
|
1531
|
+
* CreatableSelects are input components used to choose a value from a set.
|
|
1532
|
+
*
|
|
1533
|
+
* @param {CreatableSelectProps} props - The props for the CreatableSelect component
|
|
1534
|
+
* @returns {ReactElement} CreatableSelect component
|
|
1236
1535
|
*/
|
|
1237
|
-
const
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1536
|
+
const CreatableSelect = (props) => {
|
|
1537
|
+
const { id, dataTestId = "creatableSelect", prefix, async, maxMenuHeight = 200, label, hasError, disabled, isMulti, value, options, onChange, isLoading, classNamePrefix = "creatableSelect", onMenuScrollToBottom, onInputChange, isSearchable, isClearable = false, readOnly, openMenuOnClick = !disabled, openMenuOnFocus = !disabled, allowCreateWhileLoading, onCreateOption, } = props;
|
|
1538
|
+
const { refContainer, customStyles, menuIsOpen, customComponents, menuPlacement, openMenuHandler, closeMenuHandler } = useSelect(props);
|
|
1539
|
+
const reactCreatableSelectProps = react.useMemo(() => ({
|
|
1540
|
+
value,
|
|
1541
|
+
menuPlacement,
|
|
1542
|
+
maxMenuHeight,
|
|
1543
|
+
onChange,
|
|
1544
|
+
"aria-label": label,
|
|
1545
|
+
"data-testid": dataTestId,
|
|
1546
|
+
components: customComponents,
|
|
1547
|
+
styles: customStyles,
|
|
1548
|
+
tabSelectsValue: false,
|
|
1549
|
+
blurInputOnSelect: !isMulti,
|
|
1550
|
+
menuPortalTarget: props.menuPortalTarget || document.body,
|
|
1551
|
+
isSearchable: disabled || readOnly ? false : isSearchable,
|
|
1552
|
+
menuShouldBlockScroll: true,
|
|
1553
|
+
menuShouldScrollIntoView: true,
|
|
1554
|
+
openMenuOnFocus,
|
|
1555
|
+
menuIsOpen: !readOnly ? menuIsOpen : false,
|
|
1556
|
+
openMenuOnClick,
|
|
1557
|
+
closeMenuOnSelect: false,
|
|
1558
|
+
isMulti,
|
|
1559
|
+
classNamePrefix,
|
|
1560
|
+
isLoading,
|
|
1561
|
+
isClearable,
|
|
1562
|
+
id,
|
|
1563
|
+
onMenuScrollToBottom,
|
|
1564
|
+
onInputChange,
|
|
1565
|
+
allowCreateWhileLoading,
|
|
1566
|
+
onCreateOption,
|
|
1567
|
+
isDisabled: Boolean(disabled),
|
|
1568
|
+
}), [
|
|
1569
|
+
allowCreateWhileLoading,
|
|
1570
|
+
classNamePrefix,
|
|
1571
|
+
customComponents,
|
|
1572
|
+
customStyles,
|
|
1573
|
+
dataTestId,
|
|
1574
|
+
disabled,
|
|
1575
|
+
id,
|
|
1576
|
+
isClearable,
|
|
1577
|
+
isLoading,
|
|
1578
|
+
isMulti,
|
|
1579
|
+
isSearchable,
|
|
1580
|
+
label,
|
|
1581
|
+
maxMenuHeight,
|
|
1582
|
+
menuIsOpen,
|
|
1583
|
+
menuPlacement,
|
|
1584
|
+
onChange,
|
|
1585
|
+
onCreateOption,
|
|
1586
|
+
onInputChange,
|
|
1587
|
+
onMenuScrollToBottom,
|
|
1588
|
+
openMenuOnClick,
|
|
1589
|
+
openMenuOnFocus,
|
|
1590
|
+
props.menuPortalTarget,
|
|
1591
|
+
readOnly,
|
|
1592
|
+
value,
|
|
1593
|
+
]);
|
|
1594
|
+
const renderAsDisabled = Boolean(props.disabled) || props.readOnly;
|
|
1595
|
+
return (jsxRuntime.jsxs("div", { className: cvaSelect({ invalid: hasError, disabled: renderAsDisabled, className: props.className }), "data-testid": dataTestId, ref: refContainer, children: [prefix !== undefined ? (jsxRuntime.jsx("div", { className: cvaSelectPrefixSuffix({ kind: "prefix" }), "data-testid": dataTestId ? `${dataTestId}-prefix` : null, children: prefix })) : null, async ? (jsxRuntime.jsx(ReactAsyncCreatableSelect, { ...props, ...reactCreatableSelectProps, ...async, onMenuClose: closeMenuHandler, onMenuOpen: openMenuHandler, placeholder: renderAsDisabled ? null : props.placeholder })) : (jsxRuntime.jsx(ReactCreatableSelect, { ...props, ...reactCreatableSelectProps, hideSelectedOptions: false, isMulti: isMulti, onMenuClose: closeMenuHandler, onMenuOpen: openMenuHandler, options: options, placeholder: renderAsDisabled ? null : props.placeholder })), typeof props.disabled === "object" ? (jsxRuntime.jsx("div", { className: cvaSelectPrefixSuffix({ kind: "suffix" }), "data-testid": dataTestId ? `${dataTestId}-locked` : null, children: jsxRuntime.jsx(InputLockReasonTooltip, { ...props.disabled }) })) : null] }));
|
|
1242
1596
|
};
|
|
1597
|
+
CreatableSelect.displayName = "CreatableSelect";
|
|
1243
1598
|
|
|
1244
1599
|
/**
|
|
1245
|
-
*
|
|
1600
|
+
* The Label component is used for labels for input fields.
|
|
1601
|
+
* This component is **not used directly**, but is part of the FormGroup and Field components.
|
|
1602
|
+
*
|
|
1603
|
+
* @param {LabelProps} props - The props for the Label component
|
|
1604
|
+
* @returns {ReactElement} Label component
|
|
1246
1605
|
*/
|
|
1247
|
-
const
|
|
1248
|
-
|
|
1249
|
-
return undefined;
|
|
1250
|
-
}
|
|
1251
|
-
if (!emailId && required) {
|
|
1252
|
-
return "REQUIRED";
|
|
1253
|
-
}
|
|
1254
|
-
if (emailId && isString(emailId) && validateEmailAddress(emailId)) {
|
|
1255
|
-
return undefined;
|
|
1256
|
-
}
|
|
1257
|
-
return "INVALID_EMAIL";
|
|
1606
|
+
const Label = ({ id, htmlFor, children, className, dataTestId, disabled, isInvalid, }) => {
|
|
1607
|
+
return (jsxRuntime.jsx("label", { className: cvaLabel({ invalid: isInvalid, disabled, className }), "data-testid": dataTestId, htmlFor: htmlFor || "", id: id || "", children: children }));
|
|
1258
1608
|
};
|
|
1259
1609
|
|
|
1610
|
+
const cvaFormGroup = cssClassVarianceUtilities.cvaMerge(["component-formGroup-gap", "group", "form-group"]);
|
|
1611
|
+
const cvaFormGroupContainerBefore = cssClassVarianceUtilities.cvaMerge(["flex", "mb-1", "items-center"]);
|
|
1612
|
+
const cvaFormGroupContainerAfter = cssClassVarianceUtilities.cvaMerge(["flex", "justify-between", "mt-1", "text-xs", "text-neutral-500"], {
|
|
1613
|
+
variants: {
|
|
1614
|
+
invalid: {
|
|
1615
|
+
true: "text-danger-500",
|
|
1616
|
+
false: "",
|
|
1617
|
+
},
|
|
1618
|
+
isWarning: {
|
|
1619
|
+
true: "text-default-500 ",
|
|
1620
|
+
false: "",
|
|
1621
|
+
},
|
|
1622
|
+
},
|
|
1623
|
+
compoundVariants: [
|
|
1624
|
+
{
|
|
1625
|
+
invalid: true,
|
|
1626
|
+
isWarning: true,
|
|
1627
|
+
className: "text-danger-500 ", // Ensures that 'invalid' takes precedence
|
|
1628
|
+
},
|
|
1629
|
+
],
|
|
1630
|
+
});
|
|
1631
|
+
const cvaHelpAddon = cssClassVarianceUtilities.cvaMerge(["ml-auto"]);
|
|
1632
|
+
|
|
1260
1633
|
/**
|
|
1261
|
-
*
|
|
1262
|
-
*
|
|
1263
|
-
* It has an ActionButton which sends an email to the specified address using "mailto:" link.
|
|
1634
|
+
* The FormGroup component should be used to wrap any Input element that needs a label.
|
|
1635
|
+
* Besides a label the component supplies an optional Tooltip, HelpText and HelpAddon support.
|
|
1264
1636
|
*
|
|
1265
|
-
*
|
|
1637
|
+
* @param {FormGroupProps} props - The props for the FormGroup component
|
|
1638
|
+
* @returns {ReactElement} FormGroup component
|
|
1639
|
+
*/
|
|
1640
|
+
const FormGroup = ({ isInvalid, isWarning, helpText, helpAddon, tip, className, dataTestId, label, htmlFor, children, required = false, }) => {
|
|
1641
|
+
const [t] = useTranslation();
|
|
1642
|
+
const validationStateIcon = react.useMemo(() => {
|
|
1643
|
+
const color = isInvalid ? "danger" : isWarning ? "warning" : null;
|
|
1644
|
+
return color ? jsxRuntime.jsx(reactComponents.Icon, { color: color, name: "ExclamationTriangle", size: "small" }) : null;
|
|
1645
|
+
}, [isInvalid, isWarning]);
|
|
1646
|
+
return (jsxRuntime.jsxs("div", { className: cvaFormGroup({ className }), "data-testid": dataTestId, children: [label ? (jsxRuntime.jsxs("div", { className: cvaFormGroupContainerBefore(), children: [jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Label, { className: "component-formGroup-font", dataTestId: dataTestId ? `${dataTestId}-label` : undefined, htmlFor: htmlFor, id: htmlFor + "-label", children: label }), required ? (jsxRuntime.jsx(reactComponents.Tooltip, { "data-testid": "required-asterisk", label: t("field.required.asterisk.tooltip"), children: "*" })) : null] }), tip ? (jsxRuntime.jsx(reactComponents.Tooltip, { className: "ml-1", dataTestId: dataTestId ? `${dataTestId}-tooltip` : undefined, label: tip, placement: "bottom" })) : null] })) : null, children, helpText || helpAddon ? (jsxRuntime.jsxs("div", { className: cvaFormGroupContainerAfter({ invalid: isInvalid, isWarning: isWarning }), children: [helpText ? (jsxRuntime.jsxs("div", { className: "flex gap-1", children: [validationStateIcon, jsxRuntime.jsx("span", { "data-testid": dataTestId ? `${dataTestId}-helpText` : undefined, children: helpText })] })) : undefined, helpAddon ? (jsxRuntime.jsx("span", { className: cvaHelpAddon(), "data-testid": dataTestId ? `${dataTestId}-helpAddon` : null, children: helpAddon })) : null] })) : null] }));
|
|
1647
|
+
};
|
|
1648
|
+
|
|
1649
|
+
/**
|
|
1650
|
+
* The checkbox field component is used for entering boolean values.
|
|
1266
1651
|
*
|
|
1267
|
-
*
|
|
1268
|
-
* For specific input types make sure to use the corresponding input component.
|
|
1652
|
+
* _**Do use**_ the CheckboxField for boolean input.
|
|
1269
1653
|
*/
|
|
1270
|
-
const
|
|
1271
|
-
const
|
|
1272
|
-
|
|
1273
|
-
return window.open(`mailto:${email}`);
|
|
1274
|
-
};
|
|
1275
|
-
const handleChange = react.useCallback(event => {
|
|
1276
|
-
const newValue = event.target.value;
|
|
1277
|
-
onChange?.(event);
|
|
1278
|
-
setEmail(newValue);
|
|
1279
|
-
}, [onChange]);
|
|
1280
|
-
const renderAsInvalid = (email && !validateEmailAddress(email)) || isInvalid;
|
|
1281
|
-
return (jsxRuntime.jsx(BaseInput, { actions: email && email.length > 0 ? (jsxRuntime.jsx(ActionButton, { dataTestId: dataTestId ? `${dataTestId}-emailIcon` : undefined, disabled: disableAction || isInvalid, onClick: sendEmail, size: fieldSize ?? undefined, type: "EMAIL", value: email })) : null, dataTestId: dataTestId, disabled: disabled, fieldSize: fieldSize, isInvalid: renderAsInvalid, onChange: handleChange, placeholder: rest.placeholder || "mail@example.com", ref: ref, type: "email", ...rest }));
|
|
1654
|
+
const CheckboxField = ({ label, id, tip, helpText, helpAddon, isInvalid, className, checked, dataTestId, checkboxLabel, onChange, ref, ...rest }) => {
|
|
1655
|
+
const htmlForId = id ? id : "checkboxField-" + sharedUtils.uuidv4();
|
|
1656
|
+
return (jsxRuntime.jsx(FormGroup, { className: "flex flex-col gap-1", dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: helpText, htmlFor: htmlForId, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(Checkbox, { checked: checked, className: className, dataTestId: dataTestId, id: htmlForId, label: checkboxLabel, onChange: onChange, ref: ref, ...rest }) }));
|
|
1282
1657
|
};
|
|
1658
|
+
CheckboxField.displayName = "CheckboxField";
|
|
1283
1659
|
|
|
1284
1660
|
/**
|
|
1285
|
-
* The EmailField component is used to enter email.
|
|
1286
|
-
* EmailField validates that user enters a valid email address.
|
|
1287
1661
|
*
|
|
1662
|
+
* @param inputValue - value to check if it is a string
|
|
1663
|
+
* @returns {boolean} - true if value is a string
|
|
1288
1664
|
*/
|
|
1289
|
-
const
|
|
1290
|
-
|
|
1291
|
-
const [t] = useTranslation();
|
|
1292
|
-
const [innerValue, setInnerValue] = react.useState(() => {
|
|
1293
|
-
return (value?.toString() || defaultValue?.toString()) ?? "";
|
|
1294
|
-
});
|
|
1295
|
-
const [renderAsInvalid, setRenderAsInvalid] = react.useState(!!errorMessage || (value && isString(value) && !validateEmailAddress(value)) || isInvalid);
|
|
1296
|
-
const errorType = react.useMemo(() => validateEmailId(innerValue ?? "", rest.required), [rest.required, innerValue]);
|
|
1297
|
-
const error = react.useMemo(() => (errorType ? t(`emailField.error.${errorType}`) : errorMessage), [errorType, errorMessage, t]);
|
|
1298
|
-
const handleBlur = react.useCallback(event => {
|
|
1299
|
-
const newValue = event.target.value;
|
|
1300
|
-
setInnerValue(newValue);
|
|
1301
|
-
setRenderAsInvalid(!!errorType);
|
|
1302
|
-
onBlur?.(event);
|
|
1303
|
-
}, [errorType, onBlur]);
|
|
1304
|
-
const handleChange = react.useCallback((event) => {
|
|
1305
|
-
setInnerValue(event.target.value);
|
|
1306
|
-
if (onChange) {
|
|
1307
|
-
onChange(event);
|
|
1308
|
-
}
|
|
1309
|
-
}, [onChange]);
|
|
1310
|
-
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(EmailBaseInput, { "aria-labelledby": htmlForId + "-label", defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, onBlur: handleBlur, onChange: handleChange, ref: ref, value: value, ...rest, className: className, dataTestId: dataTestId }) }));
|
|
1665
|
+
const isString = (inputValue) => {
|
|
1666
|
+
return typeof inputValue === "string";
|
|
1311
1667
|
};
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
return true;
|
|
1320
|
-
}
|
|
1321
|
-
return /^(?=.)([+-]?([0-9]*)(\.([0-9]+))?)$/.test(number);
|
|
1668
|
+
/**
|
|
1669
|
+
*
|
|
1670
|
+
* @param inputValue - value to check if it is a number
|
|
1671
|
+
* @returns {boolean} - true if value is a number
|
|
1672
|
+
*/
|
|
1673
|
+
const isNumber = (inputValue) => {
|
|
1674
|
+
return typeof inputValue === "number";
|
|
1322
1675
|
};
|
|
1676
|
+
|
|
1323
1677
|
/**
|
|
1324
|
-
* Validates a
|
|
1678
|
+
* Validates a url
|
|
1325
1679
|
*/
|
|
1326
|
-
const
|
|
1327
|
-
|
|
1328
|
-
const minValue = typeof min === "string" ? parseFloat(min) : min;
|
|
1329
|
-
const maxValue = typeof max === "string" ? parseFloat(max) : max;
|
|
1330
|
-
if (number === undefined) {
|
|
1331
|
-
return undefined;
|
|
1332
|
-
}
|
|
1333
|
-
// if the value is a string eg:'test'
|
|
1334
|
-
if (number && !isNaN(+number) === false) {
|
|
1335
|
-
return "INVALID_NUMBER";
|
|
1336
|
-
}
|
|
1337
|
-
// if the value is empty and not required
|
|
1338
|
-
if (!parsedNumber && !required && !min && !max && !!number) {
|
|
1680
|
+
const validateColorCode = (colorCode, required) => {
|
|
1681
|
+
if (!colorCode && !required) {
|
|
1339
1682
|
return undefined;
|
|
1340
1683
|
}
|
|
1341
|
-
|
|
1342
|
-
if (required && !!number === false) {
|
|
1684
|
+
if (!colorCode && required) {
|
|
1343
1685
|
return "REQUIRED";
|
|
1344
1686
|
}
|
|
1345
|
-
|
|
1346
|
-
if (minValue && maxValue && isNumberValid(parsedNumber) && !(parsedNumber >= minValue && parsedNumber <= maxValue)) {
|
|
1347
|
-
return "NOT_IN_BETWEEN";
|
|
1348
|
-
}
|
|
1349
|
-
// if the value is less than min
|
|
1350
|
-
if (isNumberValid(parsedNumber) && minValue !== undefined && parsedNumber < minValue) {
|
|
1351
|
-
return "GREATER_THAN";
|
|
1352
|
-
}
|
|
1353
|
-
// if the value is greater than max
|
|
1354
|
-
if (isNumberValid(parsedNumber) && maxValue !== undefined && parsedNumber > maxValue) {
|
|
1355
|
-
return "LESS_THAN";
|
|
1356
|
-
}
|
|
1357
|
-
// if the value is a number and is valid
|
|
1358
|
-
if (isNumber(parsedNumber) && isNumberValid(parsedNumber)) {
|
|
1687
|
+
if (colorCode && isString(colorCode) && isValidHEXColor(colorCode)) {
|
|
1359
1688
|
return undefined;
|
|
1360
1689
|
}
|
|
1361
|
-
return "
|
|
1690
|
+
return "INVALID_HEX_CODE";
|
|
1362
1691
|
};
|
|
1363
1692
|
|
|
1693
|
+
const cvaInputColorField = cssClassVarianceUtilities.cvaMerge([
|
|
1694
|
+
"ml-3",
|
|
1695
|
+
"h-4",
|
|
1696
|
+
"w-4",
|
|
1697
|
+
"self-center",
|
|
1698
|
+
"bg-inherit",
|
|
1699
|
+
"disabled:opacity-50",
|
|
1700
|
+
"disabled:pointer-events-none",
|
|
1701
|
+
"rounded-[4px]",
|
|
1702
|
+
], {
|
|
1703
|
+
variants: {
|
|
1704
|
+
readOnly: {
|
|
1705
|
+
true: "pointer-events-none",
|
|
1706
|
+
false: "",
|
|
1707
|
+
},
|
|
1708
|
+
},
|
|
1709
|
+
compoundVariants: [
|
|
1710
|
+
{
|
|
1711
|
+
readOnly: true,
|
|
1712
|
+
},
|
|
1713
|
+
],
|
|
1714
|
+
defaultVariants: {
|
|
1715
|
+
readOnly: false,
|
|
1716
|
+
},
|
|
1717
|
+
});
|
|
1718
|
+
|
|
1364
1719
|
/**
|
|
1365
|
-
*
|
|
1720
|
+
* Validates if the given value is a valid hex color.
|
|
1366
1721
|
*
|
|
1367
|
-
*
|
|
1722
|
+
* @param value - The string value to be validated.
|
|
1723
|
+
* @returns {boolean} True if the value is a valid hex color, otherwise false.
|
|
1724
|
+
*/
|
|
1725
|
+
const isValidHEXColor = (value) => {
|
|
1726
|
+
const hexRegex = /^#([0-9A-F]{6})$/i;
|
|
1727
|
+
return hexRegex.test(value);
|
|
1728
|
+
};
|
|
1729
|
+
/**
|
|
1730
|
+
* The ColorField component is used to enter color.
|
|
1731
|
+
* ColorField validates that user enters a valid color address.
|
|
1368
1732
|
*
|
|
1369
|
-
* _**Do not use**_ this fields for non-serialized numbers. Use TextField instead.
|
|
1370
1733
|
*/
|
|
1371
|
-
const
|
|
1372
|
-
const
|
|
1734
|
+
const ColorField = react.forwardRef(({ label, id, tip, helpText, errorMessage, helpAddon, className, defaultValue, dataTestId, value: propValue, onChange, isInvalid = false, onBlur, fieldSize = "medium", ...rest }, ref) => {
|
|
1735
|
+
const renderAsDisabled = Boolean(rest.disabled);
|
|
1736
|
+
const renderAsReadonly = Boolean(rest.readOnly);
|
|
1737
|
+
const htmlForId = react.useMemo(() => (id ? id : "colorField-" + sharedUtils.uuidv4()), [id]);
|
|
1738
|
+
const innerRef = react.useRef(null);
|
|
1739
|
+
react.useImperativeHandle(ref, () => innerRef.current, []);
|
|
1373
1740
|
const [t] = useTranslation();
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
const
|
|
1378
|
-
|
|
1379
|
-
const
|
|
1380
|
-
const error = react.useMemo(() => {
|
|
1381
|
-
// for the case when a custom error message is provided
|
|
1382
|
-
if (errorMessage) {
|
|
1383
|
-
return errorMessage;
|
|
1384
|
-
}
|
|
1385
|
-
else if (errorType) {
|
|
1386
|
-
return t(`numberField.error.${errorType}`, { min: rest.min, max: rest.max });
|
|
1387
|
-
}
|
|
1388
|
-
return errorMessage;
|
|
1389
|
-
}, [errorMessage, errorType, rest.max, rest.min, t]);
|
|
1390
|
-
react.useEffect(() => {
|
|
1391
|
-
if (errorMessage) {
|
|
1392
|
-
setRenderAsInvalid(Boolean(errorMessage));
|
|
1393
|
-
}
|
|
1394
|
-
}, [errorMessage]);
|
|
1395
|
-
const handleBlur = react.useCallback(event => {
|
|
1741
|
+
// Internal state for color value
|
|
1742
|
+
const [innerValue, setInnerValue] = react.useState(propValue || defaultValue || "");
|
|
1743
|
+
const [renderAsInvalid, setRenderAsInvalid] = react.useState(!!errorMessage || (innerValue && typeof innerValue === "string" && !isValidHEXColor(innerValue)) || isInvalid);
|
|
1744
|
+
const errorType = react.useMemo(() => validateColorCode(innerValue, rest.required), [rest.required, innerValue]);
|
|
1745
|
+
const error = react.useMemo(() => (errorType ? t(`colorField.error.${errorType}`) : errorMessage), [errorType, errorMessage, t]);
|
|
1746
|
+
const handleInputChange = react.useCallback((event) => {
|
|
1396
1747
|
const newValue = event.target.value;
|
|
1397
|
-
setInnerValue(newValue
|
|
1398
|
-
// for the case when a custom error message is provided
|
|
1399
|
-
if (errorMessage && !validateNumber(newValue, rest.required, rest.min, rest.max)) {
|
|
1400
|
-
setRenderAsInvalid(Boolean(errorMessage));
|
|
1401
|
-
}
|
|
1402
|
-
else {
|
|
1403
|
-
setRenderAsInvalid(!!validateNumber(newValue, rest.required, rest.min, rest.max));
|
|
1404
|
-
}
|
|
1405
|
-
onBlur?.(event);
|
|
1406
|
-
}, [errorMessage, onBlur, rest.max, rest.min, rest.required]);
|
|
1407
|
-
const handleChange = react.useCallback((event) => {
|
|
1408
|
-
setInnerValue(event.target.value);
|
|
1748
|
+
setInnerValue(newValue);
|
|
1409
1749
|
if (onChange) {
|
|
1410
1750
|
onChange(event);
|
|
1411
1751
|
}
|
|
1412
1752
|
}, [onChange]);
|
|
1413
|
-
|
|
1753
|
+
const handleBlur = react.useCallback(event => {
|
|
1754
|
+
const newValue = event.target.value;
|
|
1755
|
+
setInnerValue(newValue);
|
|
1756
|
+
setRenderAsInvalid(!!errorType);
|
|
1757
|
+
onBlur?.(event);
|
|
1758
|
+
}, [errorType, onBlur]);
|
|
1759
|
+
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(renderAsDisabled || renderAsReadonly) : false, tip: tip, children: jsxRuntime.jsxs("div", { className: cvaInput$1({
|
|
1760
|
+
size: fieldSize,
|
|
1761
|
+
disabled: renderAsDisabled,
|
|
1762
|
+
invalid: renderAsInvalid,
|
|
1763
|
+
readOnly: renderAsReadonly,
|
|
1764
|
+
className,
|
|
1765
|
+
}), "data-testid": dataTestId ? `${dataTestId}-container` : undefined, children: [jsxRuntime.jsx("input", { "aria-labelledby": htmlForId + "-label", className: cvaInputColorField({ readOnly: renderAsReadonly }), "data-testid": dataTestId, defaultValue: defaultValue, disabled: renderAsDisabled, id: htmlForId, onBlur: handleBlur, onChange: handleInputChange, readOnly: renderAsReadonly, ref: innerRef, type: "color", value: innerValue }), jsxRuntime.jsx("input", { "aria-labelledby": htmlForId + "-label-text", className: cvaInputElement({
|
|
1766
|
+
className: "px-1 focus-visible:outline-none",
|
|
1767
|
+
}), "data-testid": dataTestId ? `${dataTestId}-textField` : undefined, disabled: renderAsDisabled, onBlur: handleBlur, onChange: handleInputChange, readOnly: renderAsReadonly, type: "text", value: innerValue }), jsxRuntime.jsx(GenericActionsRenderer, { disabled: renderAsDisabled || renderAsReadonly, fieldSize: fieldSize, genericAction: "edit", innerRef: innerRef, tooltipLabel: t("colorField.tooltip") })] }) }));
|
|
1768
|
+
});
|
|
1769
|
+
ColorField.displayName = "ColorField";
|
|
1770
|
+
|
|
1771
|
+
/**
|
|
1772
|
+
* The date field component is used for entering date values.
|
|
1773
|
+
*
|
|
1774
|
+
* _**Do use**_ the DateField for date input.
|
|
1775
|
+
*
|
|
1776
|
+
* _**Do not use**_ this fields for non-serialized dates. Use TextField instead.
|
|
1777
|
+
*/
|
|
1778
|
+
const DateField = ({ label, id, tip, helpText, errorMessage, helpAddon, isInvalid, className, defaultValue, dataTestId, ref, ...rest }) => {
|
|
1779
|
+
const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
|
|
1780
|
+
const htmlForId = id ? id : "dateField-" + sharedUtils.uuidv4();
|
|
1781
|
+
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(DateBaseInput, { "aria-labelledby": htmlForId + "-label", defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, ref: ref, ...rest, className: className, dataTestId: dataTestId }) }));
|
|
1414
1782
|
};
|
|
1415
|
-
|
|
1783
|
+
DateField.displayName = "DateField";
|
|
1416
1784
|
|
|
1417
|
-
const
|
|
1418
|
-
"group",
|
|
1419
|
-
"transition",
|
|
1420
|
-
"bg-white",
|
|
1421
|
-
"outline",
|
|
1422
|
-
"outline-1",
|
|
1423
|
-
"outline-neutral-300",
|
|
1424
|
-
"hover:bg-neutral-100",
|
|
1425
|
-
"focus:bg-neutral-200",
|
|
1426
|
-
"active:bg-neutral-200",
|
|
1427
|
-
"peer-checked:bg-primary-50",
|
|
1428
|
-
"peer-checked:outline-primary-600",
|
|
1429
|
-
"peer-checked:outline-2",
|
|
1785
|
+
const cvaDropZone = cssClassVarianceUtilities.cvaMerge([
|
|
1430
1786
|
"flex",
|
|
1431
|
-
"
|
|
1787
|
+
"component-baseInput-background",
|
|
1432
1788
|
"justify-center",
|
|
1433
|
-
"
|
|
1434
|
-
"
|
|
1435
|
-
"
|
|
1436
|
-
"
|
|
1789
|
+
"text-neutral-500",
|
|
1790
|
+
"rounded-lg",
|
|
1791
|
+
"border-2",
|
|
1792
|
+
"border-neutral-200",
|
|
1793
|
+
"border-dashed",
|
|
1794
|
+
"hover:bg-neutral-100",
|
|
1795
|
+
"hover:border-solid",
|
|
1796
|
+
"hover:border-primary-500",
|
|
1437
1797
|
], {
|
|
1438
1798
|
variants: {
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
layout: {
|
|
1444
|
-
default: ["flex-col", "p-responsive-space", "w-full", "aspect-square"],
|
|
1445
|
-
compact: ["px-3", "py-1.5", "h-8", "min-h-[calc(var(--line-height-sm)+var(--spacing-3))]", "flex-row", "w-fit"],
|
|
1799
|
+
size: {
|
|
1800
|
+
small: ["p-2"],
|
|
1801
|
+
medium: ["p-4"],
|
|
1802
|
+
large: ["p-8"],
|
|
1446
1803
|
},
|
|
1447
|
-
|
|
1448
|
-
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
true: ["text-neutral-400"],
|
|
1459
|
-
false: ["focus:text-neutral-800", "active:text-neutral-800"],
|
|
1460
|
-
},
|
|
1461
|
-
},
|
|
1462
|
-
});
|
|
1463
|
-
const cvaOptionCardText = cssClassVarianceUtilities.cvaMerge(["text-neutral-600", "text-sm"], {
|
|
1464
|
-
variants: {
|
|
1465
|
-
type: {
|
|
1466
|
-
subheading: ["font-medium"],
|
|
1467
|
-
description: ["font-normal"],
|
|
1468
|
-
},
|
|
1469
|
-
disabled: {
|
|
1470
|
-
true: ["text-neutral-400"],
|
|
1471
|
-
false: ["focus:text-neutral-800", "active:text-neutral-800"],
|
|
1472
|
-
},
|
|
1473
|
-
},
|
|
1474
|
-
});
|
|
1475
|
-
const cvaInput = cssClassVarianceUtilities.cvaMerge(["peer", "absolute", "h-0", "w-0", "opacity-0"]);
|
|
1476
|
-
const cvaCustomImage = cssClassVarianceUtilities.cvaMerge(["text-neutral-400"], {
|
|
1477
|
-
variants: {
|
|
1478
|
-
disabled: {
|
|
1479
|
-
true: ["!text-neutral-400"],
|
|
1480
|
-
false: [""],
|
|
1804
|
+
disabled: { true: ["bg-neutral-100", "hover:bg-neutral-100"], false: "" },
|
|
1805
|
+
dragActive: { true: ["border-neutral-200", "bg-neutral-100"], false: "" },
|
|
1806
|
+
dropComplete: {
|
|
1807
|
+
true: [
|
|
1808
|
+
"border-solid",
|
|
1809
|
+
"border-primary-500",
|
|
1810
|
+
"bg-neutral-100",
|
|
1811
|
+
"hover:border-solid",
|
|
1812
|
+
"hover:border-neutral-200",
|
|
1813
|
+
],
|
|
1814
|
+
false: "",
|
|
1481
1815
|
},
|
|
1816
|
+
invalid: { true: ["border-danger-600", "text-danger-500"], false: "" },
|
|
1482
1817
|
},
|
|
1483
1818
|
});
|
|
1484
|
-
const
|
|
1819
|
+
const cvaDropZoneLabel = cssClassVarianceUtilities.cvaMerge([
|
|
1820
|
+
"h-full",
|
|
1821
|
+
"pt-1",
|
|
1822
|
+
"pb-1",
|
|
1823
|
+
"gap-2",
|
|
1824
|
+
"items-center",
|
|
1825
|
+
"flex-col",
|
|
1826
|
+
"flex",
|
|
1827
|
+
"justify-center",
|
|
1828
|
+
]);
|
|
1829
|
+
const cvaDropZoneIconBackground = cssClassVarianceUtilities.cvaMerge(["relative", "flex", "items-center", "justify-center", "rounded-full", "p-3"], {
|
|
1485
1830
|
variants: {
|
|
1486
|
-
|
|
1487
|
-
default: ["absolute", "top-2", "right-2"],
|
|
1488
|
-
compact: [],
|
|
1489
|
-
},
|
|
1831
|
+
invalid: { true: ["bg-red-100"], false: ["bg-neutral-200"] },
|
|
1490
1832
|
},
|
|
1491
1833
|
});
|
|
1492
1834
|
|
|
1493
1835
|
/**
|
|
1494
|
-
*
|
|
1836
|
+
*
|
|
1837
|
+
* Default UX-intuitive label for the DropZone - can be overwritten by the label prop
|
|
1495
1838
|
*/
|
|
1496
|
-
const
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
return (jsxRuntime.jsx(reactComponents.Tooltip, { className: "w-fit", disabled: layout !== "compact" || (!subheading && !description), label: subContent, mode: "light", placement: "top", children: jsxRuntime.jsxs("div", { className: cvaOptionCardContainer(), "data-testid": dataTestId, children: [jsxRuntime.jsx("input", { className: cvaInput(), "data-testid": `${dataTestId}-option-card`, disabled: disabled, id: htmlForId, ref: ref, type: "radio", value: value, ...rest }), jsxRuntime.jsxs("label", { className: cvaOptionCardLabel({ className, disabled, layout }), "data-testid": `${dataTestId}-option-card-label`, htmlFor: htmlForId, children: [disabled && icon && !customImage
|
|
1500
|
-
? react.cloneElement(icon, { className: cvaCustomImage({ disabled, className: icon.props.className }) })
|
|
1501
|
-
: null, disabled && customImage ? jsxRuntime.jsx("img", { alt: "logo", className: customImage.className, src: customImage.src }) : null, !disabled && !customImage && icon, !disabled && customImage ? jsxRuntime.jsx("img", { alt: "logo", className: customImage.className, src: customImage.src }) : null, heading ? (layout === "default" ? (jsxRuntime.jsx(reactComponents.Heading, { className: cvaOptionCardTitle({ disabled, layout }), subtle: disabled, variant: "secondary", children: heading })) : (jsxRuntime.jsx(reactComponents.Text, { align: "center", className: cvaOptionCardTitle({ disabled, layout }), subtle: disabled, type: "span", weight: "thick", children: heading }))) : null, layout === "default" && (subheading || description) ? subContent : null, tagProps ? jsxRuntime.jsx(reactComponents.Tag, { className: cvaTag({ className: tagProps.className, layout }), ...tagProps }) : null] })] }) }));
|
|
1502
|
-
};
|
|
1503
|
-
OptionCard.displayName = "OptionCard";
|
|
1839
|
+
const DropZoneDefaultLabel = () => (jsxRuntime.jsx(Trans, { components: {
|
|
1840
|
+
clickable: jsxRuntime.jsx("span", { className: "text-primary-600 hover:text-primary-700 cursor-pointer underline" }),
|
|
1841
|
+
}, i18nKey: "dropzone.label.default", values: {} }));
|
|
1504
1842
|
|
|
1505
1843
|
/**
|
|
1506
|
-
*
|
|
1844
|
+
* The Drop Zone can be used to drag and drop files or to browse and select files from the file system.
|
|
1507
1845
|
*
|
|
1508
|
-
*
|
|
1846
|
+
* @param {DropZoneProps} props - The props for the DropZone component
|
|
1847
|
+
* @returns {ReactElement} DropZone component
|
|
1509
1848
|
*/
|
|
1510
|
-
const
|
|
1511
|
-
const [
|
|
1512
|
-
|
|
1849
|
+
const DropZone = ({ className, dataTestId, filesSelected, label = jsxRuntime.jsx(DropZoneDefaultLabel, {}), size = "large", isInvalid = false, disabled = false, accept, multiple = false, ...rest }) => {
|
|
1850
|
+
const [dragActive, setDragActive] = react.useState(false);
|
|
1851
|
+
const [fileDropped, setFileDropped] = react.useState(false);
|
|
1852
|
+
const [t] = useTranslation();
|
|
1853
|
+
const inputLabelRef = react.useRef(null);
|
|
1854
|
+
// function that handles drag enter, drag leave and drag over
|
|
1855
|
+
const handleDrag = (e) => {
|
|
1856
|
+
e.preventDefault();
|
|
1857
|
+
e.stopPropagation();
|
|
1858
|
+
if ((e.type === "dragenter" || e.type === "dragover") && !disabled) {
|
|
1859
|
+
setDragActive(true);
|
|
1860
|
+
}
|
|
1861
|
+
else if (e.type === "dragleave") {
|
|
1862
|
+
setDragActive(false);
|
|
1863
|
+
}
|
|
1864
|
+
};
|
|
1865
|
+
//function to handle when user clicks on dropzone to upload
|
|
1866
|
+
const handleChange = (e) => {
|
|
1867
|
+
e.preventDefault();
|
|
1868
|
+
e.stopPropagation();
|
|
1869
|
+
if (e.target.files && !disabled) {
|
|
1870
|
+
filesSelected(e.target.files);
|
|
1871
|
+
}
|
|
1872
|
+
};
|
|
1873
|
+
//function to handle drop
|
|
1874
|
+
const handleDrop = (e) => {
|
|
1875
|
+
e.preventDefault();
|
|
1876
|
+
e.stopPropagation();
|
|
1877
|
+
setDragActive(false);
|
|
1878
|
+
if (e.dataTransfer.files[0] && !disabled) {
|
|
1879
|
+
filesSelected(e.dataTransfer.files);
|
|
1880
|
+
setFileDropped(true);
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
//function to handle focusable button click (for accessibility)
|
|
1884
|
+
const handleButtonClick = (e) => {
|
|
1885
|
+
e.preventDefault();
|
|
1886
|
+
e.stopPropagation();
|
|
1887
|
+
if (disabled) {
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1890
|
+
inputLabelRef.current?.click();
|
|
1891
|
+
};
|
|
1892
|
+
return (jsxRuntime.jsx("div", { className: cvaDropZone({ size, dropComplete: fileDropped, dragActive, disabled, invalid: isInvalid, className }), "data-testid": dataTestId, onClick: e => {
|
|
1893
|
+
if (disabled) {
|
|
1894
|
+
e.preventDefault();
|
|
1895
|
+
e.stopPropagation();
|
|
1896
|
+
}
|
|
1897
|
+
}, onDragEnter: handleDrag, onDragLeave: handleDrag, onDragOver: handleDrag, onDrop: handleDrop, ...rest, children: jsxRuntime.jsxs("label", { className: cvaDropZoneLabel(), "data-testid": dataTestId ? `${dataTestId}-label` : null, ref: inputLabelRef, children: [jsxRuntime.jsx("input", { accept: accept, className: "hidden", multiple: multiple, onChange: handleChange, title: t("dropzone.input.title"), type: "file" }), jsxRuntime.jsx("div", { className: cvaDropZoneIconBackground({ invalid: isInvalid }), children: jsxRuntime.jsx(reactComponents.Icon, { className: !isInvalid ? "text-neutral-400" : "", color: isInvalid ? "danger" : "neutral", name: "ArrowUpCircle", type: "solid" }) }), jsxRuntime.jsx("button", { disabled: disabled, onClick: handleButtonClick, children: label })] }) }));
|
|
1513
1898
|
};
|
|
1514
1899
|
|
|
1900
|
+
// Doing the same check as we do on the backend
|
|
1901
|
+
// Using OWASP pattern from: https://owasp.org/www-community/OWASP_Validation_Regex_Repository
|
|
1902
|
+
const EMAIL_REGEX = /^[a-zA-Z0-9_+&*-]+(?:\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,7}$/;
|
|
1515
1903
|
/**
|
|
1516
|
-
*
|
|
1517
|
-
*
|
|
1518
|
-
*
|
|
1519
|
-
*
|
|
1520
|
-
* _**Do not use** to confirm user actions, such as deleting. Use a checkbox for such flows._
|
|
1904
|
+
* @description Validate given email id.
|
|
1905
|
+
* @param email The address to validate.
|
|
1906
|
+
* @returns {boolean} Returns true if the email address is valid else false.
|
|
1907
|
+
* @example validateEmailAddress(test@gmail.com) // true
|
|
1521
1908
|
*/
|
|
1522
|
-
const
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
}, [onChange]);
|
|
1528
|
-
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlFor, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(PasswordBaseInput, { ...rest, "aria-labelledby": htmlFor + "-label", className: className, dataTestId: dataTestId, disabled: rest.readOnly, id: htmlFor, isInvalid: renderAsInvalid, maxLength: maxLength, onChange: handleChange, ref: ref, value: value }) }));
|
|
1909
|
+
const validateEmailAddress = (email) => {
|
|
1910
|
+
if (!email) {
|
|
1911
|
+
return false;
|
|
1912
|
+
}
|
|
1913
|
+
return EMAIL_REGEX.test(email);
|
|
1529
1914
|
};
|
|
1530
|
-
PasswordField.displayName = "PasswordField";
|
|
1531
1915
|
|
|
1532
1916
|
/**
|
|
1533
|
-
* Validates a
|
|
1917
|
+
* Validates a email id
|
|
1534
1918
|
*/
|
|
1535
|
-
const
|
|
1536
|
-
if (!
|
|
1537
|
-
return "REQUIRED";
|
|
1538
|
-
}
|
|
1539
|
-
const asYouType = new parsePhoneNumberFromString.AsYouType();
|
|
1540
|
-
asYouType.input(phoneNumber);
|
|
1541
|
-
const countryCode = asYouType.getCallingCode();
|
|
1542
|
-
const national = asYouType.getNationalNumber();
|
|
1543
|
-
const safePhoneNumber = getPhoneNumberWithPlus(phoneNumber.trim());
|
|
1544
|
-
const number = parsePhoneNumberFromString(safePhoneNumber);
|
|
1545
|
-
if (phoneNumber && parsePhoneNumberFromString.isValidPhoneNumber(phoneNumber)) {
|
|
1919
|
+
const validateEmailId = (emailId, required) => {
|
|
1920
|
+
if (!emailId && !required) {
|
|
1546
1921
|
return undefined;
|
|
1547
1922
|
}
|
|
1548
|
-
if (!
|
|
1549
|
-
return "
|
|
1550
|
-
}
|
|
1551
|
-
if (phoneNumber &&
|
|
1552
|
-
(checkIfPhoneNumberHasPlus(phoneNumber)
|
|
1553
|
-
? isNaN(+phoneNumber.slice(1, phoneNumber.length))
|
|
1554
|
-
: isNaN(+phoneNumber) || !number)) {
|
|
1555
|
-
return "NOT_A_NUMBER";
|
|
1923
|
+
if (!emailId && required) {
|
|
1924
|
+
return "REQUIRED";
|
|
1556
1925
|
}
|
|
1557
|
-
if (
|
|
1558
|
-
|
|
1559
|
-
return "TOO_SHORT";
|
|
1926
|
+
if (emailId && isString(emailId) && validateEmailAddress(emailId)) {
|
|
1927
|
+
return undefined;
|
|
1560
1928
|
}
|
|
1561
|
-
return "
|
|
1929
|
+
return "INVALID_EMAIL";
|
|
1562
1930
|
};
|
|
1931
|
+
|
|
1563
1932
|
/**
|
|
1564
|
-
*
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
*
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
* Checks if the phone number is valid and returns corresponding error message
|
|
1933
|
+
* A Email Input component is used for input of the type Email.
|
|
1934
|
+
*
|
|
1935
|
+
* It has an ActionButton which sends an email to the specified address using "mailto:" link.
|
|
1936
|
+
*
|
|
1937
|
+
* Extends props of BaseInput.
|
|
1938
|
+
*
|
|
1939
|
+
* A reference to the input element is provided as the `ref` prop.
|
|
1940
|
+
* For specific input types make sure to use the corresponding input component.
|
|
1573
1941
|
*/
|
|
1574
|
-
const
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
return
|
|
1578
|
-
}
|
|
1579
|
-
|
|
1942
|
+
const EmailBaseInput = ({ fieldSize = "medium", disabled = false, dataTestId, isInvalid = false, onChange, disableAction = false, ref, ...rest }) => {
|
|
1943
|
+
const [email, setEmail] = react.useState(rest.value?.toString() || rest.defaultValue?.toString());
|
|
1944
|
+
const sendEmail = () => {
|
|
1945
|
+
return window.open(`mailto:${email}`);
|
|
1946
|
+
};
|
|
1947
|
+
const handleChange = react.useCallback(event => {
|
|
1948
|
+
const newValue = event.target.value;
|
|
1949
|
+
onChange?.(event);
|
|
1950
|
+
setEmail(newValue);
|
|
1951
|
+
}, [onChange]);
|
|
1952
|
+
const renderAsInvalid = (email && !validateEmailAddress(email)) || isInvalid;
|
|
1953
|
+
return (jsxRuntime.jsx(BaseInput, { actions: email && email.length > 0 ? (jsxRuntime.jsx(ActionButton, { dataTestId: dataTestId ? `${dataTestId}-emailIcon` : undefined, disabled: disableAction || isInvalid, onClick: sendEmail, size: fieldSize ?? undefined, type: "EMAIL", value: email })) : null, dataTestId: dataTestId, disabled: disabled, fieldSize: fieldSize, isInvalid: renderAsInvalid, onChange: handleChange, placeholder: rest.placeholder || "mail@example.com", ref: ref, type: "email", ...rest }));
|
|
1580
1954
|
};
|
|
1581
1955
|
|
|
1582
1956
|
/**
|
|
1583
|
-
* The
|
|
1584
|
-
*
|
|
1585
|
-
* It is used to render a phone number field with a label, a tip, a help text, a help addon and an error message.
|
|
1957
|
+
* The EmailField component is used to enter email.
|
|
1958
|
+
* EmailField validates that user enters a valid email address.
|
|
1586
1959
|
*
|
|
1587
|
-
* @param {string} [label] - The label for the component.
|
|
1588
|
-
* @param {string} [tip] - The tip for the component.
|
|
1589
|
-
* @param {string} [helpText] - The help text for the component.
|
|
1590
|
-
* @param {string} [helpAddon] - The help addon for the component.
|
|
1591
|
-
* @param {string} [errorMessage] - The error message for the component.
|
|
1592
|
-
* @param {string} [defaultValue] - The default value for the component.
|
|
1593
|
-
* @param {boolean} [disabled=false] - Whether the component is disabled or not.
|
|
1594
|
-
* @param {string} [fieldSize="medium"] - The size of the input field.
|
|
1595
|
-
* @param {boolean} [disableAction=false] - Whether the action button is disabled or not.
|
|
1596
1960
|
*/
|
|
1597
|
-
const
|
|
1598
|
-
const htmlForId = id ? id : "
|
|
1961
|
+
const EmailField = ({ label, id, tip, helpText, errorMessage, helpAddon, className, defaultValue, dataTestId, value, onChange, onBlur, isInvalid = false, ref, ...rest }) => {
|
|
1962
|
+
const htmlForId = id ? id : "emailField-" + sharedUtils.uuidv4();
|
|
1599
1963
|
const [t] = useTranslation();
|
|
1600
1964
|
const [innerValue, setInnerValue] = react.useState(() => {
|
|
1601
|
-
return (value?.toString() || defaultValue?.toString()) ??
|
|
1965
|
+
return (value?.toString() || defaultValue?.toString()) ?? "";
|
|
1602
1966
|
});
|
|
1603
|
-
const [renderAsInvalid, setRenderAsInvalid] = react.useState((
|
|
1604
|
-
|
|
1605
|
-
const
|
|
1606
|
-
const
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1967
|
+
const [renderAsInvalid, setRenderAsInvalid] = react.useState(!!errorMessage || (value && isString(value) && !validateEmailAddress(value)) || isInvalid);
|
|
1968
|
+
const errorType = react.useMemo(() => validateEmailId(innerValue ?? "", rest.required), [rest.required, innerValue]);
|
|
1969
|
+
const error = react.useMemo(() => (errorType ? t(`emailField.error.${errorType}`) : errorMessage), [errorType, errorMessage, t]);
|
|
1970
|
+
const handleBlur = react.useCallback(event => {
|
|
1971
|
+
const newValue = event.target.value;
|
|
1972
|
+
setInnerValue(newValue);
|
|
1973
|
+
setRenderAsInvalid(!!errorType);
|
|
1974
|
+
onBlur?.(event);
|
|
1975
|
+
}, [errorType, onBlur]);
|
|
1976
|
+
const handleChange = react.useCallback((event) => {
|
|
1977
|
+
setInnerValue(event.target.value);
|
|
1978
|
+
if (onChange) {
|
|
1979
|
+
onChange(event);
|
|
1980
|
+
}
|
|
1981
|
+
}, [onChange]);
|
|
1982
|
+
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(EmailBaseInput, { "aria-labelledby": htmlForId + "-label", defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, onBlur: handleBlur, onChange: handleChange, ref: ref, value: value, ...rest, className: className, dataTestId: dataTestId }) }));
|
|
1983
|
+
};
|
|
1984
|
+
EmailField.displayName = "EmailField";
|
|
1985
|
+
|
|
1986
|
+
/** Type guard for function refs vs. object refs without deprecated types or assertions */
|
|
1987
|
+
function isWritableRef(r) {
|
|
1988
|
+
return typeof r === "object" && r !== null && "current" in r;
|
|
1989
|
+
}
|
|
1990
|
+
/**
|
|
1991
|
+
* Multi adapter:
|
|
1992
|
+
* - keeps Option[] semantics (via `MultiValue<Option>`)
|
|
1993
|
+
* - renders FormGroup chrome (label, help, error)
|
|
1994
|
+
* - exposes a hidden <select> for a stable ref target
|
|
1995
|
+
* - optionally renders one hidden <input> per selected option IF `getOptionValue` is provided
|
|
1996
|
+
* - passes through all remaining BaseSelect props with isMulti=true
|
|
1997
|
+
*/
|
|
1998
|
+
const FormFieldSelectAdapterMulti = (props) => {
|
|
1999
|
+
const { className, dataTestId, helpText, helpAddon, tip, label, isInvalid, errorMessage, name, onBlur, options, value, defaultValue, id, onChange, children, ref, ...selectProps } = props;
|
|
2000
|
+
// Hidden select for a stable DOM ref target (API parity with single adapter)
|
|
2001
|
+
const innerRef = react.useRef(null);
|
|
2002
|
+
// Bridge external ref (supports both callback and object refs)
|
|
2003
|
+
react.useEffect(() => {
|
|
2004
|
+
if (typeof ref === "function") {
|
|
2005
|
+
ref(innerRef.current);
|
|
2006
|
+
}
|
|
2007
|
+
else if (isWritableRef(ref)) {
|
|
2008
|
+
ref.current = innerRef.current;
|
|
2009
|
+
}
|
|
2010
|
+
}, [ref]);
|
|
2011
|
+
// Determine invalid state
|
|
2012
|
+
const renderAsInvalid = react.useMemo(() => (isInvalid === undefined ? Boolean(errorMessage) : isInvalid), [errorMessage, isInvalid]);
|
|
2013
|
+
// id to connect label and control
|
|
2014
|
+
const controlId = react.useMemo(() => (id ? id : "multiSelectField-" + sharedUtils.uuidv4()), [id]);
|
|
2015
|
+
// If consumers provided getOptionValue (from BaseSelect props),
|
|
2016
|
+
// we can render hidden inputs for native form submit / RHF.
|
|
2017
|
+
const selectPropsWithAccessors = selectProps;
|
|
2018
|
+
const getOptionValue = typeof selectPropsWithAccessors.getOptionValue === "function" ? selectPropsWithAccessors.getOptionValue : undefined;
|
|
2019
|
+
// Compute selected options snapshot for hidden inputs (prefer controlled `value`)
|
|
2020
|
+
const selectedOptions = react.useMemo(() => value ?? defaultValue ?? [], [value, defaultValue]);
|
|
2021
|
+
// Build the exact prop bag for BaseSelect (multi=true).
|
|
2022
|
+
const childProps = {
|
|
2023
|
+
...selectProps,
|
|
2024
|
+
id: controlId,
|
|
2025
|
+
onBlur,
|
|
2026
|
+
options,
|
|
2027
|
+
isMulti: true,
|
|
2028
|
+
value: value ?? null,
|
|
2029
|
+
defaultValue,
|
|
2030
|
+
onChange: next => onChange?.(next),
|
|
2031
|
+
};
|
|
2032
|
+
return (jsxRuntime.jsxs(FormGroup, { className: className, dataTestId: dataTestId, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: controlId, isInvalid: renderAsInvalid, label: label, required: "required" in selectProps && selectProps.required
|
|
2033
|
+
? !(("disabled" in selectProps && Boolean(selectProps.disabled)) ||
|
|
2034
|
+
("readOnly" in selectProps && Boolean(selectProps.readOnly)))
|
|
2035
|
+
: false, tip: tip, children: [jsxRuntime.jsx("select", { "aria-hidden": "true", defaultValue: "", hidden: true, name: name, ref: innerRef }), typeof getOptionValue === "function" &&
|
|
2036
|
+
selectedOptions.map((opt, idx) => {
|
|
2037
|
+
const primitiveValue = getOptionValue(opt);
|
|
2038
|
+
return typeof primitiveValue === "string" ? (jsxRuntime.jsx("input", { name: name, type: "hidden", value: primitiveValue }, `${primitiveValue}-${idx}`)) : null;
|
|
2039
|
+
}), children(childProps)] }));
|
|
2040
|
+
};
|
|
2041
|
+
FormFieldSelectAdapterMulti.displayName = "FormFieldSelectAdapterMulti";
|
|
2042
|
+
|
|
2043
|
+
/**
|
|
2044
|
+
* MultiSelectField — validated multi-select field.
|
|
2045
|
+
* Types mirror BaseSelect: options: Option[], value/defaultValue: Option[], onChange: (Option[] | null) => void
|
|
2046
|
+
* Implemented as a generic const component (no forwardRef, no assertions).
|
|
2047
|
+
*/
|
|
2048
|
+
const MultiSelectField = ({ ref, ...props }) => {
|
|
2049
|
+
return (jsxRuntime.jsx(FormFieldSelectAdapterMulti, { ...props, ref: ref, children: convertedProps => jsxRuntime.jsx(BaseSelect, { ...convertedProps }) }));
|
|
2050
|
+
};
|
|
2051
|
+
MultiSelectField.displayName = "MultiSelectField";
|
|
2052
|
+
|
|
2053
|
+
const isNumberValid = (number) => {
|
|
2054
|
+
if (!isNaN(+number) === false) {
|
|
2055
|
+
return false;
|
|
2056
|
+
}
|
|
2057
|
+
if (typeof number === "number") {
|
|
2058
|
+
return true;
|
|
2059
|
+
}
|
|
2060
|
+
return /^(?=.)([+-]?([0-9]*)(\.([0-9]+))?)$/.test(number);
|
|
2061
|
+
};
|
|
2062
|
+
/**
|
|
2063
|
+
* Validates a number
|
|
2064
|
+
*/
|
|
2065
|
+
const validateNumber = (number, required = false, min, max) => {
|
|
2066
|
+
const parsedNumber = Number(number);
|
|
2067
|
+
const minValue = typeof min === "string" ? parseFloat(min) : min;
|
|
2068
|
+
const maxValue = typeof max === "string" ? parseFloat(max) : max;
|
|
2069
|
+
if (number === undefined) {
|
|
2070
|
+
return undefined;
|
|
2071
|
+
}
|
|
2072
|
+
// if the value is a string eg:'test'
|
|
2073
|
+
if (number && !isNaN(+number) === false) {
|
|
2074
|
+
return "INVALID_NUMBER";
|
|
2075
|
+
}
|
|
2076
|
+
// if the value is empty and not required
|
|
2077
|
+
if (!parsedNumber && !required && !min && !max && !!number) {
|
|
2078
|
+
return undefined;
|
|
2079
|
+
}
|
|
2080
|
+
// if the value is empty and required
|
|
2081
|
+
if (required && !!number === false) {
|
|
2082
|
+
return "REQUIRED";
|
|
2083
|
+
}
|
|
2084
|
+
// if the value is not in between min and max
|
|
2085
|
+
if (minValue && maxValue && isNumberValid(parsedNumber) && !(parsedNumber >= minValue && parsedNumber <= maxValue)) {
|
|
2086
|
+
return "NOT_IN_BETWEEN";
|
|
2087
|
+
}
|
|
2088
|
+
// if the value is less than min
|
|
2089
|
+
if (isNumberValid(parsedNumber) && minValue !== undefined && parsedNumber < minValue) {
|
|
2090
|
+
return "GREATER_THAN";
|
|
2091
|
+
}
|
|
2092
|
+
// if the value is greater than max
|
|
2093
|
+
if (isNumberValid(parsedNumber) && maxValue !== undefined && parsedNumber > maxValue) {
|
|
2094
|
+
return "LESS_THAN";
|
|
2095
|
+
}
|
|
2096
|
+
// if the value is a number and is valid
|
|
2097
|
+
if (isNumber(parsedNumber) && isNumberValid(parsedNumber)) {
|
|
2098
|
+
return undefined;
|
|
2099
|
+
}
|
|
2100
|
+
return "INVALID_NUMBER";
|
|
2101
|
+
};
|
|
2102
|
+
|
|
2103
|
+
/**
|
|
2104
|
+
* The number field component is used for entering numeric values and includes controls for incrementally increasing or decreasing the value.
|
|
2105
|
+
*
|
|
2106
|
+
* _**Do use**_ the NumberField when the controls to incrementally increase or decrease makes the task easier for the user.
|
|
2107
|
+
*
|
|
2108
|
+
* _**Do not use**_ this fields for non-serialized numbers. Use TextField instead.
|
|
2109
|
+
*/
|
|
2110
|
+
const NumberField = ({ label, id, tip, helpText, errorMessage, helpAddon, isInvalid, maxLength, className, value, dataTestId, defaultValue, onBlur, onChange, ref, ...rest }) => {
|
|
2111
|
+
const htmlForId = id ? id : "numberField-" + sharedUtils.uuidv4();
|
|
2112
|
+
const [t] = useTranslation();
|
|
2113
|
+
const [innerValue, setInnerValue] = react.useState(() => {
|
|
2114
|
+
return Number(value?.toString()) || Number(defaultValue?.toString());
|
|
2115
|
+
});
|
|
2116
|
+
const [renderAsInvalid, setRenderAsInvalid] = react.useState((isInvalid === undefined ? Boolean(errorMessage) : isInvalid) ||
|
|
2117
|
+
!!validateNumber(value?.toString(), rest.required, rest.min, rest.max));
|
|
2118
|
+
const errorType = react.useMemo(() => validateNumber(innerValue, rest.required, rest.min, rest.max), [innerValue, rest.max, rest.min, rest.required]);
|
|
2119
|
+
const error = react.useMemo(() => {
|
|
2120
|
+
// for the case when a custom error message is provided
|
|
2121
|
+
if (errorMessage) {
|
|
2122
|
+
return errorMessage;
|
|
2123
|
+
}
|
|
2124
|
+
else if (errorType) {
|
|
2125
|
+
return t(`numberField.error.${errorType}`, { min: rest.min, max: rest.max });
|
|
2126
|
+
}
|
|
1614
2127
|
return errorMessage;
|
|
1615
|
-
}, [errorMessage, errorType, t]);
|
|
2128
|
+
}, [errorMessage, errorType, rest.max, rest.min, t]);
|
|
1616
2129
|
react.useEffect(() => {
|
|
1617
|
-
|
|
2130
|
+
if (errorMessage) {
|
|
2131
|
+
setRenderAsInvalid(Boolean(errorMessage));
|
|
2132
|
+
}
|
|
1618
2133
|
}, [errorMessage]);
|
|
1619
2134
|
const handleBlur = react.useCallback(event => {
|
|
1620
2135
|
const newValue = event.target.value;
|
|
1621
|
-
setInnerValue(newValue);
|
|
2136
|
+
setInnerValue(newValue.toString());
|
|
1622
2137
|
// for the case when a custom error message is provided
|
|
1623
|
-
if (errorMessage && !
|
|
2138
|
+
if (errorMessage && !validateNumber(newValue, rest.required, rest.min, rest.max)) {
|
|
1624
2139
|
setRenderAsInvalid(Boolean(errorMessage));
|
|
1625
2140
|
}
|
|
1626
2141
|
else {
|
|
1627
|
-
setRenderAsInvalid(!!
|
|
2142
|
+
setRenderAsInvalid(!!validateNumber(newValue, rest.required, rest.min, rest.max));
|
|
1628
2143
|
}
|
|
1629
2144
|
onBlur?.(event);
|
|
1630
|
-
}, [errorMessage, onBlur, rest.required]);
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
*/
|
|
1639
|
-
const PhoneFieldWithController = ({ control, controllerProps, name, value, ref, ...rest }) => {
|
|
1640
|
-
return (jsxRuntime.jsx(reactHookForm.Controller, { control: control, defaultValue: value, name: name, ...controllerProps, render: ({ field }) => jsxRuntime.jsx(PhoneField, { ...rest, ...field, ref: ref }) }));
|
|
2145
|
+
}, [errorMessage, onBlur, rest.max, rest.min, rest.required]);
|
|
2146
|
+
const handleChange = react.useCallback((event) => {
|
|
2147
|
+
setInnerValue(event.target.value);
|
|
2148
|
+
if (onChange) {
|
|
2149
|
+
onChange(event);
|
|
2150
|
+
}
|
|
2151
|
+
}, [onChange]);
|
|
2152
|
+
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(NumberBaseInput, { "aria-labelledby": htmlForId + "-label", id: htmlForId, isInvalid: renderAsInvalid, maxLength: maxLength, onBlur: handleBlur, onChange: handleChange, ref: ref, value: value, ...rest, className: className, dataTestId: dataTestId }) }));
|
|
1641
2153
|
};
|
|
1642
|
-
|
|
2154
|
+
NumberField.displayName = "NumberField";
|
|
1643
2155
|
|
|
1644
|
-
const
|
|
2156
|
+
const cvaOptionCardLabel = cssClassVarianceUtilities.cvaMerge([
|
|
2157
|
+
"group",
|
|
2158
|
+
"transition",
|
|
2159
|
+
"bg-white",
|
|
2160
|
+
"outline",
|
|
2161
|
+
"outline-1",
|
|
2162
|
+
"outline-neutral-300",
|
|
2163
|
+
"hover:bg-neutral-100",
|
|
2164
|
+
"focus:bg-neutral-200",
|
|
2165
|
+
"active:bg-neutral-200",
|
|
2166
|
+
"peer-checked:bg-primary-50",
|
|
2167
|
+
"peer-checked:outline-primary-600",
|
|
2168
|
+
"peer-checked:outline-2",
|
|
2169
|
+
"flex",
|
|
2170
|
+
"gap-2",
|
|
2171
|
+
"justify-center",
|
|
2172
|
+
"items-center",
|
|
2173
|
+
"text-center",
|
|
2174
|
+
"rounded-md",
|
|
2175
|
+
"relative",
|
|
2176
|
+
], {
|
|
1645
2177
|
variants: {
|
|
2178
|
+
disabled: {
|
|
2179
|
+
true: ["cursor-not-allowed", "bg-neutral-100"],
|
|
2180
|
+
false: ["cursor-pointer"],
|
|
2181
|
+
},
|
|
1646
2182
|
layout: {
|
|
1647
|
-
|
|
2183
|
+
default: ["flex-col", "p-responsive-space", "w-full", "aspect-square"],
|
|
2184
|
+
compact: ["px-3", "py-1.5", "h-8", "min-h-[calc(var(--line-height-sm)+var(--spacing-3))]", "flex-row", "w-fit"],
|
|
1648
2185
|
},
|
|
1649
2186
|
},
|
|
1650
2187
|
});
|
|
1651
|
-
const
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
"h-4",
|
|
1655
|
-
"appearance-none",
|
|
1656
|
-
"rounded-3xl",
|
|
1657
|
-
"bg-white",
|
|
1658
|
-
"border-solid",
|
|
1659
|
-
"border",
|
|
1660
|
-
"border-neutral-300",
|
|
1661
|
-
"shadow-sm",
|
|
1662
|
-
"shrink-0",
|
|
1663
|
-
"transition",
|
|
1664
|
-
"box-border",
|
|
1665
|
-
"hover:cursor-pointer",
|
|
1666
|
-
"hover:bg-neutral-100",
|
|
1667
|
-
"focus-visible:outline-primary-700",
|
|
1668
|
-
], {
|
|
2188
|
+
const cvaOptionCardContent = cssClassVarianceUtilities.cvaMerge(["flex", "flex-col", "items-center"]);
|
|
2189
|
+
const cvaOptionCardContainer = cssClassVarianceUtilities.cvaMerge(["contents"]);
|
|
2190
|
+
const cvaOptionCardTitle = cssClassVarianceUtilities.cvaMerge(["text-neutral-600"], {
|
|
1669
2191
|
variants: {
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
"border-4",
|
|
1674
|
-
"border-primary-600",
|
|
1675
|
-
"bg-white",
|
|
1676
|
-
"hover:bg-neutral-100",
|
|
1677
|
-
"hover:cursor-pointer",
|
|
1678
|
-
"outline-0",
|
|
1679
|
-
"active:bg-neutral-200",
|
|
1680
|
-
"active:ring-2",
|
|
1681
|
-
"active:ring-inset",
|
|
1682
|
-
"active:ring-primary-700",
|
|
1683
|
-
"group-active:ring-2",
|
|
1684
|
-
"group-active:ring-inset",
|
|
1685
|
-
"group-active:ring-primary-700",
|
|
1686
|
-
],
|
|
1687
|
-
false: "",
|
|
2192
|
+
layout: {
|
|
2193
|
+
default: ["text-lg", "line-clamp-2"],
|
|
2194
|
+
compact: ["text-sm", "line-clamp-1"],
|
|
1688
2195
|
},
|
|
1689
|
-
|
|
1690
|
-
true: ["
|
|
1691
|
-
false: "",
|
|
2196
|
+
disabled: {
|
|
2197
|
+
true: ["text-neutral-400"],
|
|
2198
|
+
false: ["focus:text-neutral-800", "active:text-neutral-800"],
|
|
2199
|
+
},
|
|
2200
|
+
},
|
|
2201
|
+
});
|
|
2202
|
+
const cvaOptionCardText = cssClassVarianceUtilities.cvaMerge(["text-neutral-600", "text-sm"], {
|
|
2203
|
+
variants: {
|
|
2204
|
+
type: {
|
|
2205
|
+
subheading: ["font-medium"],
|
|
2206
|
+
description: ["font-normal"],
|
|
1692
2207
|
},
|
|
1693
2208
|
disabled: {
|
|
1694
|
-
true: [
|
|
1695
|
-
|
|
1696
|
-
"border-neutral-300",
|
|
1697
|
-
"cursor-not-allowed",
|
|
1698
|
-
"hover:bg-neutral-400",
|
|
1699
|
-
"active:bg-neutral-400",
|
|
1700
|
-
"group-active:ring-0",
|
|
1701
|
-
"group-active:ring-inset",
|
|
1702
|
-
],
|
|
1703
|
-
false: "",
|
|
2209
|
+
true: ["text-neutral-400"],
|
|
2210
|
+
false: ["focus:text-neutral-800", "active:text-neutral-800"],
|
|
1704
2211
|
},
|
|
1705
2212
|
},
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
2213
|
+
});
|
|
2214
|
+
const cvaInput = cssClassVarianceUtilities.cvaMerge(["peer", "absolute", "h-0", "w-0", "opacity-0"]);
|
|
2215
|
+
const cvaCustomImage = cssClassVarianceUtilities.cvaMerge(["text-neutral-400"], {
|
|
2216
|
+
variants: {
|
|
2217
|
+
disabled: {
|
|
2218
|
+
true: ["!text-neutral-400"],
|
|
2219
|
+
false: [""],
|
|
1711
2220
|
},
|
|
1712
|
-
|
|
2221
|
+
},
|
|
1713
2222
|
});
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
2223
|
+
const cvaTag = cssClassVarianceUtilities.cvaMerge([], {
|
|
2224
|
+
variants: {
|
|
2225
|
+
layout: {
|
|
2226
|
+
default: ["absolute", "top-2", "right-2"],
|
|
2227
|
+
compact: [],
|
|
2228
|
+
},
|
|
2229
|
+
},
|
|
2230
|
+
});
|
|
2231
|
+
|
|
2232
|
+
/**
|
|
2233
|
+
* A card version of a radio button that includes an icon, headings and a description.
|
|
2234
|
+
*/
|
|
2235
|
+
const OptionCard = ({ icon, heading, subheading, description, disabled, id, value, className, contentClassName, dataTestId, customImage, layout = "default", ref, tagProps, ...rest }) => {
|
|
2236
|
+
const htmlForId = id ?? "option-card-" + sharedUtils.uuidv4();
|
|
2237
|
+
const subContent = react.useMemo(() => (jsxRuntime.jsxs("div", { className: cvaOptionCardContent({ className: contentClassName }), children: [subheading ? (jsxRuntime.jsx(reactComponents.Text, { align: "center", className: cvaOptionCardText({ type: "subheading", disabled }), type: "span", children: subheading })) : null, description ? (jsxRuntime.jsx(reactComponents.Text, { align: "center", className: cvaOptionCardText({ type: "description", disabled }), type: "span", children: description })) : null] })), [subheading, description, contentClassName, disabled]);
|
|
2238
|
+
return (jsxRuntime.jsx(reactComponents.Tooltip, { className: "w-fit", disabled: layout !== "compact" || (!subheading && !description), label: subContent, mode: "light", placement: "top", children: jsxRuntime.jsxs("div", { className: cvaOptionCardContainer(), "data-testid": dataTestId, children: [jsxRuntime.jsx("input", { className: cvaInput(), "data-testid": `${dataTestId}-option-card`, disabled: disabled, id: htmlForId, ref: ref, type: "radio", value: value, ...rest }), jsxRuntime.jsxs("label", { className: cvaOptionCardLabel({ className, disabled, layout }), "data-testid": `${dataTestId}-option-card-label`, htmlFor: htmlForId, children: [disabled && icon && !customImage
|
|
2239
|
+
? react.cloneElement(icon, { className: cvaCustomImage({ disabled, className: icon.props.className }) })
|
|
2240
|
+
: null, disabled && customImage ? jsxRuntime.jsx("img", { alt: "logo", className: customImage.className, src: customImage.src }) : null, !disabled && !customImage && icon, !disabled && customImage ? jsxRuntime.jsx("img", { alt: "logo", className: customImage.className, src: customImage.src }) : null, heading ? (layout === "default" ? (jsxRuntime.jsx(reactComponents.Heading, { className: cvaOptionCardTitle({ disabled, layout }), subtle: disabled, variant: "secondary", children: heading })) : (jsxRuntime.jsx(reactComponents.Text, { align: "center", className: cvaOptionCardTitle({ disabled, layout }), subtle: disabled, type: "span", weight: "thick", children: heading }))) : null, layout === "default" && (subheading || description) ? subContent : null, tagProps ? jsxRuntime.jsx(reactComponents.Tag, { className: cvaTag({ className: tagProps.className, layout }), ...tagProps }) : null] })] }) }));
|
|
2241
|
+
};
|
|
2242
|
+
OptionCard.displayName = "OptionCard";
|
|
2243
|
+
|
|
2244
|
+
/**
|
|
2245
|
+
* A thin wrapper around the `BaseInput` component for password input fields.
|
|
2246
|
+
*
|
|
2247
|
+
* NOTE: If shown with a label, please use the `PasswordField` component instead.
|
|
2248
|
+
*/
|
|
2249
|
+
const PasswordBaseInput = ({ ref, fieldSize, ...rest }) => {
|
|
2250
|
+
const [showPassword, setShowPassword] = react.useState(false);
|
|
2251
|
+
return (jsxRuntime.jsx(BaseInput, { ref: ref, ...rest, actions: jsxRuntime.jsx("div", { className: cvaActionContainer({ size: fieldSize }), children: jsxRuntime.jsx(reactComponents.IconButton, { className: cvaActionButton({ size: fieldSize }), icon: jsxRuntime.jsx(reactComponents.Icon, { name: showPassword ? "EyeSlash" : "Eye", size: "small" }), onClick: () => setShowPassword(prevState => !prevState), size: "small", variant: "secondary" }) }), type: showPassword ? "text" : "password" }));
|
|
2252
|
+
};
|
|
2253
|
+
|
|
2254
|
+
/**
|
|
2255
|
+
* Password fields enter a password or other confidential information. Characters are masked as they are typed.
|
|
2256
|
+
*
|
|
2257
|
+
* _**Do use** when the user has to input a password or something that needs to be obfuscated_
|
|
2258
|
+
*
|
|
2259
|
+
* _**Do not use** to confirm user actions, such as deleting. Use a checkbox for such flows._
|
|
2260
|
+
*/
|
|
2261
|
+
const PasswordField = ({ id, label, tip, helpText, helpAddon, errorMessage, isInvalid, maxLength, onChange, className, value, dataTestId, ref, ...rest }) => {
|
|
2262
|
+
const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
|
|
2263
|
+
const htmlFor = id ? id : "passwordField-" + sharedUtils.uuidv4();
|
|
2264
|
+
const handleChange = react.useCallback((event) => {
|
|
2265
|
+
onChange?.(event);
|
|
2266
|
+
}, [onChange]);
|
|
2267
|
+
return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlFor, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(PasswordBaseInput, { ...rest, "aria-labelledby": htmlFor + "-label", className: className, dataTestId: dataTestId, disabled: rest.readOnly, id: htmlFor, isInvalid: renderAsInvalid, maxLength: maxLength, onChange: handleChange, ref: ref, value: value }) }));
|
|
2268
|
+
};
|
|
2269
|
+
PasswordField.displayName = "PasswordField";
|
|
2270
|
+
|
|
2271
|
+
/**
|
|
2272
|
+
* Validates a phone number
|
|
2273
|
+
*/
|
|
2274
|
+
const validatePhoneNumber = (phoneNumber) => {
|
|
2275
|
+
if (!phoneNumber) {
|
|
2276
|
+
return "REQUIRED";
|
|
2277
|
+
}
|
|
2278
|
+
const asYouType = new parsePhoneNumberFromString.AsYouType();
|
|
2279
|
+
asYouType.input(phoneNumber);
|
|
2280
|
+
const countryCode = asYouType.getCallingCode();
|
|
2281
|
+
const national = asYouType.getNationalNumber();
|
|
2282
|
+
const safePhoneNumber = getPhoneNumberWithPlus(phoneNumber.trim());
|
|
2283
|
+
const number = parsePhoneNumberFromString(safePhoneNumber);
|
|
2284
|
+
if (phoneNumber && parsePhoneNumberFromString.isValidPhoneNumber(phoneNumber)) {
|
|
2285
|
+
return undefined;
|
|
2286
|
+
}
|
|
2287
|
+
if (!countryCode && national) {
|
|
2288
|
+
return "REQUIRED_COUNTRY";
|
|
2289
|
+
}
|
|
2290
|
+
if (phoneNumber &&
|
|
2291
|
+
(checkIfPhoneNumberHasPlus(phoneNumber)
|
|
2292
|
+
? isNaN(+phoneNumber.slice(1, phoneNumber.length))
|
|
2293
|
+
: isNaN(+phoneNumber) || !number)) {
|
|
2294
|
+
return "NOT_A_NUMBER";
|
|
2295
|
+
}
|
|
2296
|
+
if (safePhoneNumber.length <= 5) {
|
|
2297
|
+
//needs to be handled manually, parsePhoneNumberFromString can't parse it
|
|
2298
|
+
return "TOO_SHORT";
|
|
2299
|
+
}
|
|
2300
|
+
return "INVALID_NUMBER";
|
|
2301
|
+
};
|
|
2302
|
+
/**
|
|
2303
|
+
* Checks if the country code is valid and required
|
|
2304
|
+
*/
|
|
2305
|
+
const isInvalidCountryCode = (error, required) => (!!required && error === "REQUIRED") || error === "REQUIRED_COUNTRY";
|
|
2306
|
+
/**
|
|
2307
|
+
* Checks if the phone number is valid and required
|
|
2308
|
+
*/
|
|
2309
|
+
const isInvalidPhoneNumber = (error, required) => error !== "REQUIRED_COUNTRY" && ((!!error && error !== "REQUIRED") || (!!required && error === "REQUIRED"));
|
|
2310
|
+
/**
|
|
2311
|
+
* Checks if the phone number is valid and returns corresponding error message
|
|
2312
|
+
*/
|
|
2313
|
+
const phoneErrorMessage = (phoneNumber, required) => {
|
|
2314
|
+
if ((validatePhoneNumber(phoneNumber) === "REQUIRED" && !required) ||
|
|
2315
|
+
(validatePhoneNumber(phoneNumber) === "REQUIRED" && required && phoneNumber === undefined)) {
|
|
2316
|
+
return undefined;
|
|
2317
|
+
}
|
|
2318
|
+
return validatePhoneNumber(phoneNumber);
|
|
2319
|
+
};
|
|
2320
|
+
|
|
2321
|
+
/**
|
|
2322
|
+
* The PhoneField component is used to enter phone number.
|
|
2323
|
+
* It is a wrapper around the PhoneInput component and the FormGroup component.
|
|
2324
|
+
* It is used to render a phone number field with a label, a tip, a help text, a help addon and an error message.
|
|
2325
|
+
*
|
|
2326
|
+
* @param {string} [label] - The label for the component.
|
|
2327
|
+
* @param {string} [tip] - The tip for the component.
|
|
2328
|
+
* @param {string} [helpText] - The help text for the component.
|
|
2329
|
+
* @param {string} [helpAddon] - The help addon for the component.
|
|
2330
|
+
* @param {string} [errorMessage] - The error message for the component.
|
|
2331
|
+
* @param {string} [defaultValue] - The default value for the component.
|
|
2332
|
+
* @param {boolean} [disabled=false] - Whether the component is disabled or not.
|
|
2333
|
+
* @param {string} [fieldSize="medium"] - The size of the input field.
|
|
2334
|
+
* @param {boolean} [disableAction=false] - Whether the action button is disabled or not.
|
|
2335
|
+
*/
|
|
2336
|
+
const PhoneField = ({ label, id, tip, helpText, isInvalid, errorMessage, value, helpAddon, className, defaultValue, dataTestId, name, onBlur, ref, ...rest }) => {
|
|
2337
|
+
const htmlForId = id ? id : "phoneField-" + sharedUtils.uuidv4();
|
|
2338
|
+
const [t] = useTranslation();
|
|
2339
|
+
const [innerValue, setInnerValue] = react.useState(() => {
|
|
2340
|
+
return (value?.toString() || defaultValue?.toString()) ?? undefined;
|
|
2341
|
+
});
|
|
2342
|
+
const [renderAsInvalid, setRenderAsInvalid] = react.useState((isInvalid === undefined ? Boolean(errorMessage) : isInvalid) ||
|
|
2343
|
+
!!phoneErrorMessage(value?.toString(), rest.required));
|
|
2344
|
+
const errorType = react.useMemo(() => phoneErrorMessage(innerValue, rest.required), [innerValue, rest.required]);
|
|
2345
|
+
const error = react.useMemo(() => {
|
|
2346
|
+
// for the case when a custom error message is provided
|
|
2347
|
+
if (errorMessage) {
|
|
2348
|
+
return errorMessage;
|
|
2349
|
+
}
|
|
2350
|
+
else if (errorType) {
|
|
2351
|
+
return t(`phoneField.error.${errorType}`);
|
|
2352
|
+
}
|
|
2353
|
+
return errorMessage;
|
|
2354
|
+
}, [errorMessage, errorType, t]);
|
|
2355
|
+
react.useEffect(() => {
|
|
2356
|
+
setRenderAsInvalid(Boolean(errorMessage));
|
|
2357
|
+
}, [errorMessage]);
|
|
2358
|
+
const handleBlur = react.useCallback(event => {
|
|
2359
|
+
const newValue = event.target.value;
|
|
2360
|
+
setInnerValue(newValue);
|
|
2361
|
+
// for the case when a custom error message is provided
|
|
2362
|
+
if (errorMessage && !phoneErrorMessage(newValue.toString(), rest.required)) {
|
|
2363
|
+
setRenderAsInvalid(Boolean(errorMessage));
|
|
2364
|
+
}
|
|
2365
|
+
else {
|
|
2366
|
+
setRenderAsInvalid(!!phoneErrorMessage(newValue.toString(), rest.required));
|
|
2367
|
+
}
|
|
2368
|
+
onBlur?.(event);
|
|
2369
|
+
}, [errorMessage, onBlur, rest.required]);
|
|
2370
|
+
return (jsxRuntime.jsx(FormGroup, { className: className, dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required ? !(rest.disabled || rest.readOnly) : false, tip: tip, children: jsxRuntime.jsx(PhoneBaseInput, { "aria-labelledby": htmlForId + "-label", dataTestId: dataTestId, defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, name: name, onBlur: handleBlur, ref: ref, value: value, ...rest }) }));
|
|
2371
|
+
};
|
|
2372
|
+
PhoneField.displayName = "PhoneField";
|
|
2373
|
+
|
|
2374
|
+
/**
|
|
2375
|
+
* The PhoneFieldWithController component is a wrapper for the PhoneField component to connect it to react-hook-form.
|
|
2376
|
+
*
|
|
2377
|
+
*/
|
|
2378
|
+
const PhoneFieldWithController = ({ control, controllerProps, name, value, ref, ...rest }) => {
|
|
2379
|
+
return (jsxRuntime.jsx(reactHookForm.Controller, { control: control, defaultValue: value, name: name, ...controllerProps, render: ({ field }) => jsxRuntime.jsx(PhoneField, { ...rest, ...field, ref: ref }) }));
|
|
2380
|
+
};
|
|
2381
|
+
PhoneFieldWithController.displayName = "PhoneFieldWithController";
|
|
2382
|
+
|
|
2383
|
+
const cvaRadioGroup = cssClassVarianceUtilities.cvaMerge(["flex", "gap-2", "flex-col", "items-start"], {
|
|
2384
|
+
variants: {
|
|
2385
|
+
layout: {
|
|
2386
|
+
inline: ["flex", "gap-3", "flex-row", "items-center"],
|
|
2387
|
+
},
|
|
2388
|
+
},
|
|
2389
|
+
});
|
|
2390
|
+
const cvaRadioItem = cssClassVarianceUtilities.cvaMerge([
|
|
2391
|
+
"self-center",
|
|
2392
|
+
"w-4",
|
|
2393
|
+
"h-4",
|
|
2394
|
+
"appearance-none",
|
|
2395
|
+
"rounded-3xl",
|
|
2396
|
+
"bg-white",
|
|
2397
|
+
"border-solid",
|
|
2398
|
+
"border",
|
|
2399
|
+
"border-neutral-300",
|
|
2400
|
+
"shadow-sm",
|
|
2401
|
+
"shrink-0",
|
|
2402
|
+
"transition",
|
|
2403
|
+
"box-border",
|
|
2404
|
+
"hover:cursor-pointer",
|
|
2405
|
+
"hover:bg-neutral-100",
|
|
2406
|
+
"focus-visible:outline-primary-700",
|
|
2407
|
+
], {
|
|
2408
|
+
variants: {
|
|
2409
|
+
checked: {
|
|
2410
|
+
true: [
|
|
2411
|
+
"border-solid",
|
|
2412
|
+
"border-4",
|
|
2413
|
+
"border-primary-600",
|
|
2414
|
+
"bg-white",
|
|
2415
|
+
"hover:bg-neutral-100",
|
|
2416
|
+
"hover:cursor-pointer",
|
|
2417
|
+
"outline-0",
|
|
2418
|
+
"active:bg-neutral-200",
|
|
2419
|
+
"active:ring-2",
|
|
2420
|
+
"active:ring-inset",
|
|
2421
|
+
"active:ring-primary-700",
|
|
2422
|
+
"group-active:ring-2",
|
|
2423
|
+
"group-active:ring-inset",
|
|
2424
|
+
"group-active:ring-primary-700",
|
|
2425
|
+
],
|
|
2426
|
+
false: "",
|
|
2427
|
+
},
|
|
2428
|
+
invalid: {
|
|
2429
|
+
true: ["border-red-600", "active:ring-red-700"],
|
|
2430
|
+
false: "",
|
|
2431
|
+
},
|
|
2432
|
+
disabled: {
|
|
2433
|
+
true: [
|
|
2434
|
+
"bg-neutral-400",
|
|
2435
|
+
"border-neutral-300",
|
|
2436
|
+
"cursor-not-allowed",
|
|
2437
|
+
"hover:bg-neutral-400",
|
|
2438
|
+
"active:bg-neutral-400",
|
|
2439
|
+
"group-active:ring-0",
|
|
2440
|
+
"group-active:ring-inset",
|
|
2441
|
+
],
|
|
2442
|
+
false: "",
|
|
2443
|
+
},
|
|
2444
|
+
},
|
|
2445
|
+
compoundVariants: [
|
|
2446
|
+
{
|
|
2447
|
+
checked: true,
|
|
2448
|
+
disabled: true,
|
|
2449
|
+
className: ["bg-white"],
|
|
2450
|
+
},
|
|
2451
|
+
],
|
|
2452
|
+
});
|
|
2453
|
+
|
|
2454
|
+
const RadioGroupContext = react.createContext(null);
|
|
2455
|
+
|
|
2456
|
+
/**
|
|
2457
|
+
* Use radio buttons when you have a group of mutually exclusive choices and only one selection from the group is allowed.
|
|
2458
|
+
*
|
|
2459
|
+
* Radio buttons are used for mutually exclusive choices, not for multiple choices. Only one radio button can be selected at a time. When a user chooses a new item, the previous choice is automatically deselected.
|
|
1721
2460
|
*
|
|
1722
2461
|
* _**Do use** Radio buttons in forms, settings, or selections in a list._
|
|
1723
2462
|
*
|
|
@@ -1922,763 +2661,94 @@ const parseSchedule = (scheduleString) => {
|
|
|
1922
2661
|
return {
|
|
1923
2662
|
variant,
|
|
1924
2663
|
schedule: filteredSchedule,
|
|
1925
|
-
};
|
|
1926
|
-
};
|
|
1927
|
-
/**
|
|
1928
|
-
* Serialize week schedule to string schedule
|
|
1929
|
-
*
|
|
1930
|
-
* @param {WeekSchedule} weekSchedule Week schedule range
|
|
1931
|
-
* @returns {string} Schedule string
|
|
1932
|
-
*/
|
|
1933
|
-
const serializeSchedule = (weekSchedule) => {
|
|
1934
|
-
return weekSchedule.schedule
|
|
1935
|
-
.filter(({ range, day, isAllDay }) => {
|
|
1936
|
-
const hasRange = range.timeFrom && range.timeTo;
|
|
1937
|
-
switch (weekSchedule.variant) {
|
|
1938
|
-
case exports.ScheduleVariant.WEEKDAYS:
|
|
1939
|
-
return day <= 5 && hasRange;
|
|
1940
|
-
case exports.ScheduleVariant.ALL_DAYS:
|
|
1941
|
-
return day <= 7 && hasRange;
|
|
1942
|
-
case exports.ScheduleVariant.CUSTOM:
|
|
1943
|
-
default:
|
|
1944
|
-
return hasRange || isAllDay;
|
|
1945
|
-
}
|
|
1946
|
-
})
|
|
1947
|
-
.map(({ day, range, isAllDay }) => {
|
|
1948
|
-
if (isAllDay) {
|
|
1949
|
-
return `${day}#00:00-24:00`;
|
|
1950
|
-
}
|
|
1951
|
-
return `${day}#${range.timeFrom}-${range.timeTo}`;
|
|
1952
|
-
})
|
|
1953
|
-
.join(",");
|
|
1954
|
-
};
|
|
1955
|
-
/**
|
|
1956
|
-
* Checks if a list of schedule objects have the same ranges
|
|
1957
|
-
*
|
|
1958
|
-
* @param {RawSchedule[]} schedule List of schedule objects
|
|
1959
|
-
* @returns {boolean} Whether the schedule is uniform
|
|
1960
|
-
*/
|
|
1961
|
-
const isUniform = (schedule) => {
|
|
1962
|
-
return schedule.every((day, _, collection) => collection[0]?.range?.timeFrom === day.range?.timeFrom && collection[0]?.range?.timeTo === day.range?.timeTo);
|
|
1963
|
-
};
|
|
1964
|
-
/**
|
|
1965
|
-
* Checks if a list of schedule objects are consecutive days
|
|
1966
|
-
*
|
|
1967
|
-
* @param {RawSchedule[]} schedule List of schedule objects
|
|
1968
|
-
* @returns {boolean} Whether the schedule has consecutive days
|
|
1969
|
-
*/
|
|
1970
|
-
const hasConsecutiveDays = (schedule) => {
|
|
1971
|
-
const days = [1, 2, 3, 4, 5];
|
|
1972
|
-
return schedule.every(({ day }, index) => day === days[index]);
|
|
1973
|
-
};
|
|
1974
|
-
|
|
1975
|
-
const cvaSearch = cssClassVarianceUtilities.cvaMerge([
|
|
1976
|
-
"shadow-none",
|
|
1977
|
-
"component-search-border",
|
|
1978
|
-
"component-search-background",
|
|
1979
|
-
"hover:component-search-background",
|
|
1980
|
-
"hover:component-search-focus-hover",
|
|
1981
|
-
"transition-all",
|
|
1982
|
-
"duration-300",
|
|
1983
|
-
], {
|
|
1984
|
-
variants: {
|
|
1985
|
-
border: { true: ["!component-search-borderless"], false: "" },
|
|
1986
|
-
widenOnFocus: {
|
|
1987
|
-
true: [
|
|
1988
|
-
"component-search-width",
|
|
1989
|
-
"component-search-widen",
|
|
1990
|
-
"hover:component-search-widen",
|
|
1991
|
-
"focus-within:w-full",
|
|
1992
|
-
"max-w-sm",
|
|
1993
|
-
],
|
|
1994
|
-
false: "w-full",
|
|
1995
|
-
},
|
|
1996
|
-
},
|
|
1997
|
-
});
|
|
1998
|
-
|
|
1999
|
-
/**
|
|
2000
|
-
* The Search component is used to render a search input field.
|
|
2001
|
-
*
|
|
2002
|
-
* @param {SearchProps} props - The props for the Search component
|
|
2003
|
-
*/
|
|
2004
|
-
const Search = ({ className, placeholder, value, widenInputOnFocus, hideBorderWhenNotInFocus = false, disabled = false, onKeyUp, onChange, onFocus, onBlur, name, onClear, dataTestId, autoComplete = "on", loading = false, inputClassName, iconName = "MagnifyingGlass", style, xMarkRef, ref, ...rest }) => {
|
|
2005
|
-
const { t } = useTranslation();
|
|
2006
|
-
return (jsxRuntime.jsx(TextBaseInput, { ...rest, autoComplete: autoComplete, className: cvaSearch({ className, border: hideBorderWhenNotInFocus, widenOnFocus: widenInputOnFocus }), dataTestId: dataTestId, disabled: disabled, inputClassName: inputClassName, name: name, onBlur: onBlur, onChange: onChange, onFocus: onFocus, onKeyUp: onKeyUp, placeholder: placeholder ?? t("search.placeholder"), prefix: loading ? (jsxRuntime.jsx(reactComponents.Spinner, { centering: "centered", size: rest.fieldSize ?? undefined })) : (jsxRuntime.jsx(reactComponents.Icon, { name: iconName, size: rest.fieldSize ?? undefined })), ref: ref, suffix:
|
|
2007
|
-
//only show the clear button if there is a value and the onClear function is provided
|
|
2008
|
-
onClear && value ? (jsxRuntime.jsx("button", { className: "flex", "data-testid": dataTestId ? `${dataTestId}_suffix_component` : null, onClick: () => {
|
|
2009
|
-
onClear();
|
|
2010
|
-
}, ref: xMarkRef, type: "button", children: jsxRuntime.jsx(reactComponents.Icon, { name: "XMark", size: "small" }) })) : undefined, value: value }));
|
|
2011
|
-
};
|
|
2012
|
-
Search.displayName = "Search";
|
|
2013
|
-
|
|
2014
|
-
const cvaSelect = cssClassVarianceUtilities.cvaMerge([
|
|
2015
|
-
"relative",
|
|
2016
|
-
"flex",
|
|
2017
|
-
"shadow-sm",
|
|
2018
|
-
"rounded-lg",
|
|
2019
|
-
"border-neutral-300",
|
|
2020
|
-
"hover:border-neutral-400",
|
|
2021
|
-
"hover:bg-neutral-50",
|
|
2022
|
-
"bg-white",
|
|
2023
|
-
"transition",
|
|
2024
|
-
"text-sm",
|
|
2025
|
-
"min-h-0",
|
|
2026
|
-
], {
|
|
2027
|
-
variants: {
|
|
2028
|
-
fieldSize: {
|
|
2029
|
-
small: ["h-7", "text-xs"],
|
|
2030
|
-
medium: ["h-8"],
|
|
2031
|
-
large: ["h-10"],
|
|
2032
|
-
},
|
|
2033
|
-
invalid: {
|
|
2034
|
-
true: "border border-red-600 text-red-600 hover:border-red-600",
|
|
2035
|
-
false: "",
|
|
2036
|
-
},
|
|
2037
|
-
disabled: {
|
|
2038
|
-
true: "!bg-neutral-100 hover:border-neutral-300",
|
|
2039
|
-
false: "",
|
|
2040
|
-
},
|
|
2041
|
-
},
|
|
2042
|
-
defaultVariants: {
|
|
2043
|
-
invalid: false,
|
|
2044
|
-
disabled: false,
|
|
2045
|
-
},
|
|
2046
|
-
});
|
|
2047
|
-
const cvaSelectControl = cssClassVarianceUtilities.cvaMerge([], {
|
|
2048
|
-
variants: {
|
|
2049
|
-
isDisabled: {
|
|
2050
|
-
true: "!bg-neutral-100",
|
|
2051
|
-
false: "",
|
|
2052
|
-
},
|
|
2053
|
-
prefix: {
|
|
2054
|
-
true: ["ps-7"],
|
|
2055
|
-
false: "",
|
|
2056
|
-
},
|
|
2057
|
-
invalid: {
|
|
2058
|
-
true: "!border-0",
|
|
2059
|
-
false: "",
|
|
2060
|
-
},
|
|
2061
|
-
},
|
|
2062
|
-
defaultVariants: {
|
|
2063
|
-
isDisabled: false,
|
|
2064
|
-
prefix: false,
|
|
2065
|
-
invalid: false,
|
|
2066
|
-
},
|
|
2067
|
-
});
|
|
2068
|
-
const cvaSelectIcon = cssClassVarianceUtilities.cvaMerge([
|
|
2069
|
-
"mr-2",
|
|
2070
|
-
"flex",
|
|
2071
|
-
"cursor-pointer",
|
|
2072
|
-
"items-center",
|
|
2073
|
-
"justify-center",
|
|
2074
|
-
"text-neutral-400",
|
|
2075
|
-
"hover:text-neutral-500",
|
|
2076
|
-
]);
|
|
2077
|
-
const cvaSelectPrefixSuffix = cssClassVarianceUtilities.cvaMerge(["flex", "justify-center", "items-center", "text-neutral-400", "absolute", "inset-y-0"], {
|
|
2078
|
-
variants: {
|
|
2079
|
-
kind: {
|
|
2080
|
-
prefix: ["pl-3", "left-0"],
|
|
2081
|
-
suffix: ["pr-3", "right-0"],
|
|
2082
|
-
},
|
|
2083
|
-
},
|
|
2084
|
-
});
|
|
2085
|
-
const cvaSelectXIcon = cssClassVarianceUtilities.cvaMerge([
|
|
2086
|
-
"mr-2",
|
|
2087
|
-
"flex",
|
|
2088
|
-
"cursor-pointer",
|
|
2089
|
-
"items-center",
|
|
2090
|
-
"justify-center",
|
|
2091
|
-
"text-neutral-400",
|
|
2092
|
-
"hover:text-neutral-500",
|
|
2093
|
-
"ml-1",
|
|
2094
|
-
]);
|
|
2095
|
-
const cvaSelectMenuList = cssClassVarianceUtilities.cvaMerge([], {
|
|
2096
|
-
variants: {
|
|
2097
|
-
menuIsOpen: {
|
|
2098
|
-
true: "animate-fade-in-fast",
|
|
2099
|
-
false: "animate-fade-out-fast",
|
|
2100
|
-
},
|
|
2101
|
-
},
|
|
2102
|
-
});
|
|
2103
|
-
const cvaSelectDynamicTagContainer = cssClassVarianceUtilities.cvaMerge(["h-full", "flex", "gap-1", "items-center"], {
|
|
2104
|
-
variants: {
|
|
2105
|
-
visible: { true: "visible", false: "invisible" },
|
|
2106
|
-
},
|
|
2107
|
-
});
|
|
2108
|
-
const cvaSelectCounter = cssClassVarianceUtilities.cvaMerge(["overflow-hidden", "whitespace-nowrap"]);
|
|
2109
|
-
const cvaSelectMenu = cssClassVarianceUtilities.cvaMerge(["relative", "p-1", "grid", "gap-1"]);
|
|
2110
|
-
|
|
2111
|
-
/**
|
|
2112
|
-
* A single select menu item is a basic wrapper around Menu item designed to be used as a single value render in Select list
|
|
2113
|
-
*
|
|
2114
|
-
* @param {SelectMenuItemProps} props - The props for the SingleSelectMenuItem
|
|
2115
|
-
* @returns {ReactElement} SingleSelectMenuItem
|
|
2116
|
-
*/
|
|
2117
|
-
const SingleSelectMenuItem = ({ label, icon, onClick, selected, focused, dataTestId, disabled, optionLabelDescription, optionPrefix, fieldSize, }) => {
|
|
2118
|
-
return (jsxRuntime.jsx(reactComponents.MenuItem, { dataTestId: dataTestId, disabled: disabled, fieldSize: fieldSize, focused: focused, label: label, onClick: onClick, optionLabelDescription: optionLabelDescription, optionPrefix: react.isValidElement(optionPrefix)
|
|
2119
|
-
? react.cloneElement(optionPrefix, {
|
|
2120
|
-
className: "mr-1 flex items-center",
|
|
2121
|
-
size: "medium",
|
|
2122
|
-
})
|
|
2123
|
-
: optionPrefix, prefix: icon, selected: selected, suffix: selected ? jsxRuntime.jsx(reactComponents.Icon, { className: "block text-blue-600", name: "Check", size: "medium" }) : undefined }));
|
|
2124
|
-
};
|
|
2125
|
-
/**
|
|
2126
|
-
* A multi select menu item is a basic wrapper around Menu item designed to be used as a multi value render in Select list
|
|
2127
|
-
*
|
|
2128
|
-
* @param {SelectMenuItemProps} props - The props for the MultiSelectMenuItem
|
|
2129
|
-
* @returns {ReactElement} multi select menu item
|
|
2130
|
-
*/
|
|
2131
|
-
const MultiSelectMenuItem = ({ label, onClick, selected, focused, dataTestId, disabled, optionLabelDescription, optionPrefix, fieldSize, }) => {
|
|
2132
|
-
return (jsxRuntime.jsx(reactComponents.MenuItem, { dataTestId: dataTestId, disabled: disabled, fieldSize: fieldSize, focused: focused, label: label, onClick: e => {
|
|
2133
|
-
e.stopPropagation();
|
|
2134
|
-
onClick && onClick(e);
|
|
2135
|
-
}, optionLabelDescription: optionLabelDescription, optionPrefix: react.isValidElement(optionPrefix)
|
|
2136
|
-
? react.cloneElement(optionPrefix, {
|
|
2137
|
-
className: "mr-1 flex items-center",
|
|
2138
|
-
size: "medium",
|
|
2139
|
-
})
|
|
2140
|
-
: optionPrefix, prefix: jsxRuntime.jsx(Checkbox, { checked: selected, className: "gap-x-0", disabled: disabled, onChange: () => null, onClick: e => {
|
|
2141
|
-
e.stopPropagation();
|
|
2142
|
-
}, readOnly: false }), selected: selected }));
|
|
2143
|
-
};
|
|
2144
|
-
|
|
2145
|
-
/**
|
|
2146
|
-
* Extended Tag component with information about its own width.
|
|
2147
|
-
* Used in the select component.
|
|
2148
|
-
*
|
|
2149
|
-
* @param {TagProps} props - The props for the tag component
|
|
2150
|
-
* @returns {ReactElement} TagWithWidth component
|
|
2151
|
-
*/
|
|
2152
|
-
const TagWithWidth = ({ onWidthKnown, children, ...rest }) => {
|
|
2153
|
-
const ref = react.useRef(null);
|
|
2154
|
-
react.useLayoutEffect(() => {
|
|
2155
|
-
onWidthKnown && onWidthKnown({ width: ref.current?.offsetWidth || 0 });
|
|
2156
|
-
}, [ref, onWidthKnown]);
|
|
2157
|
-
return (jsxRuntime.jsx(reactComponents.Tag, { ref: ref, ...rest, icon: react.isValidElement(rest.icon) ? react.cloneElement(rest.icon, { size: "small" }) : rest.icon, children: children }));
|
|
2158
|
-
};
|
|
2159
|
-
|
|
2160
|
-
/**
|
|
2161
|
-
* TagsContainer component to display tags in limited space when children can't fit space it displays counter
|
|
2162
|
-
*
|
|
2163
|
-
* @param {TagsContainerProps} props - The props for the TagContainer
|
|
2164
|
-
* @returns {ReactElement} TagsContainer
|
|
2165
|
-
*/
|
|
2166
|
-
const TagsContainer = ({ items, width = "100%", itemsGap = 6, postFix, preFix, disabled, }) => {
|
|
2167
|
-
const containerRef = react.useRef(null);
|
|
2168
|
-
const [isReady, setIsReady] = react.useState(false);
|
|
2169
|
-
const [counterWidth, setCounterWidth] = react.useState(0);
|
|
2170
|
-
const [availableSpaceWidth, setAvailableSpaceWidth] = react.useState(0);
|
|
2171
|
-
const [childrenWidths, setChildrenWidths] = react.useState([]);
|
|
2172
|
-
const itemsCount = items.length;
|
|
2173
|
-
const dimensions = reactComponents.useResize();
|
|
2174
|
-
const { width: windowWidth } = reactComponents.useDebounce(dimensions, 100);
|
|
2175
|
-
react.useEffect(() => {
|
|
2176
|
-
const containerWidth = containerRef.current?.offsetWidth || 0;
|
|
2177
|
-
setAvailableSpaceWidth(containerWidth);
|
|
2178
|
-
}, [windowWidth]);
|
|
2179
|
-
const onWidthKnownHandler = react.useCallback(({ width: reportedWidth }) => {
|
|
2180
|
-
setChildrenWidths(prev => {
|
|
2181
|
-
const next = [...prev, { width: reportedWidth + itemsGap }];
|
|
2182
|
-
if (next.length === itemsCount) {
|
|
2183
|
-
setIsReady(true);
|
|
2184
|
-
}
|
|
2185
|
-
return next;
|
|
2186
|
-
});
|
|
2187
|
-
}, [itemsCount, itemsGap]);
|
|
2188
|
-
const renderedElements = react.useMemo(() => {
|
|
2189
|
-
const requiredSpace = childrenWidths.reduce((previous, current) => {
|
|
2190
|
-
return previous + current.width;
|
|
2191
|
-
}, 0);
|
|
2192
|
-
const availableSpace = availableSpaceWidth - counterWidth;
|
|
2193
|
-
const { elements } = items
|
|
2194
|
-
.concat({ text: "", onClick: () => null, disabled: false })
|
|
2195
|
-
.reduce((acc, item, index) => {
|
|
2196
|
-
const spaceNeeded = childrenWidths.slice(0, index + 1).reduce((previous, current) => {
|
|
2197
|
-
return previous + current.width;
|
|
2198
|
-
}, 0);
|
|
2199
|
-
const isLast = index === items.length;
|
|
2200
|
-
const counterRequired = requiredSpace > availableSpace && acc.counter !== 0;
|
|
2201
|
-
if (isLast && counterRequired) {
|
|
2202
|
-
return {
|
|
2203
|
-
...acc,
|
|
2204
|
-
elements: [
|
|
2205
|
-
...acc.elements,
|
|
2206
|
-
jsxRuntime.jsx(TagWithWidth, { color: "white", disabled: disabled, icon: item.Icon, onWidthKnown: ({ width: reportedWidth }) => setCounterWidth(reportedWidth), children: jsxRuntime.jsxs("div", { className: cvaSelectCounter(), "data-testid": "select-counter", children: ["+", acc.counter] }) }, item.text + index),
|
|
2207
|
-
],
|
|
2208
|
-
};
|
|
2209
|
-
}
|
|
2210
|
-
if (isLast) {
|
|
2211
|
-
return acc;
|
|
2212
|
-
}
|
|
2213
|
-
const itemCanFit = spaceNeeded <= availableSpace;
|
|
2214
|
-
if (itemCanFit) {
|
|
2215
|
-
return {
|
|
2216
|
-
...acc,
|
|
2217
|
-
elements: [
|
|
2218
|
-
...acc.elements,
|
|
2219
|
-
jsxRuntime.jsx(TagWithWidth, { className: "inline-flex shrink-0", color: item.disabled ? "neutral" : "white", dataTestId: `${item.text}-tag`, disabled: disabled, icon: item.Icon, onClose: e => {
|
|
2220
|
-
e.stopPropagation();
|
|
2221
|
-
item.onClick();
|
|
2222
|
-
}, onWidthKnown: onWidthKnownHandler, children: item.text }, item.text + index),
|
|
2223
|
-
],
|
|
2224
|
-
};
|
|
2225
|
-
}
|
|
2226
|
-
return {
|
|
2227
|
-
elements: acc.elements,
|
|
2228
|
-
counter: item.text !== "" ? acc.counter + 1 : acc.counter,
|
|
2229
|
-
};
|
|
2230
|
-
}, { elements: [], counter: 0 });
|
|
2231
|
-
return elements;
|
|
2232
|
-
}, [items, availableSpaceWidth, counterWidth, disabled, onWidthKnownHandler, childrenWidths]);
|
|
2233
|
-
return (jsxRuntime.jsxs("div", { className: cvaSelectDynamicTagContainer({ visible: isReady || !!preFix }), ref: containerRef, style: {
|
|
2234
|
-
width: `${width}`,
|
|
2235
|
-
}, children: [preFix, renderedElements, postFix] }));
|
|
2236
|
-
};
|
|
2237
|
-
|
|
2238
|
-
/**
|
|
2239
|
-
* A hook to retrieve components override object.
|
|
2240
|
-
* 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.
|
|
2241
|
-
*
|
|
2242
|
-
* @template IsMulti
|
|
2243
|
-
* @template Group
|
|
2244
|
-
* @param {Partial<SelectComponents<Option, IsMulti, Group>> | undefined} componentsProps a custom component prop that you can to override defaults
|
|
2245
|
-
* @param {boolean} disabled decide to override disabled variant
|
|
2246
|
-
* @param {boolean} menuIsOpen menu is open state
|
|
2247
|
-
* @param {string} dataTestId a test id
|
|
2248
|
-
* @param {number} maxSelectedDisplayCount a number of max display count
|
|
2249
|
-
* @param {boolean} hasError decide to override hasError variant
|
|
2250
|
-
* @param {ReactNode} prefix a prefix element
|
|
2251
|
-
* @returns {Partial<SelectComponents<Option, boolean, GroupBase<Option>>> | undefined} components object to override react-select default components
|
|
2252
|
-
*/
|
|
2253
|
-
const useCustomComponents = ({ componentsProps, disabled, readOnly, setMenuIsEnabled, dataTestId, maxSelectedDisplayCount, prefix, hasError, fieldSize, getOptionLabelDescription, getOptionPrefix, }) => {
|
|
2254
|
-
const [t] = useTranslation();
|
|
2255
|
-
// perhaps it should not be wrap in memo (causing some issues with opening and closing on mobiles)
|
|
2256
|
-
const customComponents = react.useMemo(() => {
|
|
2257
|
-
return {
|
|
2258
|
-
ValueContainer: props => {
|
|
2259
|
-
if (props.isMulti && Array.isArray(props.children) && props.children.length > 0) {
|
|
2260
|
-
const PLACEHOLDER_KEY = "placeholder";
|
|
2261
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
2262
|
-
const key = props && props.children && props.children[0] ? props.children[0]?.key : "";
|
|
2263
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
2264
|
-
const values = props && props.children ? props.children[0] : [];
|
|
2265
|
-
const tags = key === PLACEHOLDER_KEY ? [] : values;
|
|
2266
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
2267
|
-
const searchInput = props && props.children && props.children[1];
|
|
2268
|
-
const placeholderElement = Array.isArray(props.children)
|
|
2269
|
-
? props.children.find(child => child && child.key === PLACEHOLDER_KEY)
|
|
2270
|
-
: null;
|
|
2271
|
-
return (jsxRuntime.jsx(ReactSelect.components.ValueContainer, { ...props, isDisabled: props.selectProps.isDisabled, children: maxSelectedDisplayCount === undefined ? (jsxRuntime.jsx(TagsContainer, { disabled: disabled, items: tags
|
|
2272
|
-
? tags.map(({ props: tagProps }) => {
|
|
2273
|
-
const optionPrefix = tagProps.data && getOptionPrefix ? getOptionPrefix(tagProps.data) : null;
|
|
2274
|
-
return {
|
|
2275
|
-
text: tagProps.children,
|
|
2276
|
-
onClick: disabled
|
|
2277
|
-
? undefined
|
|
2278
|
-
: (e) => {
|
|
2279
|
-
setMenuIsEnabled(false);
|
|
2280
|
-
tagProps.removeProps.onClick && tagProps.removeProps.onClick(e);
|
|
2281
|
-
},
|
|
2282
|
-
disabled: disabled,
|
|
2283
|
-
Icon: optionPrefix,
|
|
2284
|
-
};
|
|
2285
|
-
})
|
|
2286
|
-
: [], postFix: searchInput, preFix: placeholderElement ? jsxRuntime.jsx("span", { className: "absolute", children: placeholderElement }) : null, width: "100%" })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [tags
|
|
2287
|
-
? tags.slice(0, maxSelectedDisplayCount).map(({ props: tagProps }) => {
|
|
2288
|
-
return (jsxRuntime.jsx(reactComponents.Tag, { className: "inline-flex shrink-0", color: disabled ? "unknown" : "primary", dataTestId: tagProps.children ? `${tagProps.children.toString()}-tag` : undefined, onClose: e => {
|
|
2289
|
-
e.stopPropagation();
|
|
2290
|
-
setMenuIsEnabled(false);
|
|
2291
|
-
tagProps.removeProps.onClick && tagProps.removeProps.onClick(e);
|
|
2292
|
-
}, children: tagProps.children }, tagProps.children?.toString()));
|
|
2293
|
-
})
|
|
2294
|
-
: null, tags && tags.length > maxSelectedDisplayCount ? (jsxRuntime.jsxs(reactComponents.Tag, { color: "neutral", dataTestId: "counter-tag", children: ["+", tags.length - maxSelectedDisplayCount] })) : null, searchInput, placeholderElement] })) }));
|
|
2295
|
-
}
|
|
2296
|
-
return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: jsxRuntime.jsx(ReactSelect.components.ValueContainer, { ...props, isDisabled: props.selectProps.isDisabled, children: props.children }) }));
|
|
2297
|
-
},
|
|
2298
|
-
LoadingIndicator: () => {
|
|
2299
|
-
return jsxRuntime.jsx(reactComponents.Spinner, { centering: "vertically", className: "mr-2", size: "small" });
|
|
2300
|
-
},
|
|
2301
|
-
DropdownIndicator: props => {
|
|
2302
|
-
const icon = props.selectProps.menuIsOpen ? (jsxRuntime.jsx(reactComponents.Icon, { name: "ChevronUp", size: "medium" })) : (jsxRuntime.jsx(reactComponents.Icon, { name: "ChevronDown", size: "medium" }));
|
|
2303
|
-
return props.selectProps.isLoading || props.selectProps.isDisabled || readOnly ? null : (jsxRuntime.jsx(ReactSelect.components.DropdownIndicator, { ...props, children: jsxRuntime.jsx("div", { className: cvaSelectIcon(), children: icon }) }));
|
|
2304
|
-
},
|
|
2305
|
-
IndicatorSeparator: () => null,
|
|
2306
|
-
ClearIndicator: props => {
|
|
2307
|
-
if (disabled) {
|
|
2308
|
-
return null;
|
|
2309
|
-
}
|
|
2310
|
-
return (jsxRuntime.jsx(ReactSelect.components.ClearIndicator, { ...props, innerProps: {
|
|
2311
|
-
...props.innerProps,
|
|
2312
|
-
onMouseDown: e => {
|
|
2313
|
-
e.preventDefault();
|
|
2314
|
-
},
|
|
2315
|
-
}, children: jsxRuntime.jsx("div", { className: cvaSelectXIcon(), "data-testid": dataTestId ? `${dataTestId}-XMarkIcon` : null, onClick: props.clearValue, children: jsxRuntime.jsx(reactComponents.Icon, { ariaLabel: t("clearIndicator.icon.tooltip.clearAll"), name: "XCircle", size: "medium" }) }) }));
|
|
2316
|
-
},
|
|
2317
|
-
Control: props => {
|
|
2318
|
-
return (jsxRuntime.jsx(ReactSelect.components.Control, { ...props, className: cvaSelectControl({
|
|
2319
|
-
isDisabled: props.isDisabled,
|
|
2320
|
-
prefix: prefix ? true : false,
|
|
2321
|
-
invalid: hasError,
|
|
2322
|
-
}) }));
|
|
2323
|
-
},
|
|
2324
|
-
SingleValue: props => {
|
|
2325
|
-
const optionPrefix = getOptionPrefix ? getOptionPrefix(props.data) : null;
|
|
2326
|
-
return (jsxRuntime.jsx(ReactSelect.components.SingleValue, { ...props, className: props.isDisabled ? "text-neutral-700" : "", children: jsxRuntime.jsxs("div", { className: "flex items-center gap-1", "data-testid": dataTestId + "-singleValue", children: [optionPrefix !== null ? optionPrefix : null, props.children, getOptionLabelDescription && getOptionLabelDescription(props.data) ? (jsxRuntime.jsxs("span", { className: "ml-1 text-neutral-400", children: ["(", getOptionLabelDescription(props.data), ")"] })) : null] }) }));
|
|
2327
|
-
},
|
|
2328
|
-
Menu: props => {
|
|
2329
|
-
return (jsxRuntime.jsx(ReactSelect.components.Menu, { ...props, className: cvaSelectMenuList({ menuIsOpen: props.selectProps.menuIsOpen }) }));
|
|
2330
|
-
},
|
|
2331
|
-
Placeholder: props => {
|
|
2332
|
-
return (jsxRuntime.jsx(ReactSelect.components.Placeholder, { ...props, className: "!text-neutral-400", children: props.children }));
|
|
2333
|
-
},
|
|
2334
|
-
MenuList: props => {
|
|
2335
|
-
return (jsxRuntime.jsx(ReactSelect.components.MenuList, { ...props, innerProps: {
|
|
2336
|
-
...props.innerProps,
|
|
2337
|
-
onScroll: e => {
|
|
2338
|
-
const listEl = e.currentTarget;
|
|
2339
|
-
if (listEl.scrollTop + listEl.clientHeight >= listEl.scrollHeight &&
|
|
2340
|
-
props.selectProps.onMenuScrollToBottom) {
|
|
2341
|
-
/Firefox/.test(navigator.userAgent)
|
|
2342
|
-
? props.selectProps.onMenuScrollToBottom(new WheelEvent("scroll"))
|
|
2343
|
-
: props.selectProps.onMenuScrollToBottom(new TouchEvent(""));
|
|
2344
|
-
}
|
|
2345
|
-
},
|
|
2346
|
-
}, children: props.children }));
|
|
2347
|
-
},
|
|
2348
|
-
Option: props => {
|
|
2349
|
-
const componentProps = {
|
|
2350
|
-
label: props.label,
|
|
2351
|
-
focused: props.isFocused,
|
|
2352
|
-
selected: props.isSelected,
|
|
2353
|
-
onClick: props.innerProps.onClick,
|
|
2354
|
-
};
|
|
2355
|
-
return (jsxRuntime.jsx(ReactSelect.components.Option, { ...props, innerProps: {
|
|
2356
|
-
...props.innerProps,
|
|
2357
|
-
role: "option",
|
|
2358
|
-
onClick: () => { },
|
|
2359
|
-
}, children: props.isMulti ? (jsxRuntime.jsx(MultiSelectMenuItem, { ...componentProps, dataTestId: typeof props.label === "string" ? props.label : undefined, disabled: disabled, fieldSize: fieldSize, optionLabelDescription: getOptionLabelDescription?.(props.data), optionPrefix: getOptionPrefix?.(props.data) })) : (jsxRuntime.jsx(SingleSelectMenuItem, { ...componentProps, dataTestId: typeof props.label === "string" ? props.label : undefined, disabled: disabled || props.isDisabled, fieldSize: fieldSize, optionLabelDescription: getOptionLabelDescription?.(props.data), optionPrefix: getOptionPrefix?.(props.data) })) }));
|
|
2360
|
-
},
|
|
2361
|
-
...componentsProps,
|
|
2362
|
-
};
|
|
2363
|
-
}, [
|
|
2364
|
-
componentsProps,
|
|
2365
|
-
maxSelectedDisplayCount,
|
|
2366
|
-
disabled,
|
|
2367
|
-
setMenuIsEnabled,
|
|
2368
|
-
readOnly,
|
|
2369
|
-
dataTestId,
|
|
2370
|
-
t,
|
|
2371
|
-
prefix,
|
|
2372
|
-
hasError,
|
|
2373
|
-
getOptionLabelDescription,
|
|
2374
|
-
fieldSize,
|
|
2375
|
-
getOptionPrefix,
|
|
2376
|
-
]);
|
|
2377
|
-
return customComponents;
|
|
2378
|
-
};
|
|
2379
|
-
|
|
2380
|
-
/**
|
|
2381
|
-
* @template IsMulti
|
|
2382
|
-
* @template Group
|
|
2383
|
-
* @param {RefObject<HTMLDivElement | null>} refContainer react ref to container element
|
|
2384
|
-
* @param {number | undefined} maxSelectedDisplayCount a number of max display count
|
|
2385
|
-
* @param {StylesConfig<Option, IsMulti, Group> | undefined} styles a optional object to override styles of react-select
|
|
2386
|
-
* @returns {StylesConfig<Option, boolean>} styles to override in select
|
|
2387
|
-
*/
|
|
2388
|
-
const useCustomStyles = ({ refContainer, maxSelectedDisplayCount, styles, disabled, fieldSize, }) => {
|
|
2389
|
-
const customStyles = react.useMemo(() => {
|
|
2390
|
-
return {
|
|
2391
|
-
control: base => {
|
|
2392
|
-
return {
|
|
2393
|
-
...base,
|
|
2394
|
-
minHeight: fieldSize === "small" ? "28px" : fieldSize === "large" ? "40px" : "32px",
|
|
2395
|
-
borderRadius: "var(--border-radius-lg)",
|
|
2396
|
-
backgroundColor: "inherit",
|
|
2397
|
-
};
|
|
2398
|
-
},
|
|
2399
|
-
singleValue: base => ({
|
|
2400
|
-
...base,
|
|
2401
|
-
}),
|
|
2402
|
-
multiValue: base => ({
|
|
2403
|
-
...base,
|
|
2404
|
-
}),
|
|
2405
|
-
multiValueLabel: base => ({
|
|
2406
|
-
...base,
|
|
2407
|
-
}),
|
|
2408
|
-
indicatorsContainer: base => ({
|
|
2409
|
-
...base,
|
|
2410
|
-
...(disabled && { display: "none" }),
|
|
2411
|
-
}),
|
|
2412
|
-
indicatorSeparator: () => ({
|
|
2413
|
-
width: "0px",
|
|
2414
|
-
}),
|
|
2415
|
-
menu: base => {
|
|
2416
|
-
return {
|
|
2417
|
-
...base,
|
|
2418
|
-
width: "100%",
|
|
2419
|
-
marginTop: "4px",
|
|
2420
|
-
marginBottom: "18px",
|
|
2421
|
-
transition: "all 1s ease-in-out",
|
|
2422
|
-
};
|
|
2423
|
-
},
|
|
2424
|
-
input: base => ({
|
|
2425
|
-
...base,
|
|
2426
|
-
marginLeft: "0px",
|
|
2427
|
-
}),
|
|
2428
|
-
placeholder: base => ({
|
|
2429
|
-
...base,
|
|
2430
|
-
}),
|
|
2431
|
-
option: () => ({}),
|
|
2432
|
-
menuPortal: base => ({
|
|
2433
|
-
...base,
|
|
2434
|
-
width: refContainer.current ? `${refContainer.current.clientWidth}px` : base.width,
|
|
2435
|
-
backgroundColor: "#ffffff",
|
|
2436
|
-
borderRadius: "var(--border-radius-lg)",
|
|
2437
|
-
zIndex: "var(--z-overlay)",
|
|
2438
|
-
borderColor: "rgb(var(--color-neutral-300))",
|
|
2439
|
-
boxShadow: "var(--tw-ring-inset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)",
|
|
2440
|
-
}),
|
|
2441
|
-
menuList: base => {
|
|
2442
|
-
return {
|
|
2443
|
-
...base,
|
|
2444
|
-
position: "relative",
|
|
2445
|
-
padding: "var(--spacing-1)",
|
|
2446
|
-
display: "grid",
|
|
2447
|
-
gap: "var(--spacing-1)",
|
|
2448
|
-
width: "100%",
|
|
2449
|
-
borderRadius: "0px",
|
|
2450
|
-
boxShadow: "none",
|
|
2451
|
-
paddingTop: "0px",
|
|
2452
|
-
};
|
|
2453
|
-
},
|
|
2454
|
-
valueContainer: base => {
|
|
2455
|
-
return {
|
|
2456
|
-
...base,
|
|
2457
|
-
paddingBlock: 0,
|
|
2458
|
-
flexWrap: maxSelectedDisplayCount !== undefined ? "wrap" : "nowrap",
|
|
2459
|
-
gap: "0.25rem",
|
|
2460
|
-
};
|
|
2461
|
-
},
|
|
2462
|
-
container: base => ({
|
|
2463
|
-
...base,
|
|
2464
|
-
width: "100%",
|
|
2465
|
-
}),
|
|
2466
|
-
dropdownIndicator: base => ({
|
|
2467
|
-
...base,
|
|
2468
|
-
padding: "0px",
|
|
2469
|
-
}),
|
|
2470
|
-
clearIndicator: base => {
|
|
2471
|
-
return {
|
|
2472
|
-
...base,
|
|
2473
|
-
padding: "0px",
|
|
2474
|
-
};
|
|
2475
|
-
},
|
|
2476
|
-
...styles,
|
|
2477
|
-
};
|
|
2478
|
-
}, [refContainer, disabled, fieldSize, maxSelectedDisplayCount, styles]);
|
|
2479
|
-
return { customStyles };
|
|
2664
|
+
};
|
|
2480
2665
|
};
|
|
2481
|
-
|
|
2482
2666
|
/**
|
|
2483
|
-
*
|
|
2667
|
+
* Serialize week schedule to string schedule
|
|
2484
2668
|
*
|
|
2485
|
-
* @param {
|
|
2486
|
-
* @returns {
|
|
2669
|
+
* @param {WeekSchedule} weekSchedule Week schedule range
|
|
2670
|
+
* @returns {string} Schedule string
|
|
2487
2671
|
*/
|
|
2488
|
-
const
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
componentsProps: components,
|
|
2501
|
-
disabled: Boolean(disabled),
|
|
2502
|
-
readOnly: Boolean(props.readOnly),
|
|
2503
|
-
setMenuIsEnabled,
|
|
2504
|
-
dataTestId,
|
|
2505
|
-
maxSelectedDisplayCount,
|
|
2506
|
-
prefix,
|
|
2507
|
-
hasError,
|
|
2508
|
-
fieldSize,
|
|
2509
|
-
getOptionLabelDescription,
|
|
2510
|
-
getOptionPrefix,
|
|
2511
|
-
});
|
|
2512
|
-
const menuPlacement = "auto";
|
|
2513
|
-
const openMenuHandler = async () => {
|
|
2514
|
-
onMenuOpen?.();
|
|
2515
|
-
if (menuIsEnabled) {
|
|
2516
|
-
setMenuIsOpen(true);
|
|
2672
|
+
const serializeSchedule = (weekSchedule) => {
|
|
2673
|
+
return weekSchedule.schedule
|
|
2674
|
+
.filter(({ range, day, isAllDay }) => {
|
|
2675
|
+
const hasRange = range.timeFrom && range.timeTo;
|
|
2676
|
+
switch (weekSchedule.variant) {
|
|
2677
|
+
case exports.ScheduleVariant.WEEKDAYS:
|
|
2678
|
+
return day <= 5 && hasRange;
|
|
2679
|
+
case exports.ScheduleVariant.ALL_DAYS:
|
|
2680
|
+
return day <= 7 && hasRange;
|
|
2681
|
+
case exports.ScheduleVariant.CUSTOM:
|
|
2682
|
+
default:
|
|
2683
|
+
return hasRange || isAllDay;
|
|
2517
2684
|
}
|
|
2518
|
-
|
|
2519
|
-
|
|
2685
|
+
})
|
|
2686
|
+
.map(({ day, range, isAllDay }) => {
|
|
2687
|
+
if (isAllDay) {
|
|
2688
|
+
return `${day}#00:00-24:00`;
|
|
2520
2689
|
}
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
onMenuClose && onMenuClose();
|
|
2525
|
-
};
|
|
2526
|
-
return {
|
|
2527
|
-
refContainer,
|
|
2528
|
-
customStyles,
|
|
2529
|
-
menuIsOpen,
|
|
2530
|
-
customComponents,
|
|
2531
|
-
menuPlacement,
|
|
2532
|
-
openMenuHandler,
|
|
2533
|
-
closeMenuHandler,
|
|
2534
|
-
};
|
|
2690
|
+
return `${day}#${range.timeFrom}-${range.timeTo}`;
|
|
2691
|
+
})
|
|
2692
|
+
.join(",");
|
|
2535
2693
|
};
|
|
2536
|
-
|
|
2537
2694
|
/**
|
|
2538
|
-
*
|
|
2695
|
+
* Checks if a list of schedule objects have the same ranges
|
|
2539
2696
|
*
|
|
2540
|
-
* @param {
|
|
2541
|
-
* @returns {
|
|
2697
|
+
* @param {RawSchedule[]} schedule List of schedule objects
|
|
2698
|
+
* @returns {boolean} Whether the schedule is uniform
|
|
2542
2699
|
*/
|
|
2543
|
-
const
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
tabSelectsValue: false,
|
|
2556
|
-
blurInputOnSelect: !isMulti,
|
|
2557
|
-
menuPortalTarget: props.menuPortalTarget || document.body,
|
|
2558
|
-
isSearchable: disabled || readOnly ? false : isSearchable,
|
|
2559
|
-
menuShouldBlockScroll: true,
|
|
2560
|
-
menuShouldScrollIntoView: true,
|
|
2561
|
-
openMenuOnFocus,
|
|
2562
|
-
menuIsOpen: !readOnly ? menuIsOpen : false,
|
|
2563
|
-
openMenuOnClick,
|
|
2564
|
-
closeMenuOnSelect: false,
|
|
2565
|
-
isMulti,
|
|
2566
|
-
classNamePrefix,
|
|
2567
|
-
isLoading,
|
|
2568
|
-
isClearable,
|
|
2569
|
-
id,
|
|
2570
|
-
onMenuScrollToBottom,
|
|
2571
|
-
onInputChange,
|
|
2572
|
-
allowCreateWhileLoading,
|
|
2573
|
-
onCreateOption,
|
|
2574
|
-
isDisabled: Boolean(disabled),
|
|
2575
|
-
}), [
|
|
2576
|
-
allowCreateWhileLoading,
|
|
2577
|
-
classNamePrefix,
|
|
2578
|
-
customComponents,
|
|
2579
|
-
customStyles,
|
|
2580
|
-
dataTestId,
|
|
2581
|
-
disabled,
|
|
2582
|
-
id,
|
|
2583
|
-
isClearable,
|
|
2584
|
-
isLoading,
|
|
2585
|
-
isMulti,
|
|
2586
|
-
isSearchable,
|
|
2587
|
-
label,
|
|
2588
|
-
maxMenuHeight,
|
|
2589
|
-
menuIsOpen,
|
|
2590
|
-
menuPlacement,
|
|
2591
|
-
onChange,
|
|
2592
|
-
onCreateOption,
|
|
2593
|
-
onInputChange,
|
|
2594
|
-
onMenuScrollToBottom,
|
|
2595
|
-
openMenuOnClick,
|
|
2596
|
-
openMenuOnFocus,
|
|
2597
|
-
props.menuPortalTarget,
|
|
2598
|
-
readOnly,
|
|
2599
|
-
value,
|
|
2600
|
-
]);
|
|
2601
|
-
const renderAsDisabled = Boolean(props.disabled) || props.readOnly;
|
|
2602
|
-
return (jsxRuntime.jsxs("div", { className: cvaSelect({ invalid: hasError, disabled: renderAsDisabled, className: props.className }), "data-testid": dataTestId, ref: refContainer, children: [prefix !== undefined ? (jsxRuntime.jsx("div", { className: cvaSelectPrefixSuffix({ kind: "prefix" }), "data-testid": dataTestId ? `${dataTestId}-prefix` : null, children: prefix })) : null, async ? (jsxRuntime.jsx(ReactAsyncCreatableSelect, { ...props, ...reactCreatableSelectProps, ...async, onMenuClose: closeMenuHandler, onMenuOpen: openMenuHandler, placeholder: renderAsDisabled ? null : props.placeholder })) : (jsxRuntime.jsx(ReactCreatableSelect, { ...props, ...reactCreatableSelectProps, hideSelectedOptions: false, isMulti: isMulti, onMenuClose: closeMenuHandler, onMenuOpen: openMenuHandler, options: options, placeholder: renderAsDisabled ? null : props.placeholder })), typeof props.disabled === "object" ? (jsxRuntime.jsx("div", { className: cvaSelectPrefixSuffix({ kind: "suffix" }), "data-testid": dataTestId ? `${dataTestId}-locked` : null, children: jsxRuntime.jsx(InputLockReasonTooltip, { ...props.disabled }) })) : null] }));
|
|
2700
|
+
const isUniform = (schedule) => {
|
|
2701
|
+
return schedule.every((day, _, collection) => collection[0]?.range?.timeFrom === day.range?.timeFrom && collection[0]?.range?.timeTo === day.range?.timeTo);
|
|
2702
|
+
};
|
|
2703
|
+
/**
|
|
2704
|
+
* Checks if a list of schedule objects are consecutive days
|
|
2705
|
+
*
|
|
2706
|
+
* @param {RawSchedule[]} schedule List of schedule objects
|
|
2707
|
+
* @returns {boolean} Whether the schedule has consecutive days
|
|
2708
|
+
*/
|
|
2709
|
+
const hasConsecutiveDays = (schedule) => {
|
|
2710
|
+
const days = [1, 2, 3, 4, 5];
|
|
2711
|
+
return schedule.every(({ day }, index) => day === days[index]);
|
|
2603
2712
|
};
|
|
2604
|
-
CreatableSelect.displayName = "CreatableSelect";
|
|
2605
2713
|
|
|
2606
|
-
|
|
2607
|
-
|
|
2714
|
+
const cvaSearch = cssClassVarianceUtilities.cvaMerge([
|
|
2715
|
+
"shadow-none",
|
|
2716
|
+
"component-search-border",
|
|
2717
|
+
"component-search-background",
|
|
2718
|
+
"hover:component-search-background",
|
|
2719
|
+
"hover:component-search-focus-hover",
|
|
2720
|
+
"transition-all",
|
|
2721
|
+
"duration-300",
|
|
2722
|
+
], {
|
|
2723
|
+
variants: {
|
|
2724
|
+
border: { true: ["!component-search-borderless"], false: "" },
|
|
2725
|
+
widenOnFocus: {
|
|
2726
|
+
true: [
|
|
2727
|
+
"component-search-width",
|
|
2728
|
+
"component-search-widen",
|
|
2729
|
+
"hover:component-search-widen",
|
|
2730
|
+
"focus-within:w-full",
|
|
2731
|
+
"max-w-sm",
|
|
2732
|
+
],
|
|
2733
|
+
false: "w-full",
|
|
2734
|
+
},
|
|
2735
|
+
},
|
|
2736
|
+
});
|
|
2737
|
+
|
|
2608
2738
|
/**
|
|
2609
|
-
*
|
|
2739
|
+
* The Search component is used to render a search input field.
|
|
2610
2740
|
*
|
|
2611
|
-
* @param {
|
|
2612
|
-
* @returns {ReactElement} Select component
|
|
2741
|
+
* @param {SearchProps} props - The props for the Search component
|
|
2613
2742
|
*/
|
|
2614
|
-
const
|
|
2615
|
-
const {
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
value,
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
onChange,
|
|
2622
|
-
"aria-label": label,
|
|
2623
|
-
"data-testid": dataTestId,
|
|
2624
|
-
components: customComponents,
|
|
2625
|
-
styles: customStyles,
|
|
2626
|
-
tabSelectsValue: false,
|
|
2627
|
-
blurInputOnSelect: false,
|
|
2628
|
-
// This configuration allows for more flexible positioning control of the dropdown.
|
|
2629
|
-
// Setting menuPortalTarget to 'null' specifies that the dropdown should be rendered within
|
|
2630
|
-
// the parent element instead of 'document.body'.
|
|
2631
|
-
menuPortalTarget: props.menuPortalTarget !== undefined ? props.menuPortalTarget : document.body,
|
|
2632
|
-
isSearchable: disabled || readOnly ? false : isSearchable,
|
|
2633
|
-
menuShouldBlockScroll: true,
|
|
2634
|
-
menuShouldScrollIntoView: true,
|
|
2635
|
-
openMenuOnFocus,
|
|
2636
|
-
menuIsOpen: !readOnly ? menuIsOpen : false,
|
|
2637
|
-
openMenuOnClick,
|
|
2638
|
-
closeMenuOnSelect: !isMulti,
|
|
2639
|
-
isMulti,
|
|
2640
|
-
classNamePrefix,
|
|
2641
|
-
isLoading,
|
|
2642
|
-
isClearable,
|
|
2643
|
-
id,
|
|
2644
|
-
onMenuScrollToBottom,
|
|
2645
|
-
onInputChange,
|
|
2646
|
-
hideSelectedOptions,
|
|
2647
|
-
isDisabled: Boolean(disabled),
|
|
2648
|
-
}), [
|
|
2649
|
-
classNamePrefix,
|
|
2650
|
-
customComponents,
|
|
2651
|
-
customStyles,
|
|
2652
|
-
dataTestId,
|
|
2653
|
-
disabled,
|
|
2654
|
-
hideSelectedOptions,
|
|
2655
|
-
id,
|
|
2656
|
-
isClearable,
|
|
2657
|
-
isLoading,
|
|
2658
|
-
isMulti,
|
|
2659
|
-
isSearchable,
|
|
2660
|
-
label,
|
|
2661
|
-
maxMenuHeight,
|
|
2662
|
-
menuIsOpen,
|
|
2663
|
-
menuPlacement,
|
|
2664
|
-
onChange,
|
|
2665
|
-
onInputChange,
|
|
2666
|
-
onMenuScrollToBottom,
|
|
2667
|
-
openMenuOnClick,
|
|
2668
|
-
openMenuOnFocus,
|
|
2669
|
-
props.menuPortalTarget,
|
|
2670
|
-
readOnly,
|
|
2671
|
-
value,
|
|
2672
|
-
]);
|
|
2673
|
-
const renderAsDisabled = Boolean(props.disabled) || props.readOnly;
|
|
2674
|
-
return (jsxRuntime.jsxs("div", { className: cvaSelect({
|
|
2675
|
-
invalid: hasError,
|
|
2676
|
-
fieldSize: fieldSize,
|
|
2677
|
-
disabled: renderAsDisabled,
|
|
2678
|
-
className: props.className,
|
|
2679
|
-
}), "data-testid": dataTestId, ref: refContainer, children: [prefix !== undefined ? (jsxRuntime.jsx("div", { className: cvaSelectPrefixSuffix({ kind: "prefix" }), "data-testid": dataTestId ? `${dataTestId}-prefix` : null, children: prefix })) : null, async ? (jsxRuntime.jsx(ReactAsyncSelect, { ...props, ...reactSelectProps, ...async, menuPosition: menuPosition, onMenuClose: closeMenuHandler, onMenuOpen: openMenuHandler, placeholder: renderAsDisabled ? null : props.placeholder })) : (jsxRuntime.jsx(ReactSyncSelect, { ...props, ...reactSelectProps, isMulti: isMulti, menuPosition: menuPosition, onMenuClose: closeMenuHandler, onMenuOpen: openMenuHandler, options: options, placeholder: renderAsDisabled ? null : props.placeholder })), typeof props.disabled === "object" ? (jsxRuntime.jsx("div", { className: cvaSelectPrefixSuffix({ kind: "suffix" }), "data-testid": dataTestId ? `${dataTestId}-locked` : null, children: jsxRuntime.jsx(InputLockReasonTooltip, { ...props.disabled }) })) : null] }));
|
|
2743
|
+
const Search = ({ className, placeholder, value, widenInputOnFocus, hideBorderWhenNotInFocus = false, disabled = false, onKeyUp, onChange, onFocus, onBlur, name, onClear, dataTestId, autoComplete = "on", loading = false, inputClassName, iconName = "MagnifyingGlass", style, xMarkRef, ref, ...rest }) => {
|
|
2744
|
+
const { t } = useTranslation();
|
|
2745
|
+
return (jsxRuntime.jsx(TextBaseInput, { ...rest, autoComplete: autoComplete, className: cvaSearch({ className, border: hideBorderWhenNotInFocus, widenOnFocus: widenInputOnFocus }), dataTestId: dataTestId, disabled: disabled, inputClassName: inputClassName, name: name, onBlur: onBlur, onChange: onChange, onFocus: onFocus, onKeyUp: onKeyUp, placeholder: placeholder ?? t("search.placeholder"), prefix: loading ? (jsxRuntime.jsx(reactComponents.Spinner, { centering: "centered", size: rest.fieldSize ?? undefined })) : (jsxRuntime.jsx(reactComponents.Icon, { name: iconName, size: rest.fieldSize ?? undefined })), ref: ref, suffix:
|
|
2746
|
+
//only show the clear button if there is a value and the onClear function is provided
|
|
2747
|
+
onClear && value ? (jsxRuntime.jsx("button", { className: "flex", "data-testid": dataTestId ? `${dataTestId}_suffix_component` : null, onClick: () => {
|
|
2748
|
+
onClear();
|
|
2749
|
+
}, ref: xMarkRef, type: "button", children: jsxRuntime.jsx(reactComponents.Icon, { name: "XMark", size: "small" }) })) : undefined, value: value }));
|
|
2680
2750
|
};
|
|
2681
|
-
|
|
2751
|
+
Search.displayName = "Search";
|
|
2682
2752
|
|
|
2683
2753
|
/**
|
|
2684
2754
|
*
|
|
@@ -2765,7 +2835,7 @@ CreatableSelectField.displayName = "CreatableSelectField";
|
|
|
2765
2835
|
* @param {SelectFieldProps} props - The props for the SelectField component
|
|
2766
2836
|
*/
|
|
2767
2837
|
const SelectField = ({ ref, ...props }) => {
|
|
2768
|
-
return (jsxRuntime.jsx(FormFieldSelectAdapter, { ...props, ref: ref, children: convertedProps => jsxRuntime.jsx(
|
|
2838
|
+
return (jsxRuntime.jsx(FormFieldSelectAdapter, { ...props, ref: ref, children: convertedProps => jsxRuntime.jsx(BaseSelect, { ...convertedProps }) }));
|
|
2769
2839
|
};
|
|
2770
2840
|
SelectField.displayName = "SelectField";
|
|
2771
2841
|
|
|
@@ -3181,6 +3251,7 @@ setupLibraryTranslations();
|
|
|
3181
3251
|
exports.ValueType = ReactSelect;
|
|
3182
3252
|
exports.ActionButton = ActionButton;
|
|
3183
3253
|
exports.BaseInput = BaseInput;
|
|
3254
|
+
exports.BaseSelect = BaseSelect;
|
|
3184
3255
|
exports.Checkbox = Checkbox;
|
|
3185
3256
|
exports.CheckboxField = CheckboxField;
|
|
3186
3257
|
exports.ColorField = ColorField;
|
|
@@ -3196,6 +3267,7 @@ exports.EmailField = EmailField;
|
|
|
3196
3267
|
exports.FormFieldSelectAdapter = FormFieldSelectAdapter;
|
|
3197
3268
|
exports.FormGroup = FormGroup;
|
|
3198
3269
|
exports.Label = Label;
|
|
3270
|
+
exports.MultiSelectField = MultiSelectField;
|
|
3199
3271
|
exports.MultiSelectMenuItem = MultiSelectMenuItem;
|
|
3200
3272
|
exports.NumberBaseInput = NumberBaseInput;
|
|
3201
3273
|
exports.NumberField = NumberField;
|
|
@@ -3209,7 +3281,6 @@ exports.RadioGroup = RadioGroup;
|
|
|
3209
3281
|
exports.RadioItem = RadioItem;
|
|
3210
3282
|
exports.Schedule = Schedule;
|
|
3211
3283
|
exports.Search = Search;
|
|
3212
|
-
exports.Select = Select;
|
|
3213
3284
|
exports.SelectField = SelectField;
|
|
3214
3285
|
exports.SingleSelectMenuItem = SingleSelectMenuItem;
|
|
3215
3286
|
exports.TextAreaBaseInput = TextAreaBaseInput;
|
|
@@ -3233,7 +3304,9 @@ exports.cvaInputAddon = cvaInputAddon;
|
|
|
3233
3304
|
exports.cvaInputBase = cvaInputBase;
|
|
3234
3305
|
exports.cvaInputBaseDisabled = cvaInputBaseDisabled;
|
|
3235
3306
|
exports.cvaInputBaseInvalid = cvaInputBaseInvalid;
|
|
3236
|
-
exports.
|
|
3307
|
+
exports.cvaInputBaseReadOnly = cvaInputBaseReadOnly;
|
|
3308
|
+
exports.cvaInputBaseSize = cvaInputBaseSize;
|
|
3309
|
+
exports.cvaInputElement = cvaInputElement;
|
|
3237
3310
|
exports.cvaInputItemPlacementManager = cvaInputItemPlacementManager;
|
|
3238
3311
|
exports.cvaInputPrefix = cvaInputPrefix;
|
|
3239
3312
|
exports.cvaInputSuffix = cvaInputSuffix;
|