@xqmsg/ui-core 0.14.1 → 0.14.2
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/input/StackedMultiSelect/index.d.ts +4 -1
- package/dist/components/input/StackedPilledInput/index.d.ts +4 -1
- package/dist/components/input/index.d.ts +4 -2
- package/dist/ui-core.cjs.development.js +98 -24
- package/dist/ui-core.cjs.development.js.map +1 -1
- package/dist/ui-core.cjs.production.min.js +1 -1
- package/dist/ui-core.cjs.production.min.js.map +1 -1
- package/dist/ui-core.esm.js +101 -27
- package/dist/ui-core.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/form/section/index.tsx +2 -0
- package/src/components/input/Input.stories.tsx +10 -0
- package/src/components/input/StackedMultiSelect/index.tsx +153 -101
- package/src/components/input/StackedPilledInput/index.tsx +231 -191
- package/src/components/input/components/dropdown/index.tsx +7 -2
- package/src/components/input/components/token/assets/svg/close.svg +1 -1
- package/src/components/input/components/token/index.tsx +15 -9
- package/src/components/input/index.tsx +12 -0
package/package.json
CHANGED
|
@@ -96,12 +96,18 @@ const Template: Story<InputProps<StoryFormSchema>> = args => {
|
|
|
96
96
|
|
|
97
97
|
const { form } = formHandler;
|
|
98
98
|
|
|
99
|
+
console.log(form.formState.errors);
|
|
100
|
+
|
|
99
101
|
return (
|
|
100
102
|
<Form formHandler={formHandler}>
|
|
101
103
|
<Input
|
|
102
104
|
{...args}
|
|
103
105
|
inputType="multi-select"
|
|
104
106
|
setValue={form.setValue}
|
|
107
|
+
setError={form.setError}
|
|
108
|
+
clearErrors={form.clearErrors}
|
|
109
|
+
isInvalid={!!form.formState.errors['prop5']?.message}
|
|
110
|
+
errorText={form.formState.errors['prop5']?.message}
|
|
105
111
|
name="prop5"
|
|
106
112
|
/>
|
|
107
113
|
<Input
|
|
@@ -127,6 +133,10 @@ const Template: Story<InputProps<StoryFormSchema>> = args => {
|
|
|
127
133
|
name="prop"
|
|
128
134
|
inputType="pilled-text"
|
|
129
135
|
setValue={form.setValue}
|
|
136
|
+
setError={form.setError}
|
|
137
|
+
clearErrors={form.clearErrors}
|
|
138
|
+
isInvalid={!!form.formState.errors['prop']?.message}
|
|
139
|
+
errorText={form.formState.errors['prop']?.message}
|
|
130
140
|
/>
|
|
131
141
|
<Input
|
|
132
142
|
{...args}
|
|
@@ -9,6 +9,8 @@ import colors from '../../../theme/foundations/colors';
|
|
|
9
9
|
import {
|
|
10
10
|
Control,
|
|
11
11
|
FieldValues,
|
|
12
|
+
UseFormClearErrors,
|
|
13
|
+
UseFormSetError,
|
|
12
14
|
UseFormSetValue,
|
|
13
15
|
useWatch,
|
|
14
16
|
} from 'react-hook-form';
|
|
@@ -19,7 +21,11 @@ import Token from '../components/token';
|
|
|
19
21
|
export interface StackedMultiSelectProps extends ReactSelectFieldProps {
|
|
20
22
|
options: FieldOptions;
|
|
21
23
|
setValue: UseFormSetValue<FieldValues>;
|
|
24
|
+
setError: UseFormSetError<FieldValues>;
|
|
25
|
+
clearErrors: UseFormClearErrors<FieldValues>;
|
|
22
26
|
control: Control<FieldValues, any>;
|
|
27
|
+
// Number of allowed options
|
|
28
|
+
maxLength?: number;
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
/**
|
|
@@ -28,114 +34,160 @@ export interface StackedMultiSelectProps extends ReactSelectFieldProps {
|
|
|
28
34
|
const StackedMultiSelect = React.forwardRef<
|
|
29
35
|
HTMLInputElement,
|
|
30
36
|
StackedMultiSelectProps
|
|
31
|
-
>(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
setValue(name as string, newValue, {
|
|
65
|
-
shouldValidate: true,
|
|
66
|
-
shouldDirty: true,
|
|
37
|
+
>(
|
|
38
|
+
(
|
|
39
|
+
{
|
|
40
|
+
options,
|
|
41
|
+
setValue,
|
|
42
|
+
control,
|
|
43
|
+
name,
|
|
44
|
+
placeholder,
|
|
45
|
+
disabled,
|
|
46
|
+
maxLength,
|
|
47
|
+
setError,
|
|
48
|
+
},
|
|
49
|
+
_ref
|
|
50
|
+
) => {
|
|
51
|
+
const watchedValue = useWatch({ control, name: name as string });
|
|
52
|
+
const dropdownRef = useRef(null);
|
|
53
|
+
|
|
54
|
+
const [localValues, setLocalValues] = useState<FieldOptions>([]);
|
|
55
|
+
const [localOptions, setLocalOptions] = useState<FieldOptions>(options);
|
|
56
|
+
const [isFocussed, setIsFocussed] = useState(false);
|
|
57
|
+
|
|
58
|
+
useOutsideClick({
|
|
59
|
+
ref: dropdownRef,
|
|
60
|
+
handler: () => {
|
|
61
|
+
if (maxLength && localValues.length > maxLength) {
|
|
62
|
+
setError(name as string, {
|
|
63
|
+
message: `Exceeded maximum of ${maxLength} options`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return setIsFocussed(false);
|
|
68
|
+
},
|
|
67
69
|
});
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
// gets latest watched form value (common delimited) from RHF state and creates a list
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (watchedValue !== undefined && !watchedValue.length) {
|
|
74
|
+
setLocalValues([]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (
|
|
78
|
+
maxLength &&
|
|
79
|
+
watchedValue !== undefined &&
|
|
80
|
+
watchedValue.length <= maxLength &&
|
|
81
|
+
watchedValue?.length
|
|
82
|
+
) {
|
|
83
|
+
setLocalValues(
|
|
84
|
+
watchedValue
|
|
85
|
+
.split(',')
|
|
86
|
+
.filter(Boolean)
|
|
87
|
+
.map((value: string) =>
|
|
88
|
+
options.find(option => option.value === value)
|
|
89
|
+
)
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}, [maxLength, options, watchedValue]);
|
|
93
|
+
|
|
94
|
+
const handleChange = (option: FieldOption) => {
|
|
95
|
+
console.log(localValues.length, maxLength);
|
|
96
|
+
|
|
97
|
+
if (maxLength && localValues.length > maxLength) {
|
|
98
|
+
return setError(name as string, {
|
|
99
|
+
message: `Exceeded maximum of ${maxLength} options`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const newValue = [...localValues, option]
|
|
104
|
+
.map(({ value }) => value)
|
|
105
|
+
.join(',');
|
|
106
|
+
|
|
107
|
+
setValue(name as string, newValue, {
|
|
108
|
+
shouldValidate: true,
|
|
109
|
+
shouldDirty: true,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
setLocalOptions(prevLocalOptions =>
|
|
113
|
+
prevLocalOptions.filter(prevLocalOption => prevLocalOption !== option)
|
|
114
|
+
);
|
|
72
115
|
|
|
73
|
-
|
|
74
|
-
|
|
116
|
+
setLocalValues(prevLocalValues => [...prevLocalValues, option]);
|
|
117
|
+
};
|
|
75
118
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
119
|
+
const handleDelete = (option: FieldOption) => {
|
|
120
|
+
const newValue = localValues
|
|
121
|
+
.filter(localValue => localValue !== option)
|
|
122
|
+
.map(({ value }) => value)
|
|
123
|
+
.join(',');
|
|
81
124
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
125
|
+
setValue(name as string, newValue, {
|
|
126
|
+
shouldValidate: true,
|
|
127
|
+
shouldDirty: true,
|
|
128
|
+
});
|
|
86
129
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
130
|
+
setLocalOptions(prevLocalOptions =>
|
|
131
|
+
[...prevLocalOptions, option].sort((a, b) => a.sortValue - b.sortValue)
|
|
132
|
+
);
|
|
90
133
|
|
|
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
|
-
|
|
134
|
+
setLocalValues(prevLocalValues =>
|
|
135
|
+
prevLocalValues.filter(prevLocalValue => prevLocalValue !== option)
|
|
136
|
+
);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<Box ref={dropdownRef} position="relative">
|
|
141
|
+
<Flex
|
|
142
|
+
fontSize="13px"
|
|
143
|
+
border={isFocussed ? '2px solid' : '1px solid'}
|
|
144
|
+
borderColor={isFocussed ? colors.border.focus : colors.border.default}
|
|
145
|
+
py="5px"
|
|
146
|
+
pl="8px"
|
|
147
|
+
borderRadius="4px"
|
|
148
|
+
alignItems="center"
|
|
149
|
+
justifyContent="space-between"
|
|
150
|
+
onClick={() => !disabled && setIsFocussed(true)}
|
|
151
|
+
bg={disabled ? colors.fill.light.quaternary : '#ffffff'}
|
|
152
|
+
cursor={disabled ? 'not-allowed' : 'pointer'}
|
|
153
|
+
>
|
|
154
|
+
<Flex
|
|
155
|
+
height="28px"
|
|
156
|
+
alignItems="center"
|
|
157
|
+
overflowX="auto"
|
|
158
|
+
maxWidth="90%"
|
|
159
|
+
style={{
|
|
160
|
+
scrollbarWidth: 'none' /* Firefox */,
|
|
161
|
+
}}
|
|
162
|
+
>
|
|
163
|
+
{localValues.length ? (
|
|
164
|
+
localValues.map(option => (
|
|
165
|
+
<Box mr="4px">
|
|
166
|
+
<Token
|
|
167
|
+
label={option.label}
|
|
168
|
+
onDelete={() => handleDelete(option)}
|
|
169
|
+
/>
|
|
170
|
+
</Box>
|
|
171
|
+
))
|
|
172
|
+
) : (
|
|
173
|
+
<Text color={colors.label.secondary.light} fontSize="13px">
|
|
174
|
+
{placeholder}
|
|
175
|
+
</Text>
|
|
176
|
+
)}
|
|
177
|
+
</Flex>
|
|
178
|
+
<Flex width="39px" justifyContent="center" alignItems="center">
|
|
179
|
+
<Image src={SubtractIcon} alt="subtract" boxSize="16px" />
|
|
180
|
+
</Flex>
|
|
129
181
|
</Flex>
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
182
|
+
{isFocussed && (
|
|
183
|
+
<Dropdown
|
|
184
|
+
onSelectItem={option => handleChange(option)}
|
|
185
|
+
options={localOptions}
|
|
186
|
+
/>
|
|
187
|
+
)}
|
|
188
|
+
</Box>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
);
|
|
140
192
|
|
|
141
193
|
export default StackedMultiSelect;
|