@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.
@@ -4,6 +4,8 @@ import { InputFieldProps } from '../InputTypes';
4
4
  import {
5
5
  Control,
6
6
  FieldValues,
7
+ UseFormClearErrors,
8
+ UseFormSetError,
7
9
  UseFormSetValue,
8
10
  useWatch,
9
11
  } from 'react-hook-form';
@@ -12,7 +14,11 @@ import Token from '../components/token';
12
14
 
13
15
  export interface StackedPilledInputProps extends InputFieldProps {
14
16
  setValue: UseFormSetValue<FieldValues>;
17
+ setError: UseFormSetError<FieldValues>;
18
+ clearErrors: UseFormClearErrors<FieldValues>;
15
19
  control: Control<FieldValues, any>;
20
+ // Number of allowed options
21
+ maxLength?: number;
16
22
  }
17
23
 
18
24
  /**
@@ -21,94 +27,87 @@ export interface StackedPilledInputProps extends InputFieldProps {
21
27
  const StackedPilledInput = React.forwardRef<
22
28
  HTMLInputElement,
23
29
  StackedPilledInputProps
24
- >(({ name, setValue, control, placeholder, disabled }, _ref) => {
25
- const watchedValue = useWatch({ control, name: name as string });
26
- const [lastestFormValueToArray, setLatestFormValueToArray] = useState<
27
- string[]
28
- >([]);
29
-
30
- const inputRef = useRef<HTMLInputElement>(null);
31
- const inputWrapperRef = useRef(null);
32
-
33
- const [tokenIndex, setTokenIndex] = useState<number | null>(null);
34
- const [isFocussed, setIsFocussed] = useState(false);
35
-
36
- const [localValue, setLocalValue] = useState('');
37
-
38
- // gets latest watched form value (common delimited) from RHF state and creates a list
39
- useEffect(() => {
40
- if (watchedValue !== undefined && !watchedValue.length) {
41
- setLatestFormValueToArray([]);
42
- }
43
-
44
- if (watchedValue !== undefined && watchedValue?.length) {
45
- setLatestFormValueToArray(watchedValue.split(',').filter(Boolean));
46
- }
47
- }, [watchedValue]);
48
-
49
- const onHandleKeyDown = (e: React.KeyboardEvent) => {
50
- if (e.key === ' ' || e.key === 'Enter' || e.key === ',') {
51
- if (
52
- e.key === 'Enter' &&
53
- !localValue.trim().length &&
54
- tokenIndex !== null
55
- ) {
56
- setLocalValue(lastestFormValueToArray[tokenIndex]);
30
+ >(
31
+ (
32
+ {
33
+ name,
34
+ setValue,
35
+ control,
36
+ placeholder,
37
+ disabled,
38
+ maxLength,
39
+ setError,
40
+ clearErrors,
41
+ },
42
+ _ref
43
+ ) => {
44
+ const watchedValue = useWatch({ control, name: name as string });
45
+ const [lastestFormValueToArray, setLatestFormValueToArray] = useState<
46
+ string[]
47
+ >([]);
57
48
 
58
- const filteredUniqueValues = Array.from(
59
- new Set(
60
- lastestFormValueToArray.filter(
61
- value => value !== lastestFormValueToArray[tokenIndex]
62
- )
63
- )
64
- );
49
+ const inputRef = useRef<HTMLInputElement>(null);
50
+ const inputWrapperRef = useRef(null);
65
51
 
66
- setValue(
67
- name as string,
68
- filteredUniqueValues.toString().replace(/\s/g, ''),
69
- {
70
- shouldValidate: true,
71
- shouldDirty: true,
72
- }
73
- );
52
+ const [tokenIndex, setTokenIndex] = useState<number | null>(null);
53
+ const [isFocussed, setIsFocussed] = useState(false);
54
+
55
+ const [localValue, setLocalValue] = useState('');
74
56
 
75
- return setTokenIndex(null);
57
+ // gets latest watched form value (common delimited) from RHF state and creates a list
58
+ useEffect(() => {
59
+ if (watchedValue !== undefined && !watchedValue.length) {
60
+ setLatestFormValueToArray([]);
76
61
  }
77
62
 
78
- const filteredUniqueValues = Array.from(
79
- new Set([...lastestFormValueToArray, ...localValue.trim().split(',')])
80
- );
63
+ if (watchedValue !== undefined && watchedValue?.length) {
64
+ setLatestFormValueToArray(watchedValue.split(',').filter(Boolean));
65
+ }
66
+ }, [watchedValue]);
81
67
 
82
- setLocalValue('');
68
+ const onHandleKeyDown = (e: React.KeyboardEvent) => {
69
+ if (e.key === ' ' || e.key === 'Enter' || e.key === ',') {
70
+ if (maxLength && lastestFormValueToArray.length >= maxLength) {
71
+ return setError(name as string, {
72
+ type: 'maxLength',
73
+ message: `Exceeded maximum of ${maxLength} options`,
74
+ });
75
+ }
83
76
 
84
- return setValue(
85
- name as string,
86
- filteredUniqueValues.toString().replace(/\s/g, ''),
87
- {
88
- shouldValidate: true,
89
- shouldDirty: true,
77
+ if (
78
+ e.key === 'Enter' &&
79
+ !localValue.trim().length &&
80
+ tokenIndex !== null
81
+ ) {
82
+ setLocalValue(lastestFormValueToArray[tokenIndex]);
83
+
84
+ const filteredUniqueValues = Array.from(
85
+ new Set(
86
+ lastestFormValueToArray.filter(
87
+ value => value !== lastestFormValueToArray[tokenIndex]
88
+ )
89
+ )
90
+ );
91
+
92
+ setValue(
93
+ name as string,
94
+ filteredUniqueValues.toString().replace(/\s/g, ''),
95
+ {
96
+ shouldValidate: true,
97
+ shouldDirty: true,
98
+ }
99
+ );
100
+
101
+ return setTokenIndex(null);
90
102
  }
91
- );
92
- }
93
-
94
- if (!localValue.trim().length && lastestFormValueToArray.length) {
95
- if (e.key === 'Backspace' && tokenIndex !== null) {
96
- setLocalValue(
97
- lastestFormValueToArray[tokenIndex].substring(
98
- 0,
99
- lastestFormValueToArray[tokenIndex].length
100
- )
101
- );
102
103
 
103
104
  const filteredUniqueValues = Array.from(
104
- new Set(
105
- [...lastestFormValueToArray].filter(
106
- value => value !== lastestFormValueToArray[tokenIndex]
107
- )
108
- )
105
+ new Set([...lastestFormValueToArray, ...localValue.trim().split(',')])
109
106
  );
110
107
 
111
- setValue(
108
+ setLocalValue('');
109
+
110
+ return setValue(
112
111
  name as string,
113
112
  filteredUniqueValues.toString().replace(/\s/g, ''),
114
113
  {
@@ -116,54 +115,69 @@ const StackedPilledInput = React.forwardRef<
116
115
  shouldDirty: true,
117
116
  }
118
117
  );
119
-
120
- return setTokenIndex(null);
121
118
  }
122
119
 
123
- if (e.key === 'ArrowLeft') {
124
- if (tokenIndex === 0) return;
125
-
126
- if (!tokenIndex) {
127
- return setTokenIndex(lastestFormValueToArray.length - 1);
128
- }
120
+ if (!localValue.trim().length && lastestFormValueToArray.length) {
121
+ if (e.key === 'Backspace' && tokenIndex !== null) {
122
+ setLocalValue(
123
+ lastestFormValueToArray[tokenIndex].substring(
124
+ 0,
125
+ lastestFormValueToArray[tokenIndex].length
126
+ )
127
+ );
129
128
 
130
- return setTokenIndex(prevTokenIndex => (prevTokenIndex as number) - 1);
131
- }
129
+ const filteredUniqueValues = Array.from(
130
+ new Set(
131
+ [...lastestFormValueToArray].filter(
132
+ value => value !== lastestFormValueToArray[tokenIndex]
133
+ )
134
+ )
135
+ );
132
136
 
133
- if (e.key === 'ArrowRight') {
134
- if (tokenIndex === null) return;
137
+ setValue(
138
+ name as string,
139
+ filteredUniqueValues.toString().replace(/\s/g, ''),
140
+ {
141
+ shouldValidate: true,
142
+ shouldDirty: true,
143
+ }
144
+ );
135
145
 
136
- if (tokenIndex === lastestFormValueToArray.length - 1) {
137
146
  return setTokenIndex(null);
138
147
  }
139
- return setTokenIndex(prevTokenIndex => (prevTokenIndex as number) + 1);
140
- }
141
- }
142
- };
143
148
 
144
- const onRemoveTag = (index: number) => {
145
- const filteredUniqueValues = lastestFormValueToArray.filter(
146
- (_, i) => i !== index
147
- );
149
+ if (e.key === 'ArrowLeft') {
150
+ if (tokenIndex === 0) return;
151
+
152
+ if (!tokenIndex) {
153
+ return setTokenIndex(lastestFormValueToArray.length - 1);
154
+ }
148
155
 
149
- setLatestFormValueToArray(filteredUniqueValues);
156
+ return setTokenIndex(
157
+ prevTokenIndex => (prevTokenIndex as number) - 1
158
+ );
159
+ }
160
+
161
+ if (e.key === 'ArrowRight') {
162
+ if (tokenIndex === null) return;
150
163
 
151
- setValue(
152
- name as string,
153
- filteredUniqueValues.toString().replace(/\s/g, ''),
154
- {
155
- shouldValidate: true,
156
- shouldDirty: true,
164
+ if (tokenIndex === lastestFormValueToArray.length - 1) {
165
+ return setTokenIndex(null);
166
+ }
167
+ return setTokenIndex(
168
+ prevTokenIndex => (prevTokenIndex as number) + 1
169
+ );
170
+ }
157
171
  }
158
- );
159
- };
172
+ };
160
173
 
161
- const onBlur = () => {
162
- if (localValue.trim()) {
163
- const filteredUniqueValues = Array.from(
164
- new Set([...lastestFormValueToArray, ...localValue.trim().split(',')])
174
+ const onRemoveTag = (index: number) => {
175
+ const filteredUniqueValues = lastestFormValueToArray.filter(
176
+ (_, i) => i !== index
165
177
  );
166
178
 
179
+ setLatestFormValueToArray(filteredUniqueValues);
180
+
167
181
  setValue(
168
182
  name as string,
169
183
  filteredUniqueValues.toString().replace(/\s/g, ''),
@@ -172,92 +186,118 @@ const StackedPilledInput = React.forwardRef<
172
186
  shouldDirty: true,
173
187
  }
174
188
  );
175
- setLocalValue('');
176
- }
177
- setIsFocussed(false);
178
- };
179
-
180
- useOutsideClick({ ref: inputWrapperRef, handler: onBlur });
181
-
182
- return (
183
- <Box position="relative">
184
- <Flex
185
- fontSize="13px"
186
- border={isFocussed ? '2px solid' : '1px solid'}
187
- borderColor={isFocussed ? colors.border.focus : colors.border.default}
188
- py="5px"
189
- pl="8px"
190
- borderRadius="4px"
191
- alignItems="center"
192
- justifyContent="space-between"
193
- onClick={() => {
194
- if (!disabled) {
195
- inputRef.current?.focus();
189
+ };
190
+
191
+ const onBlur = () => {
192
+ clearErrors(name);
193
+
194
+ if (localValue.trim()) {
195
+ if (
196
+ maxLength &&
197
+ watchedValue.length + localValue.trim().length > maxLength
198
+ )
199
+ return setLocalValue('');
200
+
201
+ const filteredUniqueValues = Array.from(
202
+ new Set([...lastestFormValueToArray, ...localValue.trim().split(',')])
203
+ );
204
+
205
+ setValue(
206
+ name as string,
207
+ filteredUniqueValues.toString().replace(/\s/g, ''),
208
+ {
209
+ shouldValidate: true,
210
+ shouldDirty: true,
196
211
  }
197
- }}
198
- bg={disabled ? colors.fill.light.quaternary : '#ffffff'}
199
- cursor={disabled ? 'not-allowed' : 'pointer'}
200
- ref={inputWrapperRef}
201
- >
212
+ );
213
+ setLocalValue('');
214
+ }
215
+ setIsFocussed(false);
216
+ };
217
+
218
+ useOutsideClick({ ref: inputWrapperRef, handler: onBlur });
219
+
220
+ return (
221
+ <Box position="relative">
202
222
  <Flex
203
- height="28px"
223
+ fontSize="13px"
224
+ border={isFocussed ? '2px solid' : '1px solid'}
225
+ borderColor={isFocussed ? colors.border.focus : colors.border.default}
226
+ py="5px"
227
+ pl="8px"
228
+ borderRadius="4px"
204
229
  alignItems="center"
205
- // width="fit-content"
206
- // maxW="70%"
207
- overflowX="auto"
208
- style={{
209
- scrollbarWidth: 'none' /* Firefox */,
230
+ justifyContent="space-between"
231
+ onClick={() => {
232
+ if (!disabled) {
233
+ inputRef.current?.focus();
234
+ }
210
235
  }}
