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