@xqmsg/ui-core 0.15.4 → 0.16.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/banner/index.d.ts +1 -0
- package/dist/components/input/StackedSelect/{StackedSelect.d.ts → index.d.ts} +0 -0
- package/dist/components/input/components/dropdown/index.d.ts +1 -0
- package/dist/theme/components/button.d.ts +3 -0
- package/dist/theme/components/table.d.ts +6 -0
- package/dist/theme/styles.d.ts +1 -0
- package/dist/ui-core.cjs.development.js +245 -60
- 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 +247 -62
- package/dist/ui-core.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/banner/Banner.stories.tsx +32 -0
- package/src/components/banner/index.tsx +24 -13
- package/src/components/button/Button.stories.tsx +1 -1
- package/src/components/button/spinner/index.tsx +2 -3
- package/src/components/input/Input.stories.tsx +0 -1
- package/src/components/input/StackedMultiSelect/index.tsx +113 -21
- package/src/components/input/StackedSelect/index.tsx +236 -0
- package/src/components/input/components/dropdown/index.tsx +10 -3
- package/src/components/input/index.tsx +1 -1
- package/src/components/table/empty/index.tsx +1 -1
- package/src/theme/components/button.ts +1 -0
- package/src/theme/components/table.ts +6 -0
- package/src/theme/components/text.ts +1 -1
- package/src/theme/styles.ts +1 -0
- package/src/components/input/StackedSelect/StackedSelect.tsx +0 -127
package/package.json
CHANGED
|
@@ -31,6 +31,38 @@ const meta: Meta<BannerProps> = {
|
|
|
31
31
|
export default meta;
|
|
32
32
|
const Template: Story<BannerProps> = args => (
|
|
33
33
|
<>
|
|
34
|
+
<Box mb="20px">
|
|
35
|
+
<Banner
|
|
36
|
+
{...args}
|
|
37
|
+
type="condensed"
|
|
38
|
+
variant="positive"
|
|
39
|
+
message="Positive message."
|
|
40
|
+
/>
|
|
41
|
+
</Box>
|
|
42
|
+
<Box mb="20px">
|
|
43
|
+
<Banner
|
|
44
|
+
{...args}
|
|
45
|
+
type="condensed"
|
|
46
|
+
variant="warning"
|
|
47
|
+
message="Warning message."
|
|
48
|
+
/>
|
|
49
|
+
</Box>
|
|
50
|
+
<Box mb="20px">
|
|
51
|
+
<Banner
|
|
52
|
+
{...args}
|
|
53
|
+
type="condensed"
|
|
54
|
+
variant="error"
|
|
55
|
+
message="Error message."
|
|
56
|
+
/>
|
|
57
|
+
</Box>
|
|
58
|
+
<Box mb="20px">
|
|
59
|
+
<Banner
|
|
60
|
+
{...args}
|
|
61
|
+
type="condensed"
|
|
62
|
+
variant="neutral"
|
|
63
|
+
message="Neutral message."
|
|
64
|
+
/>
|
|
65
|
+
</Box>
|
|
34
66
|
<Box mb="20px">
|
|
35
67
|
<Banner
|
|
36
68
|
{...args}
|
|
@@ -13,6 +13,7 @@ export interface BannerProps {
|
|
|
13
13
|
message: ReactNode;
|
|
14
14
|
buttonText?: string;
|
|
15
15
|
onClick?: () => void;
|
|
16
|
+
type?: 'condensed' | 'expanded';
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -23,6 +24,7 @@ export const Banner: React.FC<BannerProps> = ({
|
|
|
23
24
|
message,
|
|
24
25
|
buttonText,
|
|
25
26
|
onClick,
|
|
27
|
+
type = 'expanded',
|
|
26
28
|
}) => {
|
|
27
29
|
const Icon = useMemo(() => {
|
|
28
30
|
switch (variant) {
|
|
@@ -42,19 +44,28 @@ export const Banner: React.FC<BannerProps> = ({
|
|
|
42
44
|
return (
|
|
43
45
|
<Alert variant={variant}>
|
|
44
46
|
<AlertDescription>
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
<Flex
|
|
48
|
+
flexDirection={type === 'condensed' ? 'row' : 'column'}
|
|
49
|
+
alignItems={type === 'condensed' ? 'center' : ''}
|
|
50
|
+
>
|
|
51
|
+
<Box pr="8px">{Icon}</Box>
|
|
52
|
+
<Box pt={type === 'condensed' ? 0 : '8px'}> {message}</Box>
|
|
53
|
+
{onClick && buttonText && (
|
|
54
|
+
<Flex
|
|
55
|
+
ml={type === 'condensed' ? 'auto' : ''}
|
|
56
|
+
pt={type === 'condensed' ? 0 : '8px'}
|
|
57
|
+
justifyContent={type === 'condensed' ? 'flex-end' : 'flex-end'}
|
|
58
|
+
>
|
|
59
|
+
<Button
|
|
60
|
+
variant="secondary"
|
|
61
|
+
onClick={onClick}
|
|
62
|
+
text={buttonText}
|
|
63
|
+
width="variable"
|
|
64
|
+
ariaLabel="banner button"
|
|
65
|
+
/>
|
|
66
|
+
</Flex>
|
|
67
|
+
)}
|
|
68
|
+
</Flex>
|
|
58
69
|
</AlertDescription>
|
|
59
70
|
</Alert>
|
|
60
71
|
);
|
|
@@ -37,7 +37,7 @@ const meta: Meta<ButtonProps> = {
|
|
|
37
37
|
};
|
|
38
38
|
export default meta;
|
|
39
39
|
const Template: Story<ButtonProps> = args => (
|
|
40
|
-
<Flex flexDir="column" height="
|
|
40
|
+
<Flex flexDir="column" height="150px" justifyContent="space-between">
|
|
41
41
|
<Button {...args} text="Primary Fixed" variant="primary" width="fixed" />
|
|
42
42
|
<Button
|
|
43
43
|
{...args}
|
|
@@ -15,14 +15,13 @@ export const SpinnerButton: React.FC<SpinnerButtonProps> = ({
|
|
|
15
15
|
onClick,
|
|
16
16
|
type,
|
|
17
17
|
ariaLabel,
|
|
18
|
-
variant = '
|
|
19
|
-
|
|
18
|
+
variant = 'primary',
|
|
20
19
|
disabled,
|
|
21
20
|
className,
|
|
22
21
|
}) => {
|
|
23
22
|
return (
|
|
24
23
|
<Button
|
|
25
|
-
spinner={<Spinner size={'
|
|
24
|
+
spinner={<Spinner size={'sm'} />}
|
|
26
25
|
isLoading={isLoading}
|
|
27
26
|
onClick={onClick}
|
|
28
27
|
type={type}
|
|
@@ -5,7 +5,6 @@ import { Input, InputProps } from '.';
|
|
|
5
5
|
import { useFormHandler } from '../form/hooks/useFormHandler';
|
|
6
6
|
import * as Yup from 'yup';
|
|
7
7
|
import { Form } from '../form';
|
|
8
|
-
import { Box } from '@chakra-ui/react';
|
|
9
8
|
|
|
10
9
|
const meta: Meta<InputProps<StoryFormSchema>> = {
|
|
11
10
|
title: 'Input example',
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
KeyboardEventHandler,
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
2
8
|
import { Box, Flex, Text, Image, Input } from '@chakra-ui/react';
|
|
9
|
+
import { debounce } from 'lodash';
|
|
3
10
|
import {
|
|
4
11
|
FieldOption,
|
|
5
12
|
FieldOptions,
|
|
@@ -44,7 +51,13 @@ const StackedMultiSelect = React.forwardRef<
|
|
|
44
51
|
const [localOptions, setLocalOptions] = useState<FieldOptions>(options);
|
|
45
52
|
const [isFocussed, setIsFocussed] = useState(false);
|
|
46
53
|
const [shouldSideScroll, setShouldSideScroll] = useState(false);
|
|
54
|
+
const [optionIndex, setOptionIndex] = useState<number | null>(null);
|
|
55
|
+
|
|
47
56
|
const [position, setPosition] = useState<'top' | 'bottom'>('top');
|
|
57
|
+
const [searchValue, setSearchValue] = useState('');
|
|
58
|
+
const [debouncedSearchValue, setDebouncedSearchValue] = useState('');
|
|
59
|
+
|
|
60
|
+
console.log({ searchValue, debouncedSearchValue });
|
|
48
61
|
|
|
49
62
|
const boundingClientRect = dropdownRef.current?.getBoundingClientRect() as DOMRect;
|
|
50
63
|
|
|
@@ -122,27 +135,105 @@ const StackedMultiSelect = React.forwardRef<
|
|
|
122
135
|
);
|
|
123
136
|
};
|
|
124
137
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
138
|
+
const handleOnKeyDown: KeyboardEventHandler<HTMLInputElement> = e => {
|
|
139
|
+
const initialOptionIndex = options[0].value === 'section_header' ? 1 : 0;
|
|
140
|
+
|
|
141
|
+
if (
|
|
142
|
+
!isFocussed &&
|
|
143
|
+
(e.key === 'Enter' || e.key === 'ArrowUp' || e.key === 'ArrowDown')
|
|
144
|
+
) {
|
|
145
|
+
setIsFocussed(true);
|
|
146
|
+
return setOptionIndex(initialOptionIndex);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (isFocussed) {
|
|
150
|
+
if (
|
|
151
|
+
optionIndex === null &&
|
|
152
|
+
(e.key === 'Enter' || e.key === 'ArrowUp' || e.key === 'ArrowDown')
|
|
153
|
+
) {
|
|
154
|
+
return setOptionIndex(initialOptionIndex);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (e.key === 'ArrowUp' && optionIndex !== null && optionIndex > 0) {
|
|
158
|
+
const incrementValue =
|
|
159
|
+
localOptions[optionIndex - 1] &&
|
|
160
|
+
localOptions[optionIndex - 1].value === 'section_header'
|
|
161
|
+
? 2
|
|
162
|
+
: 1;
|
|
163
|
+
setOptionIndex(optionIndex - incrementValue);
|
|
164
|
+
|
|
165
|
+
return dropdownMenuRef.current?.scrollTo({
|
|
166
|
+
top: optionIndex * 24,
|
|
167
|
+
behavior: 'smooth',
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (
|
|
172
|
+
e.key === 'ArrowDown' &&
|
|
173
|
+
optionIndex !== null &&
|
|
174
|
+
optionIndex < localOptions.length
|
|
175
|
+
) {
|
|
176
|
+
const incrementValue =
|
|
177
|
+
localOptions[optionIndex + 1] &&
|
|
178
|
+
localOptions[optionIndex + 1].value === 'section_header'
|
|
179
|
+
? 2
|
|
180
|
+
: 1;
|
|
181
|
+
setOptionIndex(optionIndex + incrementValue);
|
|
182
|
+
|
|
183
|
+
return dropdownMenuRef.current?.scrollTo({
|
|
184
|
+
top: optionIndex * 24,
|
|
185
|
+
behavior: 'smooth',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (e.key === 'Enter' && optionIndex !== null) {
|
|
190
|
+
const option = localOptions.find((_, idx) => optionIndex === idx);
|
|
191
|
+
if (!option) return;
|
|
134
192
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
193
|
+
handleChange(option);
|
|
194
|
+
|
|
195
|
+
return setIsFocussed(false);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (e.key === 'Tab') {
|
|
199
|
+
return setIsFocussed(false);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return update(debouncedSearchValue.concat(e.key));
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
if (searchValue.length) {
|
|
208
|
+
const idx = options.findIndex(
|
|
209
|
+
option =>
|
|
210
|
+
option.label.substring(0, searchValue.length).toLowerCase() ===
|
|
211
|
+
searchValue.toLowerCase()
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
dropdownMenuRef.current?.scrollTo({
|
|
215
|
+
top: idx * 24,
|
|
216
|
+
behavior: 'smooth',
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
setSearchValue('');
|
|
220
|
+
setDebouncedSearchValue('');
|
|
221
|
+
}
|
|
222
|
+
}, [options, searchValue]);
|
|
223
|
+
|
|
224
|
+
const updateSearchValue = useMemo(() => {
|
|
225
|
+
return debounce(val => {
|
|
226
|
+
setSearchValue(val);
|
|
227
|
+
}, 1000);
|
|
228
|
+
}, []);
|
|
229
|
+
|
|
230
|
+
const update = (value: string) => {
|
|
231
|
+
updateSearchValue(value);
|
|
232
|
+
setDebouncedSearchValue(value);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<Box ref={dropdownRef} position="relative" onKeyDown={handleOnKeyDown}>
|
|
146
237
|
<Flex
|
|
147
238
|
fontSize="13px"
|
|
148
239
|
h="26px"
|
|
@@ -218,6 +309,7 @@ const StackedMultiSelect = React.forwardRef<
|
|
|
218
309
|
onSelectItem={option => handleChange(option)}
|
|
219
310
|
options={localOptions}
|
|
220
311
|
position={position}
|
|
312
|
+
optionIndex={optionIndex}
|
|
221
313
|
/>
|
|
222
314
|
)}
|
|
223
315
|
</Box>
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
KeyboardEventHandler,
|
|
3
|
+
useEffect,
|
|
4
|
+
useRef,
|
|
5
|
+
useMemo,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
8
|
+
import {
|
|
9
|
+
Box,
|
|
10
|
+
Image,
|
|
11
|
+
Input,
|
|
12
|
+
InputGroup,
|
|
13
|
+
InputRightElement,
|
|
14
|
+
} from '@chakra-ui/react';
|
|
15
|
+
import { FieldOptions } from '../InputTypes';
|
|
16
|
+
import { StackedInputProps } from '../StackedInput/StackedInput';
|
|
17
|
+
import colors from '../../../theme/foundations/colors';
|
|
18
|
+
import { UseFormSetValue, FieldValues, Control } from 'react-hook-form';
|
|
19
|
+
import SubtractIcon from './assets/svg/subtract.svg';
|
|
20
|
+
import { Dropdown } from '../components/dropdown';
|
|
21
|
+
import useDidMountEffect from '../../../hooks/useDidMountEffect';
|
|
22
|
+
import { useOnClickOutside } from '../../../hooks/useOnOutsideClick';
|
|
23
|
+
import { debounce } from 'lodash';
|
|
24
|
+
|
|
25
|
+
export interface StackedSelectProps extends StackedInputProps {
|
|
26
|
+
options: FieldOptions;
|
|
27
|
+
setValue: UseFormSetValue<FieldValues>;
|
|
28
|
+
control: Control<FieldValues, any>;
|
|
29
|
+
handleOnChange: (value?: string) => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* A functional React component utilized to render the `StackedSelect` component.
|
|
34
|
+
*/
|
|
35
|
+
const StackedSelect = React.forwardRef<HTMLInputElement, StackedSelectProps>(
|
|
36
|
+
(
|
|
37
|
+
{
|
|
38
|
+
isRequired,
|
|
39
|
+
options,
|
|
40
|
+
name,
|
|
41
|
+
setValue,
|
|
42
|
+
handleOnChange,
|
|
43
|
+
disabled,
|
|
44
|
+
value,
|
|
45
|
+
...props
|
|
46
|
+
},
|
|
47
|
+
_ref
|
|
48
|
+
) => {
|
|
49
|
+
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
50
|
+
const dropdownMenuRef = useRef<HTMLDivElement>(null);
|
|
51
|
+
|
|
52
|
+
const [isFocussed, setIsFocussed] = useState(false);
|
|
53
|
+
const [selectedOption, setSelectedOption] = useState(
|
|
54
|
+
options.find(option => option.value === value)?.label ?? ''
|
|
55
|
+
);
|
|
56
|
+
const [optionIndex, setOptionIndex] = useState<number | null>(null);
|
|
57
|
+
const [position, setPosition] = useState<'top' | 'bottom'>('top');
|
|
58
|
+
const [searchValue, setSearchValue] = useState('');
|
|
59
|
+
const [debouncedSearchValue, setDebouncedSearchValue] = useState('');
|
|
60
|
+
|
|
61
|
+
const boundingClientRect = dropdownRef.current?.getBoundingClientRect() as DOMRect;
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const boundingClientRect = dropdownRef.current?.getBoundingClientRect() as DOMRect;
|
|
65
|
+
|
|
66
|
+
if (window.innerHeight - (boundingClientRect?.y + 240) >= 0) {
|
|
67
|
+
setPosition('top');
|
|
68
|
+
} else {
|
|
69
|
+
setPosition('bottom');
|
|
70
|
+
}
|
|
71
|
+
}, [boundingClientRect]);
|
|
72
|
+
|
|
73
|
+
useDidMountEffect(() => {
|
|
74
|
+
setSelectedOption(
|
|
75
|
+
options.find(option => option.value === value)?.label ?? ''
|
|
76
|
+
);
|
|
77
|
+
}, [value]);
|
|
78
|
+
|
|
79
|
+
useOnClickOutside(dropdownRef, () => setIsFocussed(false));
|
|
80
|
+
|
|
81
|
+
const handleOnSelectItem = (option: {
|
|
82
|
+
label: string;
|
|
83
|
+
value: string;
|
|
84
|
+
sortValue: number;
|
|
85
|
+
}) => {
|
|
86
|
+
if (handleOnChange) {
|
|
87
|
+
handleOnChange(option.value);
|
|
88
|
+
}
|
|
89
|
+
setValue(name as string, option.value);
|
|
90
|
+
setSelectedOption(option.label);
|
|
91
|
+
setIsFocussed(false);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const handleOnKeyDown: KeyboardEventHandler<HTMLInputElement> = e => {
|
|
95
|
+
const initialOptionIndex = options[0].value === 'section_header' ? 1 : 0;
|
|
96
|
+
|
|
97
|
+
if (
|
|
98
|
+
!isFocussed &&
|
|
99
|
+
(e.key === 'Enter' || e.key === 'ArrowUp' || e.key === 'ArrowDown')
|
|
100
|
+
) {
|
|
101
|
+
setIsFocussed(true);
|
|
102
|
+
return setOptionIndex(initialOptionIndex);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isFocussed) {
|
|
106
|
+
if (
|
|
107
|
+
optionIndex === null &&
|
|
108
|
+
(e.key === 'Enter' || e.key === 'ArrowUp' || e.key === 'ArrowDown')
|
|
109
|
+
) {
|
|
110
|
+
return setOptionIndex(initialOptionIndex);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (e.key === 'ArrowUp' && optionIndex !== null && optionIndex > 0) {
|
|
114
|
+
const incrementValue =
|
|
115
|
+
options[optionIndex - 1] &&
|
|
116
|
+
options[optionIndex - 1].value === 'section_header'
|
|
117
|
+
? 2
|
|
118
|
+
: 1;
|
|
119
|
+
setOptionIndex(optionIndex - incrementValue);
|
|
120
|
+
|
|
121
|
+
return dropdownMenuRef.current?.scrollTo({
|
|
122
|
+
top: optionIndex * 24,
|
|
123
|
+
behavior: 'smooth',
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (
|
|
128
|
+
e.key === 'ArrowDown' &&
|
|
129
|
+
optionIndex !== null &&
|
|
130
|
+
optionIndex < options.length
|
|
131
|
+
) {
|
|
132
|
+
const incrementValue =
|
|
133
|
+
options[optionIndex + 1] &&
|
|
134
|
+
options[optionIndex + 1].value === 'section_header'
|
|
135
|
+
? 2
|
|
136
|
+
: 1;
|
|
137
|
+
setOptionIndex(optionIndex + incrementValue);
|
|
138
|
+
|
|
139
|
+
return dropdownMenuRef.current?.scrollTo({
|
|
140
|
+
top: optionIndex * 24,
|
|
141
|
+
behavior: 'smooth',
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (e.key === 'Enter' && optionIndex !== null) {
|
|
146
|
+
const option = options.find((_, idx) => optionIndex === idx);
|
|
147
|
+
if (!option) return;
|
|
148
|
+
|
|
149
|
+
if (handleOnChange) {
|
|
150
|
+
handleOnChange(option.value);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
setSelectedOption(option?.label);
|
|
154
|
+
setValue(name as string, option.value, {
|
|
155
|
+
shouldDirty: true,
|
|
156
|
+
shouldValidate: true,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return setIsFocussed(false);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (e.key === 'Tab') {
|
|
163
|
+
return setIsFocussed(false);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
useEffect(() => {
|
|
169
|
+
if (searchValue.length) {
|
|
170
|
+
const idx = options.findIndex(
|
|
171
|
+
option =>
|
|
172
|
+
option.label.substring(0, searchValue.length).toLowerCase() ===
|
|
173
|
+
searchValue.toLowerCase()
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
dropdownMenuRef.current?.scrollTo({
|
|
177
|
+
top: idx * 24,
|
|
178
|
+
behavior: 'smooth',
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
setSearchValue('');
|
|
182
|
+
setDebouncedSearchValue('');
|
|
183
|
+
}
|
|
184
|
+
}, [options, searchValue]);
|
|
185
|
+
|
|
186
|
+
const updateSearchValue = useMemo(() => {
|
|
187
|
+
return debounce(val => {
|
|
188
|
+
setSearchValue(val);
|
|
189
|
+
}, 1000);
|
|
190
|
+
}, []);
|
|
191
|
+
|
|
192
|
+
const update = (value: string) => {
|
|
193
|
+
updateSearchValue(value);
|
|
194
|
+
setDebouncedSearchValue(value);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<Box ref={dropdownRef} position="relative">
|
|
199
|
+
<InputGroup>
|
|
200
|
+
<Input
|
|
201
|
+
isRequired={isRequired}
|
|
202
|
+
{...props}
|
|
203
|
+
ref={_ref}
|
|
204
|
+
onClick={() => setIsFocussed(!isFocussed)}
|
|
205
|
+
cursor="pointer"
|
|
206
|
+
color="transparent"
|
|
207
|
+
fontSize="13px"
|
|
208
|
+
textShadow={`0 0 0 ${colors.label.primary.light}`}
|
|
209
|
+
value={selectedOption}
|
|
210
|
+
disabled={disabled}
|
|
211
|
+
autoComplete="off"
|
|
212
|
+
onChange={e => update(debouncedSearchValue.concat(e.target.value))}
|
|
213
|
+
onKeyDown={handleOnKeyDown}
|
|
214
|
+
/>
|
|
215
|
+
<InputRightElement
|
|
216
|
+
cursor={disabled ? 'not-allowed' : 'pointer'}
|
|
217
|
+
onClick={() => !disabled && setIsFocussed(!isFocussed)}
|
|
218
|
+
>
|
|
219
|
+
<Image src={SubtractIcon} alt="subtract" boxSize="16px" />
|
|
220
|
+
</InputRightElement>
|
|
221
|
+
</InputGroup>
|
|
222
|
+
{isFocussed && (
|
|
223
|
+
<Dropdown
|
|
224
|
+
position={position}
|
|
225
|
+
dropdownRef={dropdownMenuRef}
|
|
226
|
+
onSelectItem={option => handleOnSelectItem(option)}
|
|
227
|
+
options={options}
|
|
228
|
+
optionIndex={optionIndex}
|
|
229
|
+
/>
|
|
230
|
+
)}
|
|
231
|
+
</Box>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
export default StackedSelect;
|
|
@@ -8,6 +8,7 @@ export interface DropdownProps {
|
|
|
8
8
|
options: FieldOptions;
|
|
9
9
|
dropdownRef: RefObject<HTMLDivElement>;
|
|
10
10
|
position: 'top' | 'bottom';
|
|
11
|
+
optionIndex?: number | null;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -18,6 +19,7 @@ export const Dropdown: React.FC<DropdownProps> = ({
|
|
|
18
19
|
options,
|
|
19
20
|
dropdownRef,
|
|
20
21
|
position,
|
|
22
|
+
optionIndex,
|
|
21
23
|
}) => {
|
|
22
24
|
const DropdownContent = useMemo(() => {
|
|
23
25
|
return options.map((option, idx) => (
|
|
@@ -55,22 +57,27 @@ export const Dropdown: React.FC<DropdownProps> = ({
|
|
|
55
57
|
px="8px"
|
|
56
58
|
py="4px"
|
|
57
59
|
width="100%"
|
|
58
|
-
color={
|
|
60
|
+
color={
|
|
61
|
+
optionIndex === idx
|
|
62
|
+
? colors.label.primary.dark
|
|
63
|
+
: colors.label.primary.light
|
|
64
|
+
}
|
|
59
65
|
_hover={{
|
|
60
66
|
color: colors.label.primary.dark,
|
|
61
67
|
bg: colors.fill.action,
|
|
62
68
|
borderRadius: '4px',
|
|
63
69
|
width: '100%',
|
|
64
70
|
}}
|
|
65
|
-
bg=
|
|
71
|
+
bg={optionIndex === idx ? colors.fill.action : 'inherit'}
|
|
66
72
|
whiteSpace="nowrap"
|
|
73
|
+
id={option.value}
|
|
67
74
|
>
|
|
68
75
|
{option.label}
|
|
69
76
|
</Box>
|
|
70
77
|
)}
|
|
71
78
|
</>
|
|
72
79
|
));
|
|
73
|
-
}, [onSelectItem, options]);
|
|
80
|
+
}, [onSelectItem, optionIndex, options]);
|
|
74
81
|
|
|
75
82
|
return (
|
|
76
83
|
<Flex
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import StackedCheckBox from './StackedCheckbox/StackedCheckbox';
|
|
3
3
|
import StackedInput from './StackedInput/StackedInput';
|
|
4
4
|
import StackedRadioGroup from './StackedRadio/StackedRadioGroup';
|
|
5
|
-
import StackedSelect from './StackedSelect
|
|
5
|
+
import StackedSelect from './StackedSelect';
|
|
6
6
|
import StackedTextarea from './StackedTextarea/StackedTextarea';
|
|
7
7
|
import { FieldOptions, ValidationProps, InputType } from './InputTypes';
|
|
8
8
|
import {
|
|
@@ -11,8 +11,12 @@ const baseStyle = {
|
|
|
11
11
|
},
|
|
12
12
|
tr: {
|
|
13
13
|
fontSize: '13px',
|
|
14
|
+
h: '26px',
|
|
15
|
+
lineHeight: 'normal',
|
|
14
16
|
_odd: {
|
|
15
17
|
td: {
|
|
18
|
+
h: '26px ',
|
|
19
|
+
lineHeight: 'normal',
|
|
16
20
|
bg: colors.fill.light.tertiary,
|
|
17
21
|
_first: {
|
|
18
22
|
borderTopLeftRadius: 'md',
|
|
@@ -27,6 +31,8 @@ const baseStyle = {
|
|
|
27
31
|
},
|
|
28
32
|
td: {
|
|
29
33
|
padding: '5px 8px !important',
|
|
34
|
+
lineHeight: 'normal',
|
|
35
|
+
h: '26px',
|
|
30
36
|
},
|
|
31
37
|
};
|
|
32
38
|
|
|
@@ -7,7 +7,7 @@ const baseStyle: Partial<TextProps> = {
|
|
|
7
7
|
fontWeight: typography.fontWeights.normal,
|
|
8
8
|
fontFamily: typography.fonts.base,
|
|
9
9
|
fontSize: typography.fontSizes.sm,
|
|
10
|
-
lineHeight: typography.lineHeights.
|
|
10
|
+
lineHeight: typography.lineHeights.normal,
|
|
11
11
|
letterSpacing: typography.letterSpacings.wide,
|
|
12
12
|
};
|
|
13
13
|
|