@luscii-healthtech/web-ui 1.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Accordion/Accordion.d.ts +10 -0
- package/dist/components/Accordion/AccordionItem.d.ts +9 -0
- package/dist/components/Form/Form.d.ts +9 -0
- package/dist/components/Form/FormFieldDecorator.d.ts +8 -0
- package/dist/components/Form/FormInput.d.ts +3 -0
- package/dist/components/Form/FormRadioGroup.d.ts +3 -0
- package/dist/components/Form/FormSelect.d.ts +3 -0
- package/dist/components/Form/form.transformer.d.ts +20 -0
- package/dist/components/Form/form.types.d.ts +54 -0
- package/dist/components/Icons/types/IconProps.type.d.ts +2 -1
- package/dist/components/Input/Input.d.ts +8 -7
- package/dist/components/Input/SearchInput.d.ts +1 -1
- package/dist/components/Radio/Radio.d.ts +3 -0
- package/dist/components/Radio/RadioV2.d.ts +17 -0
- package/dist/components/RadioGroup/RadioGroup.d.ts +3 -0
- package/dist/components/RadioGroup/RadioGroupV2.d.ts +9 -0
- package/dist/components/Select/Select.d.ts +3 -0
- package/dist/components/Select/SelectV2.d.ts +31 -0
- package/dist/index.d.ts +2 -0
- package/dist/web-ui-tailwind.css +34 -0
- package/dist/web-ui.cjs.development.js +523 -20
- package/dist/web-ui.cjs.development.js.map +1 -1
- package/dist/web-ui.cjs.production.min.js +1 -1
- package/dist/web-ui.cjs.production.min.js.map +1 -1
- package/dist/web-ui.esm.js +523 -21
- package/dist/web-ui.esm.js.map +1 -1
- package/package.json +6 -3
- package/src/components/Accordion/Accordion.tsx +33 -0
- package/src/components/Accordion/AccordionItem.tsx +50 -0
- package/src/components/Checkbox/Checkbox.tsx +1 -0
- package/src/components/CheckboxList/CheckboxGroup.tsx +8 -2
- package/src/components/CheckboxList/CheckboxListItem.tsx +4 -1
- package/src/components/Form/Form.tsx +106 -0
- package/src/components/Form/FormFieldDecorator.tsx +66 -0
- package/src/components/Form/FormInput.tsx +47 -0
- package/src/components/Form/FormRadioGroup.tsx +23 -0
- package/src/components/Form/FormSelect.tsx +32 -0
- package/src/components/Form/form.transformer.ts +9 -0
- package/src/components/Form/form.types.ts +132 -0
- package/src/components/Icons/types/IconProps.type.ts +1 -1
- package/src/components/Input/Input.tsx +160 -165
- package/src/components/Input/SearchInput.tsx +13 -3
- package/src/components/Radio/Radio.js +3 -0
- package/src/components/Radio/RadioV2.css +15 -0
- package/src/components/Radio/RadioV2.tsx +87 -0
- package/src/components/RadioGroup/RadioGroup.js +3 -0
- package/src/components/RadioGroup/RadioGroupV2.tsx +35 -0
- package/src/components/Select/Select.tsx +38 -12
- package/src/components/Select/SelectV2.tsx +171 -0
- package/src/index.tsx +3 -0
- package/src/types/general.types.ts +1 -1
- package/src/components/Select/Select.examples.md +0 -161
|
@@ -1,24 +1,15 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
HTMLInputTypeAttribute,
|
|
3
|
-
useEffect,
|
|
4
|
-
useRef,
|
|
5
|
-
useState,
|
|
6
|
-
} from "react";
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
7
2
|
import classNames from "classnames";
|
|
8
3
|
|
|
9
4
|
import { CrossIcon } from "../Icons/CrossIcon";
|
|
5
|
+
import { AllowedTextInputTypes } from "../Form/form.types";
|
|
10
6
|
import { IconProps } from "../Icons/types/IconProps.type";
|
|
11
7
|
|
|
12
8
|
import "./Input.css";
|
|
13
9
|
|
|
14
|
-
type AllowedInputTypes = Extract<
|
|
15
|
-
HTMLInputTypeAttribute,
|
|
16
|
-
"email" | "number" | "password" | "text"
|
|
17
|
-
>;
|
|
18
|
-
|
|
19
10
|
// FIXME: something in the tsdx.js build doesn't allow me to use `Uppercase<AllowedInputTypes>`
|
|
20
11
|
// Don't know why yet but it can be fixed later.
|
|
21
|
-
export const INPUT_TYPES: Record<string,
|
|
12
|
+
export const INPUT_TYPES: Record<string, AllowedTextInputTypes> = {
|
|
22
13
|
EMAIL: "email",
|
|
23
14
|
NUMBER: "number",
|
|
24
15
|
PASSWORD: "password",
|
|
@@ -29,7 +20,10 @@ export const INPUT_TYPES: Record<string, AllowedInputTypes> = {
|
|
|
29
20
|
* It's very complicated to tap into the onChange events of inputs,
|
|
30
21
|
* so I omit the type it requires and add a easier one to deal with.
|
|
31
22
|
*/
|
|
32
|
-
type CustomHTMLInputProps = Omit<
|
|
23
|
+
type CustomHTMLInputProps = Omit<
|
|
24
|
+
React.HTMLProps<HTMLInputElement>,
|
|
25
|
+
"onChange"
|
|
26
|
+
> & { isError?: boolean };
|
|
33
27
|
|
|
34
28
|
type FakeEventTarget = {
|
|
35
29
|
target: {
|
|
@@ -43,174 +37,175 @@ type FakeEventTarget = {
|
|
|
43
37
|
};
|
|
44
38
|
|
|
45
39
|
export interface InputProps extends CustomHTMLInputProps {
|
|
46
|
-
type?:
|
|
40
|
+
type?: AllowedTextInputTypes;
|
|
47
41
|
clearable?: boolean;
|
|
48
42
|
isDisabled?: boolean;
|
|
49
43
|
withPrefix?: string;
|
|
50
44
|
withSuffix?: string;
|
|
51
45
|
icon?: React.VoidFunctionComponent<IconProps>;
|
|
52
46
|
onChange?: (event: FakeEventTarget) => void;
|
|
53
|
-
// FIXME: this is used in the SearchAndFilterBar in Vitals, but we can use forwardRef instead
|
|
54
|
-
// and lose this function later.
|
|
55
|
-
setRef?: (refTarget: HTMLInputElement) => void;
|
|
56
47
|
}
|
|
57
48
|
|
|
58
|
-
const Input
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
setInnerValue(value);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
49
|
+
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
50
|
+
(
|
|
51
|
+
{
|
|
52
|
+
withSuffix = "",
|
|
53
|
+
withPrefix = "",
|
|
54
|
+
className,
|
|
55
|
+
clearable = false,
|
|
56
|
+
type = "text",
|
|
57
|
+
isDisabled = false,
|
|
58
|
+
icon,
|
|
59
|
+
name,
|
|
60
|
+
value = "",
|
|
61
|
+
onChange,
|
|
62
|
+
isError,
|
|
63
|
+
...otherProps
|
|
64
|
+
},
|
|
65
|
+
ref
|
|
66
|
+
) => {
|
|
67
|
+
const hasNoExtraContent = withPrefix === "" && withSuffix === "";
|
|
68
|
+
const [innerValue, setInnerValue] = useState(value);
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
setInnerValue(value);
|
|
72
|
+
}, [value]);
|
|
73
|
+
|
|
74
|
+
const clearField = () => {
|
|
75
|
+
setInnerValue("");
|
|
76
|
+
|
|
77
|
+
// This allows backwards compatibility with our usages of the input
|
|
78
|
+
onChange?.({
|
|
79
|
+
target: {
|
|
80
|
+
name: name || "",
|
|
81
|
+
value: "",
|
|
82
|
+
},
|
|
83
|
+
currentTarget: {
|
|
84
|
+
name: name || "",
|
|
85
|
+
value: "",
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const handleChangeEvent = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
91
|
+
setInnerValue(event.currentTarget.value);
|
|
92
|
+
|
|
93
|
+
// This allows backwards compatibility with our usages of the input
|
|
94
|
+
onChange?.({
|
|
95
|
+
target: {
|
|
96
|
+
name: name || "",
|
|
97
|
+
value: event.currentTarget.value,
|
|
98
|
+
},
|
|
99
|
+
currentTarget: {
|
|
100
|
+
name: name || "",
|
|
101
|
+
value: event.currentTarget.value,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const isClearIconVisible = clearable && value;
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<div
|
|
110
|
+
data-test-id="input-component-container"
|
|
111
|
+
className="relative inline-block"
|
|
112
|
+
>
|
|
113
|
+
<div className="flex flex-row">
|
|
114
|
+
<span
|
|
115
|
+
className={classNames(
|
|
116
|
+
"h-11 border-t border-b border-solid border-input-border",
|
|
117
|
+
{
|
|
118
|
+
hidden: hasNoExtraContent,
|
|
119
|
+
"order-1": withPrefix !== "",
|
|
120
|
+
"order-2": withSuffix !== "",
|
|
121
|
+
"rounded-l border-l": withPrefix !== "",
|
|
122
|
+
"rounded-r border-r": withSuffix !== "",
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
"flex flex-col items-center p-3",
|
|
126
|
+
"text-sm",
|
|
127
|
+
"text-slate-500",
|
|
128
|
+
"bg-main-background"
|
|
129
|
+
)}
|
|
130
|
+
>
|
|
131
|
+
{withSuffix || withPrefix}
|
|
132
|
+
</span>
|
|
133
|
+
{icon && (
|
|
134
|
+
<div
|
|
135
|
+
className="absolute top-1/2 left-4"
|
|
136
|
+
style={{
|
|
137
|
+
transform: "translateY(-50%)",
|
|
138
|
+
}}
|
|
139
|
+
>
|
|
140
|
+
{React.createElement(icon, {
|
|
141
|
+
className: "w-6 h-6 text-slate-300 ",
|
|
142
|
+
})}
|
|
143
|
+
</div>
|
|
135
144
|
)}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
145
|
+
<input
|
|
146
|
+
{...otherProps}
|
|
147
|
+
data-test-id="input-component"
|
|
148
|
+
name={name}
|
|
149
|
+
value={innerValue}
|
|
150
|
+
onChange={handleChangeEvent}
|
|
151
|
+
ref={ref}
|
|
152
|
+
size={otherProps.maxLength}
|
|
153
|
+
type={type}
|
|
154
|
+
disabled={isDisabled}
|
|
155
|
+
className={classNames(
|
|
156
|
+
"input",
|
|
157
|
+
"block",
|
|
158
|
+
{ "pl-12": icon, "pr-11": clearable },
|
|
159
|
+
{
|
|
160
|
+
"text-slate-400 bg-slate-50": isDisabled,
|
|
161
|
+
"text-slate-700": !isDisabled,
|
|
162
|
+
"border-input-border": !isDisabled && !isError,
|
|
163
|
+
"hover:border-input-border-dark": !isDisabled,
|
|
164
|
+
"border-red-700": isError,
|
|
165
|
+
"focus:outline-negative": isError,
|
|
166
|
+
"focus:border-blue-800": !isError,
|
|
167
|
+
"focus:outline-primary": !isError,
|
|
168
|
+
"bg-white": !isDisabled,
|
|
169
|
+
},
|
|
170
|
+
"h-11",
|
|
171
|
+
"p-2",
|
|
172
|
+
"border",
|
|
173
|
+
"text-sm",
|
|
174
|
+
"placeholder-slate-500",
|
|
175
|
+
"border-solid",
|
|
176
|
+
|
|
177
|
+
"transition-colors",
|
|
178
|
+
"duration-300",
|
|
179
|
+
{
|
|
180
|
+
"z-10": withSuffix !== "" || withPrefix !== "", // to make sure the outline is displayed completely
|
|
181
|
+
rounded: hasNoExtraContent,
|
|
182
|
+
"rounded-l": withSuffix !== "",
|
|
183
|
+
"rounded-r": withPrefix !== "",
|
|
184
|
+
"order-2": withPrefix !== "",
|
|
185
|
+
"order-1": withSuffix !== "",
|
|
186
|
+
},
|
|
187
|
+
"shadow-default",
|
|
188
|
+
className
|
|
189
|
+
)}
|
|
190
|
+
/>
|
|
140
191
|
<div
|
|
141
|
-
className="absolute top-1/2
|
|
192
|
+
className="absolute right-0 top-1/2"
|
|
142
193
|
style={{
|
|
143
194
|
transform: "translateY(-50%)",
|
|
144
195
|
}}
|
|
145
196
|
>
|
|
146
|
-
|
|
147
|
-
className
|
|
148
|
-
|
|
197
|
+
<CrossIcon
|
|
198
|
+
className={classNames(
|
|
199
|
+
"w-6 h-6 mr-3 cursor-pointer text-slate-300",
|
|
200
|
+
{ block: isClearIconVisible, hidden: !isClearIconVisible }
|
|
201
|
+
)}
|
|
202
|
+
onClick={clearField}
|
|
203
|
+
/>
|
|
149
204
|
</div>
|
|
150
|
-
)}
|
|
151
|
-
<input
|
|
152
|
-
{...otherProps}
|
|
153
|
-
data-test-id="input-component"
|
|
154
|
-
name={name}
|
|
155
|
-
value={innerValue}
|
|
156
|
-
onChange={handleChangeEvent}
|
|
157
|
-
ref={element => {
|
|
158
|
-
inputRef.current = element;
|
|
159
|
-
element && setRef?.(element);
|
|
160
|
-
}}
|
|
161
|
-
size={otherProps.maxLength}
|
|
162
|
-
type={type}
|
|
163
|
-
disabled={isDisabled}
|
|
164
|
-
className={classNames(
|
|
165
|
-
"input",
|
|
166
|
-
"block",
|
|
167
|
-
{ "pl-12": icon, "pr-11": clearable },
|
|
168
|
-
{
|
|
169
|
-
"text-slate-400 bg-slate-50": isDisabled,
|
|
170
|
-
"text-slate-700": !isDisabled,
|
|
171
|
-
"border-input-border": !isDisabled,
|
|
172
|
-
"hover:border-input-border-dark": !isDisabled,
|
|
173
|
-
"bg-white": !isDisabled,
|
|
174
|
-
},
|
|
175
|
-
"h-11",
|
|
176
|
-
"p-2",
|
|
177
|
-
"border",
|
|
178
|
-
"text-sm",
|
|
179
|
-
"placeholder-slate-500",
|
|
180
|
-
"border-solid",
|
|
181
|
-
"focus:outline-primary",
|
|
182
|
-
"transition-colors",
|
|
183
|
-
"duration-300",
|
|
184
|
-
"focus:border-blue-800",
|
|
185
|
-
{
|
|
186
|
-
"z-10": withSuffix !== "" || withPrefix !== "", // to make sure the outline is displayed completely
|
|
187
|
-
rounded: hasNoExtraContent,
|
|
188
|
-
"rounded-l": withSuffix !== "",
|
|
189
|
-
"rounded-r": withPrefix !== "",
|
|
190
|
-
"order-2": withPrefix !== "",
|
|
191
|
-
"order-1": withSuffix !== "",
|
|
192
|
-
},
|
|
193
|
-
"shadow-default",
|
|
194
|
-
className
|
|
195
|
-
)}
|
|
196
|
-
/>
|
|
197
|
-
<div
|
|
198
|
-
className="absolute right-0 top-1/2"
|
|
199
|
-
style={{
|
|
200
|
-
transform: "translateY(-50%)",
|
|
201
|
-
}}
|
|
202
|
-
>
|
|
203
|
-
<CrossIcon
|
|
204
|
-
className={classNames(
|
|
205
|
-
"w-6 h-6 mr-3 cursor-pointer text-slate-300",
|
|
206
|
-
{ block: isClearIconVisible, hidden: !isClearIconVisible }
|
|
207
|
-
)}
|
|
208
|
-
onClick={clearField}
|
|
209
|
-
/>
|
|
210
205
|
</div>
|
|
211
206
|
</div>
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
);
|
|
215
210
|
|
|
216
211
|
export default Input;
|
|
@@ -9,6 +9,16 @@ export type SearchInputProps = Omit<
|
|
|
9
9
|
"icon" | "withSuffix" | "withPrefix" | "type" | "clearable"
|
|
10
10
|
>;
|
|
11
11
|
|
|
12
|
-
export const SearchInput
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
export const SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(
|
|
13
|
+
(props, ref) => {
|
|
14
|
+
return (
|
|
15
|
+
<Input
|
|
16
|
+
{...props}
|
|
17
|
+
icon={SearchIcon}
|
|
18
|
+
type="text"
|
|
19
|
+
clearable={true}
|
|
20
|
+
ref={ref}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
.radio-form-field-label input[type="radio"]:checked + .radio-circle {
|
|
2
|
+
@apply bg-primary;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.radio-form-field-label[data-has-error="true"] .radio-circle {
|
|
6
|
+
@apply border-red-700;
|
|
7
|
+
@apply outline-negative;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.radio-form-field-label
|
|
11
|
+
input[type="radio"]:checked
|
|
12
|
+
+ .radio-circle
|
|
13
|
+
.radio-inner-circle {
|
|
14
|
+
@apply bg-white;
|
|
15
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import classNames from "classnames";
|
|
3
|
+
|
|
4
|
+
import Text from "../Text/Text";
|
|
5
|
+
|
|
6
|
+
import "./RadioV2.css";
|
|
7
|
+
|
|
8
|
+
export interface RadioProps
|
|
9
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
10
|
+
name: string;
|
|
11
|
+
value: string | number;
|
|
12
|
+
// text shown to the user to explain the option
|
|
13
|
+
text?: string;
|
|
14
|
+
// text shown to the user underneath the text for extra information
|
|
15
|
+
info?: string;
|
|
16
|
+
isError?: boolean;
|
|
17
|
+
innerRef?: React.Ref<HTMLInputElement>;
|
|
18
|
+
// value field is used by react-hook-form as the value returned
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function RadioInner({
|
|
22
|
+
text,
|
|
23
|
+
info,
|
|
24
|
+
isError,
|
|
25
|
+
innerRef,
|
|
26
|
+
className,
|
|
27
|
+
...otherProps
|
|
28
|
+
}: RadioProps): JSX.Element {
|
|
29
|
+
const { value, disabled } = otherProps;
|
|
30
|
+
const nameHtmlFor = `field-${value}`;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<label
|
|
34
|
+
className="radio-form-field-label leading-tight"
|
|
35
|
+
htmlFor={nameHtmlFor}
|
|
36
|
+
data-has-error={isError}
|
|
37
|
+
data-test-id={nameHtmlFor}
|
|
38
|
+
>
|
|
39
|
+
<div className="flex flex-row items-center ">
|
|
40
|
+
<input
|
|
41
|
+
{...otherProps}
|
|
42
|
+
className={classNames("appearance-none", className)}
|
|
43
|
+
ref={innerRef}
|
|
44
|
+
type="radio"
|
|
45
|
+
id={nameHtmlFor}
|
|
46
|
+
disabled={disabled}
|
|
47
|
+
/>
|
|
48
|
+
<span
|
|
49
|
+
className={classNames(
|
|
50
|
+
"flex flex-col items-center justify-center w-4 h-4 transition-colors duration-300 ease-in-out border radio-circle rounded-xl border-slate-300"
|
|
51
|
+
)}
|
|
52
|
+
>
|
|
53
|
+
<span className="block transition-colors duration-300 ease-in-out radio-inner-circle w-1.5 h-1.5 rounded-xl"></span>
|
|
54
|
+
</span>
|
|
55
|
+
{text && (
|
|
56
|
+
<div className="ml-2">
|
|
57
|
+
<Text
|
|
58
|
+
inline={true}
|
|
59
|
+
text={text}
|
|
60
|
+
type="base"
|
|
61
|
+
color={disabled ? "slate-500" : undefined}
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
</div>
|
|
66
|
+
{info && (
|
|
67
|
+
<Text
|
|
68
|
+
inline={true}
|
|
69
|
+
className="ml-6"
|
|
70
|
+
text={info}
|
|
71
|
+
type="sm"
|
|
72
|
+
color={disabled ? "slate-200" : "slate-500"}
|
|
73
|
+
/>
|
|
74
|
+
)}
|
|
75
|
+
</label>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* TODO: The CSS styling is all messed up, including isError
|
|
81
|
+
* Warning: don't use this prop before this is resolved
|
|
82
|
+
* Issue to track: https://github.com/Luscii/web-ui/issues/57
|
|
83
|
+
* TODO: remove this comment once this is resolved
|
|
84
|
+
*/
|
|
85
|
+
export const RadioV2 = React.forwardRef<HTMLInputElement, RadioProps>(
|
|
86
|
+
(props, ref) => <RadioInner {...props} innerRef={ref} />
|
|
87
|
+
);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import classNames from "classnames";
|
|
3
|
+
|
|
4
|
+
import { RadioProps, RadioV2 } from "../Radio/RadioV2";
|
|
5
|
+
|
|
6
|
+
export interface RadioGroupProps
|
|
7
|
+
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "value"> {
|
|
8
|
+
name: string;
|
|
9
|
+
isError?: boolean;
|
|
10
|
+
options: Omit<RadioProps, "name">[];
|
|
11
|
+
innerRef?: React.Ref<HTMLInputElement>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function RadioGroupInner({
|
|
15
|
+
innerRef,
|
|
16
|
+
options,
|
|
17
|
+
...registerProps
|
|
18
|
+
}: RadioGroupProps): JSX.Element {
|
|
19
|
+
return (
|
|
20
|
+
<div className={classNames("flex flex-col space-y-2")}>
|
|
21
|
+
{options.map((option) => (
|
|
22
|
+
<RadioV2
|
|
23
|
+
key={option.value}
|
|
24
|
+
{...option}
|
|
25
|
+
{...registerProps}
|
|
26
|
+
ref={innerRef}
|
|
27
|
+
/>
|
|
28
|
+
))}
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const RadioGroupV2 = React.forwardRef<HTMLInputElement, RadioGroupProps>(
|
|
34
|
+
(props, ref) => <RadioGroupInner {...props} innerRef={ref} />
|
|
35
|
+
);
|
|
@@ -13,10 +13,11 @@ import Select from "react-select/base";
|
|
|
13
13
|
*/
|
|
14
14
|
import "./Select.scss";
|
|
15
15
|
|
|
16
|
-
function generateCustomStyles<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
function generateCustomStyles<
|
|
17
|
+
Option,
|
|
18
|
+
IsMulti extends boolean,
|
|
19
|
+
Group extends GroupBase<Option>
|
|
20
|
+
>(hasError: boolean, isIE11: boolean): StylesConfig<Option, IsMulti, Group> {
|
|
20
21
|
return {
|
|
21
22
|
option: (baseStyles, state) => {
|
|
22
23
|
return {
|
|
@@ -47,8 +48,12 @@ function generateCustomStyles<Option, IsMulti extends boolean, Group extends Gro
|
|
|
47
48
|
const defaultBorderColor = state.isFocused ? "#045baa" : "#D1D5DB";
|
|
48
49
|
const validatedBorderColor = hasError ? "#c53030" : defaultBorderColor;
|
|
49
50
|
|
|
50
|
-
const defaultOutline = hasError
|
|
51
|
-
|
|
51
|
+
const defaultOutline = hasError
|
|
52
|
+
? "rgba(255, 98, 102, 0.3)"
|
|
53
|
+
: "rgba(0, 159, 227, 0.3)";
|
|
54
|
+
const validatedOutline = `4px solid ${
|
|
55
|
+
state.isFocused ? defaultOutline : "transparent"
|
|
56
|
+
}`;
|
|
52
57
|
|
|
53
58
|
return {
|
|
54
59
|
...baseStyles,
|
|
@@ -101,24 +106,45 @@ function generateCustomStyles<Option, IsMulti extends boolean, Group extends Gro
|
|
|
101
106
|
};
|
|
102
107
|
}
|
|
103
108
|
|
|
104
|
-
type CustomSelect = <
|
|
109
|
+
type CustomSelect = <
|
|
110
|
+
Option,
|
|
111
|
+
IsMulti extends boolean,
|
|
112
|
+
Group extends GroupBase<Option>
|
|
113
|
+
>(
|
|
105
114
|
props: Props<Option, IsMulti, Group>,
|
|
106
115
|
ref: React.RefAttributes<SelectInstance<Option, IsMulti, Group>>
|
|
107
116
|
) => React.ReactElement;
|
|
108
117
|
|
|
118
|
+
/**
|
|
119
|
+
* @deprecated: use SelectV2 instead
|
|
120
|
+
*/
|
|
109
121
|
const CustomSelect = React.forwardRef(
|
|
110
122
|
<Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
|
|
111
123
|
props: Props<Option, IsMulti, Group>,
|
|
112
|
-
ref: Ref<Select<Option, IsMulti, Group
|
|
124
|
+
ref: Ref<Select<Option, IsMulti, Group>>
|
|
113
125
|
) => {
|
|
114
126
|
const { className, styles } = props;
|
|
115
127
|
|
|
116
128
|
const hasError = className?.includes("has-error") || false;
|
|
117
|
-
const isIE11 =
|
|
118
|
-
|
|
119
|
-
const
|
|
129
|
+
const isIE11 =
|
|
130
|
+
"MSInputMethodContext" in window && "documentMode" in document;
|
|
131
|
+
const customStyles = generateCustomStyles<Option, IsMulti, Group>(
|
|
132
|
+
hasError,
|
|
133
|
+
isIE11
|
|
134
|
+
);
|
|
135
|
+
const mergedStyles = mergeStyles<Option, IsMulti, Group>(
|
|
136
|
+
customStyles,
|
|
137
|
+
styles
|
|
138
|
+
);
|
|
120
139
|
|
|
121
|
-
return
|
|
140
|
+
return (
|
|
141
|
+
<ReactSelect<Option, IsMulti, Group>
|
|
142
|
+
{...props}
|
|
143
|
+
ref={ref}
|
|
144
|
+
className={classNames("customized-select", className)}
|
|
145
|
+
styles={mergedStyles}
|
|
146
|
+
/>
|
|
147
|
+
);
|
|
122
148
|
}
|
|
123
149
|
) as CustomSelect;
|
|
124
150
|
|