236
+ bg={disabled ? colors.fill.light.quaternary : '#ffffff'}
237
+ cursor={disabled ? 'not-allowed' : 'pointer'}
238
+ ref={inputWrapperRef}
211
239
  >
212
- {lastestFormValueToArray.length ? (
213
- lastestFormValueToArray.map((label, index) => (
214
- <Box
215
- mr="4px"
216
- border={
217
- tokenIndex === index
218
- ? `2px solid ${colors.border.focus}`
219
- : 'none'
220
- }
221
- borderRadius="full"
222
- onClick={() => setTokenIndex(index)}
223
- // width="fit-content"
224
- width="100%"
225
- >
226
- <Token
227
- label={label}
228
- onDelete={(e: any) => {
229
- e.stopPropagation();
230
- e.preventDefault();
231
- onRemoveTag(index);
232
- }}
233
- />
234
- </Box>
235
- ))
236
- ) : (
237
- <Text color={colors.label.secondary.light} fontSize="13px">
238
- {placeholder}
239
- </Text>
240
- )}
240
+ <Flex
241
+ height="28px"
242
+ alignItems="center"
243
+ width="fit-content"
244
+ maxW="80%"
245
+ overflowX="auto"
246
+ style={{
247
+ scrollbarWidth: 'none' /* Firefox */,
248
+ }}
249
+ >
250
+ {lastestFormValueToArray.length ? (
251
+ lastestFormValueToArray.map((label, index) => (
252
+ <Box
253
+ mr="4px"
254
+ border={
255
+ tokenIndex === index
256
+ ? `2px solid ${colors.border.focus}`
257
+ : 'none'
258
+ }
259
+ borderRadius="full"
260
+ onClick={() => setTokenIndex(index)}
261
+ width="100%"
262
+ >
263
+ <Token
264
+ label={label}
265
+ onDelete={(e: any) => {
266
+ e.stopPropagation();
267
+ e.preventDefault();
268
+ onRemoveTag(index);
269
+ }}
270
+ />
271
+ </Box>
272
+ ))
273
+ ) : (
274
+ <Text color={colors.label.secondary.light} fontSize="13px">
275
+ {placeholder}
276
+ </Text>
277
+ )}
278
+ </Flex>
279
+ <Flex flex={1}>
280
+ <Input
281
+ onKeyDown={onHandleKeyDown}
282
+ type="text"
283
+ padding={0}
284
+ width="100%"
285
+ border="none"
286
+ height="26px"
287
+ color={tokenIndex !== null ? 'transparent' : colors.label.primary}
288
+ _focus={{ boxShadow: 'none !important' }}
289
+ value={localValue}
290
+ onChange={e =>
291
+ tokenIndex === null && setLocalValue(e.target.value)
292
+ }
293
+ ref={inputRef}
294
+ onFocus={() => setIsFocussed(true)}
295
+ />
296
+ </Flex>
241
297
  </Flex>
