@xqmsg/ui-core 0.24.11 → 0.25.1
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/SimpleTable/SimpleTable.d.ts +4 -2
- package/dist/components/SimpleTable/TableTypes.d.ts +3 -0
- package/dist/components/input/StackedPilledInput/index.d.ts +1 -0
- package/dist/components/input/components/token/index.d.ts +3 -0
- package/dist/components/input/index.d.ts +2 -1
- package/dist/ui-core.cjs.development.js +122 -45
- 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 +122 -45
- package/dist/ui-core.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/SimpleTable/SimpleTable.tsx +10 -3
- package/src/components/SimpleTable/TableTypes.ts +4 -0
- package/src/components/icons/close/index.tsx +1 -0
- package/src/components/input/Input.stories.tsx +15 -0
- package/src/components/input/StackedPilledInput/index.tsx +77 -22
- package/src/components/input/components/token/index.tsx +36 -6
- package/src/components/input/index.tsx +3 -0
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
ReadonlyTableColumns,
|
|
4
4
|
TableBody,
|
|
5
5
|
TableColumns,
|
|
6
|
+
TableColumnsWidths,
|
|
6
7
|
TableHeaders,
|
|
7
8
|
} from './TableTypes';
|
|
8
9
|
import { generateTableColumnsAsConst } from './utils/generateTableColumns';
|
|
@@ -27,6 +28,8 @@ export interface SimpleTableProps<T extends ReadonlyTableColumns> {
|
|
|
27
28
|
loading?: boolean;
|
|
28
29
|
loadMore?: () => void;
|
|
29
30
|
placeholder?: string;
|
|
31
|
+
layout?: 'fixed' | 'auto';
|
|
32
|
+
columnsWidths?: TableColumnsWidths<T>;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
/**
|
|
@@ -38,6 +41,8 @@ export function SimpleTable<T extends ReadonlyTableColumns>({
|
|
|
38
41
|
body,
|
|
39
42
|
loading,
|
|
40
43
|
loadMore,
|
|
44
|
+
layout = 'auto',
|
|
45
|
+
columnsWidths,
|
|
41
46
|
}: SimpleTableProps<T>) {
|
|
42
47
|
const columnsAsConst = generateTableColumnsAsConst(columns);
|
|
43
48
|
|
|
@@ -50,14 +55,16 @@ export function SimpleTable<T extends ReadonlyTableColumns>({
|
|
|
50
55
|
style={{
|
|
51
56
|
borderCollapse: 'separate',
|
|
52
57
|
borderSpacing: '0px',
|
|
58
|
+
tableLayout: layout,
|
|
53
59
|
}}
|
|
54
60
|
>
|
|
55
|
-
{headers && (
|
|
61
|
+
{(headers || columnsWidths) && (
|
|
56
62
|
<Thead>
|
|
57
63
|
<Tr _odd={{ bg: colors.label.primary.dark }}>
|
|
58
64
|
{columnsAsConst.map((column, idx) => (
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
<Th key={idx} width={columnsWidths?.[column as T[number]]}>
|
|
66
|
+
{headers && headers[column as T[number]]}
|
|
67
|
+
</Th>
|
|
61
68
|
))}
|
|
62
69
|
</Tr>
|
|
63
70
|
</Thead>
|
|
@@ -8,6 +8,10 @@ export type TableHeaders<K extends ReadonlyTableColumns> = {
|
|
|
8
8
|
[k in K[number]]: ReactNode;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
export type TableColumnsWidths<K extends ReadonlyTableColumns> = {
|
|
12
|
+
[k in K[number]]: string | number;
|
|
13
|
+
};
|
|
14
|
+
|
|
11
15
|
export type TableRow<K extends ReadonlyTableColumns> = {
|
|
12
16
|
[k in K[number]]: ReactNode;
|
|
13
17
|
};
|
|
@@ -112,6 +112,21 @@ const Template: Story<InputProps<StoryFormSchema>> = args => {
|
|
|
112
112
|
|
|
113
113
|
return (
|
|
114
114
|
<Form formHandler={formHandler}>
|
|
115
|
+
<Input
|
|
116
|
+
label="To"
|
|
117
|
+
name="recipients"
|
|
118
|
+
inputType="pilled-text"
|
|
119
|
+
placeholder="Enter email address..."
|
|
120
|
+
isInvalid={!!form.formState.errors['prop5']?.message}
|
|
121
|
+
errorText={form.formState.errors['prop5']?.message}
|
|
122
|
+
control={form.control}
|
|
123
|
+
setValue={form.setValue}
|
|
124
|
+
setError={form.setError}
|
|
125
|
+
clearErrors={form.clearErrors}
|
|
126
|
+
ariaLabel="email input"
|
|
127
|
+
isRequired
|
|
128
|
+
truncatePillLength={1000}
|
|
129
|
+
/>
|
|
115
130
|
<Input
|
|
116
131
|
{...args}
|
|
117
132
|
inputType="multi-select"
|
|
@@ -21,6 +21,7 @@ export interface StackedPilledInputProps extends InputFieldProps {
|
|
|
21
21
|
variant?: string;
|
|
22
22
|
label?: string;
|
|
23
23
|
truncatePillLength?: number;
|
|
24
|
+
mode?: 'scroll' | 'wrap';
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
/**
|
|
@@ -41,6 +42,7 @@ const StackedPilledInput = React.forwardRef<
|
|
|
41
42
|
variant,
|
|
42
43
|
label,
|
|
43
44
|
truncatePillLength,
|
|
45
|
+
mode = 'scroll',
|
|
44
46
|
},
|
|
45
47
|
_ref
|
|
46
48
|
) => {
|
|
@@ -60,7 +62,7 @@ const StackedPilledInput = React.forwardRef<
|
|
|
60
62
|
const latestTokenElement = document.getElementById(
|
|
61
63
|
`${name}_token_${lastestFormValueToArray.length - 1}`
|
|
62
64
|
);
|
|
63
|
-
|
|
65
|
+
const scrollMode = mode === 'scroll';
|
|
64
66
|
// gets latest watched form value (common delimited) from RHF state and creates a list
|
|
65
67
|
useEffect(() => {
|
|
66
68
|
if (watchedValue !== undefined && !watchedValue.length) {
|
|
@@ -148,19 +150,58 @@ const StackedPilledInput = React.forwardRef<
|
|
|
148
150
|
});
|
|
149
151
|
}
|
|
150
152
|
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
153
|
+
if (e.key === 'Backspace') {
|
|
154
|
+
// If input is empty and there are tokens
|
|
155
|
+
if (!localValue.length && lastestFormValueToArray.length) {
|
|
156
|
+
// If a token is selected, move it to input
|
|
157
|
+
if (tokenIndex !== null) {
|
|
158
|
+
setLocalValue(
|
|
159
|
+
lastestFormValueToArray[tokenIndex].substring(
|
|
160
|
+
0,
|
|
161
|
+
lastestFormValueToArray[tokenIndex].length
|
|
162
|
+
)
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const filteredUniqueValues = Array.from(
|
|
166
|
+
new Set(
|
|
167
|
+
[...lastestFormValueToArray].filter(
|
|
168
|
+
value => value !== lastestFormValueToArray[tokenIndex]
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
setValue(name as string, filteredUniqueValues.toString(), {
|
|
174
|
+
shouldValidate: true,
|
|
175
|
+
shouldDirty: true,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return setTokenIndex(null);
|
|
179
|
+
} else {
|
|
180
|
+
// No token selected, move last token to input
|
|
181
|
+
const lastToken =
|
|
182
|
+
lastestFormValueToArray[lastestFormValueToArray.length - 1];
|
|
183
|
+
setLocalValue(lastToken);
|
|
159
184
|
|
|
185
|
+
const filteredUniqueValues = lastestFormValueToArray.slice(0, -1);
|
|
186
|
+
|
|
187
|
+
setValue(name as string, filteredUniqueValues.toString(), {
|
|
188
|
+
shouldValidate: true,
|
|
189
|
+
shouldDirty: true,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
e.preventDefault();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (e.key === 'Delete') {
|
|
199
|
+
// If a token is selected, remove it completely
|
|
200
|
+
if (tokenIndex !== null && lastestFormValueToArray.length) {
|
|
160
201
|
const filteredUniqueValues = Array.from(
|
|
161
202
|
new Set(
|
|
162
203
|
[...lastestFormValueToArray].filter(
|
|
163
|
-
|
|
204
|
+
(_, index) => index !== tokenIndex
|
|
164
205
|
)
|
|
165
206
|
)
|
|
166
207
|
);
|
|
@@ -170,9 +211,13 @@ const StackedPilledInput = React.forwardRef<
|
|
|
170
211
|
shouldDirty: true,
|
|
171
212
|
});
|
|
172
213
|
|
|
173
|
-
|
|
214
|
+
setTokenIndex(null);
|
|
215
|
+
e.preventDefault();
|
|
216
|
+
return;
|
|
174
217
|
}
|
|
218
|
+
}
|
|
175
219
|
|
|
220
|
+
if (!localValue.trim().length && lastestFormValueToArray.length) {
|
|
176
221
|
if (e.key === 'ArrowLeft') {
|
|
177
222
|
if (tokenIndex === 0) return;
|
|
178
223
|
|
|
@@ -290,12 +335,14 @@ const StackedPilledInput = React.forwardRef<
|
|
|
290
335
|
bg={disabled ? colors.fill.light.quaternary : '#ffffff'}
|
|
291
336
|
cursor={disabled ? 'not-allowed' : 'pointer'}
|
|
292
337
|
ref={inputWrapperRef}
|
|
293
|
-
h={isMobile ? '48px' : '26px'}
|
|
338
|
+
h={isMobile ? '48px' : scrollMode ? '26px' : 'auto'}
|
|
339
|
+
minH={isMobile ? '48px' : '26px'}
|
|
294
340
|
>
|
|
295
341
|
<Flex
|
|
296
342
|
h="100%"
|
|
297
343
|
alignItems="center"
|
|
298
|
-
overflowX=
|
|
344
|
+
overflowX={scrollMode ? 'scroll' : 'hidden'}
|
|
345
|
+
flexWrap={scrollMode ? 'nowrap' : 'wrap'}
|
|
299
346
|
overflowY="hidden"
|
|
300
347
|
maxWidth={isFocussed ? '80%' : '100%'}
|
|
301
348
|
style={{
|
|
@@ -308,26 +355,37 @@ const StackedPilledInput = React.forwardRef<
|
|
|
308
355
|
},
|
|
309
356
|
}}
|
|
310
357
|
ref={scrollRef}
|
|
311
|
-
zIndex={99}
|
|
312
358
|
onKeyDown={onHandleKeyDown}
|
|
313
359
|
>
|
|
314
360
|
{lastestFormValueToArray.length
|
|
315
361
|
? lastestFormValueToArray.map((label, index) => (
|
|
316
362
|
<Box
|
|
317
363
|
key={index}
|
|
318
|
-
mr="
|
|
364
|
+
mr="2px"
|
|
319
365
|
border={
|
|
320
366
|
tokenIndex === index
|
|
321
367
|
? `1px solid ${colors.border.focus}`
|
|
322
368
|
: 'none'
|
|
323
369
|
}
|
|
324
|
-
borderRadius="
|
|
325
|
-
onClick={
|
|
326
|
-
|
|
370
|
+
borderRadius="md"
|
|
371
|
+
onClick={e => {
|
|
372
|
+
e.stopPropagation();
|
|
373
|
+
// Don't change selection if clicking on already selected token
|
|
374
|
+
if (tokenIndex === index) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
setTokenIndex(index);
|
|
378
|
+
}}
|
|
379
|
+
width={scrollMode ? '100%' : 'auto'}
|
|
380
|
+
maxWidth={'100%'}
|
|
327
381
|
id={`${name}_token_${index}`}
|
|
382
|
+
cursor="default"
|
|
328
383
|
>
|
|
329
384
|
<Token
|
|
385
|
+
maxWidth={'100%'}
|
|
330
386
|
label={label}
|
|
387
|
+
showClose={true}
|
|
388
|
+
isSelected={tokenIndex === index}
|
|
331
389
|
onDelete={(e: any) => {
|
|
332
390
|
e.stopPropagation();
|
|
333
391
|
e.preventDefault();
|
|
@@ -343,6 +401,7 @@ const StackedPilledInput = React.forwardRef<
|
|
|
343
401
|
<Text
|
|
344
402
|
color={colors.label.secondary.light}
|
|
345
403
|
fontSize={isMobile ? '17px' : '13px'}
|
|
404
|
+
pointerEvents="none"
|
|
346
405
|
>
|
|
347
406
|
{placeholder}
|
|
348
407
|
</Text>
|
|
@@ -366,10 +425,6 @@ const StackedPilledInput = React.forwardRef<
|
|
|
366
425
|
onChange={handleOnChange}
|
|
367
426
|
ref={inputRef}
|
|
368
427
|
onFocus={() => setIsFocussed(true)}
|
|
369
|
-
onBlur={() => {
|
|
370
|
-
setIsFocussed(false);
|
|
371
|
-
return setTokenIndex(null);
|
|
372
|
-
}}
|
|
373
428
|
placeholder={
|
|
374
429
|
isMobile && label && lastestFormValueToArray.length === 0
|
|
375
430
|
? (label as string)
|
|
@@ -9,6 +9,9 @@ export interface TokenProps {
|
|
|
9
9
|
onDelete: any;
|
|
10
10
|
isMobile?: boolean;
|
|
11
11
|
truncateLength?: number;
|
|
12
|
+
showClose?: boolean;
|
|
13
|
+
maxWidth?: string | number;
|
|
14
|
+
isSelected?: boolean;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
// For v1 we are truncating the label at 15 characters to avoid overflow
|
|
@@ -17,33 +20,60 @@ const Token: React.FC<TokenProps> = ({
|
|
|
17
20
|
onDelete,
|
|
18
21
|
isMobile = false,
|
|
19
22
|
truncateLength = 15,
|
|
23
|
+
showClose = true,
|
|
24
|
+
maxWidth = 'auto',
|
|
25
|
+
isSelected = false,
|
|
20
26
|
}) => {
|
|
27
|
+
// in pixels
|
|
28
|
+
const closeBoxSize = isMobile ? 20 : 15;
|
|
29
|
+
const closeBoxGap = isMobile ? 4 : 2;
|
|
30
|
+
const closeBoxPadding = closeBoxSize + closeBoxGap;
|
|
21
31
|
return (
|
|
22
32
|
<Flex
|
|
23
33
|
key={label}
|
|
24
|
-
borderRadius={'
|
|
34
|
+
borderRadius={'md'}
|
|
25
35
|
backgroundColor="#7676801F"
|
|
26
36
|
alignItems="center"
|
|
27
37
|
width="fit-content"
|
|
28
38
|
w="auto"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
maxWidth={maxWidth}
|
|
40
|
+
h={'18px'}
|
|
41
|
+
px="4px"
|
|
32
42
|
py="2px"
|
|
43
|
+
pr={showClose ? closeBoxPadding : '4px'}
|
|
33
44
|
position="relative"
|
|
45
|
+
cursor="default"
|
|
34
46
|
>
|
|
35
47
|
<Text
|
|
36
48
|
whiteSpace="nowrap"
|
|
49
|
+
wordBreak="break-word"
|
|
37
50
|
color={colors.label.primary.light}
|
|
38
51
|
fontSize={isMobile ? '17px' : '13px'}
|
|
39
|
-
|
|
52
|
+
lineHeight={isMobile ? '17px' : '13px'}
|
|
53
|
+
pr="2px"
|
|
54
|
+
maxWidth={maxWidth}
|
|
55
|
+
overflow="hidden"
|
|
56
|
+
textOverflow="ellipsis"
|
|
40
57
|
>
|
|
41
58
|
{truncate(label.trim(), {
|
|
42
59
|
length: truncateLength,
|
|
43
60
|
omission: '...',
|
|
44
61
|
})}
|
|
45
62
|
</Text>
|
|
46
|
-
|
|
63
|
+
{showClose && (
|
|
64
|
+
<Flex
|
|
65
|
+
height="100%"
|
|
66
|
+
position="absolute"
|
|
67
|
+
top={0}
|
|
68
|
+
bottom={0}
|
|
69
|
+
right={0}
|
|
70
|
+
justifyContent="center"
|
|
71
|
+
alignItems="center"
|
|
72
|
+
cursor={isSelected ? 'default' : 'pointer'}
|
|
73
|
+
>
|
|
74
|
+
<Close boxSize={`${closeBoxSize}px`} onClick={onDelete} />
|
|
75
|
+
</Flex>
|
|
76
|
+
)}
|
|
47
77
|
</Flex>
|
|
48
78
|
);
|
|
49
79
|
};
|
|
@@ -53,6 +53,7 @@ export interface InputProps<T extends FieldValues = FieldValues>
|
|
|
53
53
|
loadingOptions?: boolean;
|
|
54
54
|
truncatePillLength?: number;
|
|
55
55
|
searchable?: boolean;
|
|
56
|
+
overflowMode?: 'scroll' | 'wrap';
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
/**
|
|
@@ -88,6 +89,7 @@ export function Input<T extends FieldValues>({
|
|
|
88
89
|
loadingOptions = false,
|
|
89
90
|
truncatePillLength,
|
|
90
91
|
searchable,
|
|
92
|
+
overflowMode = 'scroll',
|
|
91
93
|
}: InputProps<T>) {
|
|
92
94
|
function selectedInputField<T extends Element = Element>(
|
|
93
95
|
onChange: ((e: ChangeEvent<T>) => void) | ((v?: string) => void),
|
|
@@ -238,6 +240,7 @@ export function Input<T extends FieldValues>({
|
|
|
238
240
|
label={label}
|
|
239
241
|
separators={separators}
|
|
240
242
|
truncatePillLength={truncatePillLength}
|
|
243
|
+
mode={overflowMode}
|
|
241
244
|
/>
|
|
242
245
|
);
|
|
243
246
|
case 'switch':
|