242
- <Flex flex={1}>
243
- <Input
244
- onKeyDown={onHandleKeyDown}
245
- type="text"
246
- padding={0}
247
- width="100%"
248
- border="none"
249
- height="26px"
250
- color={tokenIndex !== null ? 'transparent' : colors.label.primary}
251
- _focus={{ boxShadow: 'none !important' }}
252
- value={localValue}
253
- onChange={e => tokenIndex === null && setLocalValue(e.target.value)}
254
- ref={inputRef}
255
- onFocus={() => setIsFocussed(true)}
256
- />
257
- </Flex>
258
- </Flex>
259
- </Box>
260
- );
261
- });
298
+ </Box>
299
+ );
300
+ }
301
+ );
262
302
 
263
303
  export default StackedPilledInput;
@@ -26,6 +26,7 @@ export const Dropdown: React.FC<DropdownProps> = ({
26
26
  fontSize="13px"
27
27
  fontWeight="bold"
28
28
  px="8px"
29
+ bg="inherit"
29
30
  >
30
31
  {idx > 0 && (
31
32
  <Box
@@ -52,6 +53,7 @@ export const Dropdown: React.FC<DropdownProps> = ({
52
53
  bg: colors.fill.action,
53
54
  borderRadius: '4px',
54
55
  }}
56
+ bg="inherit"
55
57
  >
56
58
  {option.label}
57
59
  </Box>
@@ -63,8 +65,11 @@ export const Dropdown: React.FC<DropdownProps> = ({
63
65
  return (
64
66
  <Box
65
67
  bg={colors.fill.light.quaternary}
66
- backdropFilter="blur(64px)"
68
+ backdropFilter="auto"
69
+ backdropBlur="64px"
67
70
  borderRadius="4px"
71
+ border="0.25px solid"
72
+ borderColor={colors.fill.light.tertiary}
68
73
  mt="3px"
69
74
  maxH="320px"
70
75
  overflowY="auto"
@@ -72,7 +77,7 @@ export const Dropdown: React.FC<DropdownProps> = ({
72
77
  py="4px"
73
78
  position="absolute"
74
79
  width="100%"
75
- zIndex="100"
80
+ zIndex={100}
76
81
  >
77
82
  {DropdownContent}
78
83
  </Box>
@@ -1,3 +1,3 @@
1
- <svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
1
+ <svg viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
2
2
  <path fill-rule="evenodd" clip-rule="evenodd" d="M27.9995 56C43.4635 56 55.9995 43.464 55.9995 28C55.9995 12.536 43.4635 0 27.9995 0C12.5355 0 -0.000488281 12.536 -0.000488281 28C-0.000488281 43.464 12.5355 56 27.9995 56ZM21.9807 18.2688C20.9555 17.2437 19.2935 17.2437 18.2684 18.2688C17.2432 19.294 17.2432 20.956 18.2684 21.9812L24.2872 28L18.2684 34.0188C17.2432 35.044 17.2432 36.706 18.2684 37.7312C19.2935 38.7563 20.9555 38.7563 21.9807 37.7312L27.9995 31.7123L34.0184 37.7312C35.0435 38.7563 36.7055 38.7563 37.7307 37.7312C38.7558 36.706 38.7558 35.044 37.7307 34.0188L31.7118 28L37.7307 21.9812C38.7558 20.956 38.7558 19.294 37.7307 18.2688C36.7055 17.2437 35.0435 17.2437 34.0184 18.2688L27.9995 24.2877L21.9807 18.2688Z" fill="#3C3C43" fill-opacity="0.6"/>
3
3
  </svg>
@@ -1,7 +1,8 @@
1
- import { Flex, Image, Text } from '@chakra-ui/react';
1
+ import { Flex, Text } from '@chakra-ui/react';
2
+ import { truncate } from 'lodash';
2
3
  import React from 'react';
3
4
  import colors from '../../../../../src/theme/foundations/colors';
4
- import CloseIcon from './assets/svg/close.svg';
5
+ import { ReactComponent as CloseIcon } from './assets/svg/close.svg';
5
6
 
6
7
  export interface TokenProps {
7
8
  label: any;
@@ -15,18 +16,23 @@ const Token: React.FC<TokenProps> = ({ label, onDelete }) => {
15
16
  borderRadius="full"
16
17
  backgroundColor="#7676801F"
17
18
  alignItems="center"
18
- // width="100%"
19
+ width="fit-content"
20
+ w="auto"
21
+ h="auto"
19
22
  pl="8px"
20
23
  pr="4px"
21
24
  py="2px"
25
+ position="relative"
22
26
  >
23
- <Text color={colors.label.primary.light} fontSize="13px">
24
- {label}
27
+ <Text color={colors.label.primary.light} fontSize="13px" pr="4px">
28
+ {truncate(label.trim(), {
29
+ length: 15,
30
+ omission: '...',
31
+ })}
25
32
  </Text>
26
- <Image
27
- pl="4px"
28
- boxSize="16px"
29
- src={CloseIcon}
33
+ <CloseIcon
34
+ width="13px"
35
+ height="13px"
30
36
  onClick={onDelete}
31
37
  cursor="pointer"
32
38
  />
@@ -17,6 +17,8 @@ import {
17
17
  Path,
18
18
  PathValue,
19
19
  RefCallBack,
20
+ UseFormClearErrors,
21
+ UseFormSetError,
20
22
  UseFormSetValue,
21
23
  } from 'react-hook-form';
22
24
  import StackedMultiSelect from './StackedMultiSelect';
@@ -39,6 +41,8 @@ export interface InputProps<T extends FieldValues> extends ValidationProps {
39
41
  onChange?: (value?: string) => void;
40
42
  disabled?: boolean;
41
43
  setValue: UseFormSetValue<T>;
44
+ setError: UseFormSetError<T>;
45
+ clearErrors: UseFormClearErrors<T>;
42
46
  }
43
47
 
44
48
  /**
@@ -63,6 +67,8 @@ export function Input<T extends FieldValues>({
63
67
  disabled,
64
68
  onChange,
65
69
  setValue,
70
+ setError,
71
+ clearErrors,
66
72
  }: InputProps<T>) {
67
73
  const selectedInputField = (
68
74
  onChange: () => void,
@@ -169,7 +175,10 @@ export function Input<T extends FieldValues>({
169
175
  value={value}
170
176
  setValue={setValue as UseFormSetValue<FieldValues>}
171
177
  control={control as Control<FieldValues, any>}
178
+ setError={setError as UseFormSetError<FieldValues>}
179
+ clearErrors={clearErrors as UseFormClearErrors<FieldValues>}
172
180
  placeholder={placeholder}
181
+ maxLength={maxLength}
173
182
  />
174
183
  );
175
184
  case 'pilled-text':
@@ -186,7 +195,10 @@ export function Input<T extends FieldValues>({
186
195
  disabled={disabled}
187
196
  value={value}
188
197
  setValue={setValue as UseFormSetValue<FieldValues>}
198
+ setError={setError as UseFormSetError<FieldValues>}
199
+ clearErrors={clearErrors as UseFormClearErrors<FieldValues>}
189
200
  control={control as Control<FieldValues, any>}
201
+ maxLength={maxLength}
190
202
  />
191
203
  );
192
204
  case 'switch':