@xqmsg/ui-core 0.14.5 → 0.15.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.
Files changed (33) hide show
  1. package/dist/components/input/StackedMultiSelect/index.d.ts +0 -1
  2. package/dist/components/input/StackedPilledInput/index.d.ts +0 -1
  3. package/dist/components/input/components/dropdown/index.d.ts +3 -1
  4. package/dist/theme/components/button.d.ts +10 -8
  5. package/dist/theme/components/input.d.ts +2 -0
  6. package/dist/theme/components/select.d.ts +2 -0
  7. package/dist/theme/components/table.d.ts +1 -0
  8. package/dist/theme/components/textarea.d.ts +3 -1
  9. package/dist/ui-core.cjs.development.js +319 -169
  10. package/dist/ui-core.cjs.development.js.map +1 -1
  11. package/dist/ui-core.cjs.production.min.js +1 -1
  12. package/dist/ui-core.cjs.production.min.js.map +1 -1
  13. package/dist/ui-core.esm.js +320 -170
  14. package/dist/ui-core.esm.js.map +1 -1
  15. package/package.json +1 -1
  16. package/src/components/banner/index.tsx +7 -15
  17. package/src/components/button/Button.stories.tsx +15 -5
  18. package/src/components/button/index.tsx +2 -2
  19. package/src/components/input/Input.stories.tsx +95 -53
  20. package/src/components/input/StackedMultiSelect/index.tsx +187 -144
  21. package/src/components/input/StackedPilledInput/index.tsx +217 -225
  22. package/src/components/input/StackedSelect/StackedSelect.tsx +36 -2
  23. package/src/components/input/StackedSwitch/index.tsx +7 -1
  24. package/src/components/input/StackedTextarea/StackedTextarea.tsx +1 -1
  25. package/src/components/input/components/dropdown/index.tsx +23 -6
  26. package/src/components/input/components/token/index.tsx +11 -6
  27. package/src/components/input/index.tsx +0 -1
  28. package/src/theme/components/button.ts +10 -10
  29. package/src/theme/components/input.ts +1 -0
  30. package/src/theme/components/table.ts +1 -0
  31. package/src/theme/components/textarea.ts +4 -1
  32. package/dist/components/input/StackedMultiSelect/components/MultiValue/index.d.ts +0 -10
  33. package/src/components/input/StackedMultiSelect/components/MultiValue/index.tsx +0 -21
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.14.5",
2
+ "version": "0.15.1",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -1,17 +1,10 @@
1
1
  import React, { ReactNode, useMemo } from 'react';
2
- import {
3
- Alert,
4
- AlertDescription,
5
- Box,
6
- Button,
7
- Flex,
8
- Image,
9
- } from '@chakra-ui/react';
2
+ import { Alert, AlertDescription, Box, Flex, Image } from '@chakra-ui/react';
10
3
  import ErrorIcon from './assets/svg/error.svg';
11
4
  import PositiveIcon from './assets/svg/positive.svg';
12
5
  import NeutralIcon from './assets/svg/neutral.svg';
13
6
  import WarningIcon from './assets/svg/warning.svg';
14
- import colors from '../../../src/theme/foundations/colors';
7
+ import { Button } from '../button';
15
8
 
16
9
  export type BannerVariant = 'positive' | 'warning' | 'error' | 'neutral';
17
10
 
@@ -54,13 +47,12 @@ export const Banner: React.FC<BannerProps> = ({
54
47
  {onClick && buttonText && (
55
48
  <Flex pt="8px" justifyContent="flex-end">
56
49
  <Button
57
- size="sm"
58
- bg="white"
59
- color={colors.fill.action}
50
+ variant="secondary"
60
51
  onClick={onClick}
61
- >
62
- {buttonText}
63
- </Button>
52
+ text={buttonText}
53
+ width="variable"
54
+ ariaLabel="banner button"
55
+ />
64
56
  </Flex>
65
57
  )}
66
58
  </AlertDescription>
@@ -38,13 +38,23 @@ const meta: Meta<ButtonProps> = {
38
38
  export default meta;
39
39
  const Template: Story<ButtonProps> = args => (
40
40
  <Flex flexDir="column" height="200px" justifyContent="space-between">
41
- <Button {...args} text="Solid Fixed" variant="solid" width="fixed" />
42
- <Button {...args} text="Outline Fixed" variant="outline" width="fixed" />
43
- <Button {...args} text="Solid Variable" variant="solid" width="variable" />
41
+ <Button {...args} text="Primary Fixed" variant="primary" width="fixed" />
44
42
  <Button
45
43
  {...args}
46
- text="Outline Variable"
47
- variant="outline"
44
+ text="Secondary Fixed"
45
+ variant="secondary"
46
+ width="fixed"
47
+ />
48
+ <Button
49
+ {...args}
50
+ text="Primary Variable"
51
+ variant="primary"
52
+ width="variable"
53
+ />
54
+ <Button
55
+ {...args}
56
+ text="Secondary Variable"
57
+ variant="secondary"
48
58
  width="variable"
49
59
  />
50
60
  </Flex>
@@ -21,7 +21,7 @@ export const Button: React.FC<ButtonProps> = ({
21
21
  text,
22
22
  type = 'button',
23
23
  ariaLabel,
24
- variant = 'solid',
24
+ variant = 'primary',
25
25
  disabled,
26
26
  className,
27
27
  width,
@@ -34,7 +34,7 @@ export const Button: React.FC<ButtonProps> = ({
34
34
  disabled={disabled}
35
35
  aria-label={ariaLabel}
36
36
  className={className}
37
- width={width === 'variable' ? '100%' : 'fit-content'}
37
+ width={width === 'fixed' ? '100%' : 'fit-content'}
38
38
  >
39
39
  {text}
40
40
  </ChakraButton>
@@ -5,6 +5,7 @@ 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';
8
9
 
9
10
  const meta: Meta<InputProps<StoryFormSchema>> = {
10
11
  title: 'Input example',
@@ -96,60 +97,76 @@ const Template: Story<InputProps<StoryFormSchema>> = args => {
96
97
 
97
98
  const { form } = formHandler;
98
99
 
99
- console.log(form.formState.errors);
100
-
101
100
  return (
102
101
  <Form formHandler={formHandler}>
103
- <Input
104
- {...args}
105
- inputType="multi-select"
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}
111
- name="prop5"
112
- />
113
- <Input
114
- {...args}
115
- inputType="select"
116
- setValue={form.setValue}
117
- name="prop4"
118
- />
119
- <Input
120
- {...args}
121
- inputType="text"
122
- name="prop3"
123
- onChange={e => form.setValue('prop3', e.target.value)}
124
- />
125
- <Input
126
- {...args}
127
- inputType="textarea"
128
- name="prop2"
129
- onChange={e => form.setValue('prop2', e.target.value)}
130
- />
131
- <Input
132
- {...args}
133
- name="prop"
134
- inputType="pilled-text"
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}
140
- />
141
- <Input
142
- {...args}
143
- name="prop6"
144
- inputType="switch"
145
- setValue={form.setValue}
146
- />
147
- <Input
148
- {...args}
149
- name="prop6"
150
- inputType="checkbox"
151
- setValue={form.setValue}
152
- />
102
+ <Box overflowY={'auto'} height="100%" width="100px" overflow="visible">
103
+ <Input
104
+ {...args}
105
+ inputType="multi-select"
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}
111
+ name="prop5"
112
+ />
113
+ <Input
114
+ {...args}
115
+ inputType="text"
116
+ name="prop3"
117
+ onChange={e => form.setValue('prop3', e.target.value)}
118
+ />
119
+ <Input
120
+ {...args}
121
+ inputType="textarea"
122
+ name="prop2"
123
+ onChange={e => form.setValue('prop2', e.target.value)}
124
+ />
125
+ <Input
126
+ {...args}
127
+ name="prop"
128
+ inputType="pilled-text"
129
+ setValue={form.setValue}
130
+ setError={form.setError}
131
+ clearErrors={form.clearErrors}
132
+ isInvalid={!!form.formState.errors['prop']?.message}
133
+ errorText={form.formState.errors['prop']?.message}
134
+ />
135
+ <Input
136
+ {...args}
137
+ inputType="select"
138
+ setValue={form.setValue}
139
+ name="prop4"
140
+ />
141
+ <Input
142
+ {...args}
143
+ name="prop6"
144
+ inputType="switch"
145
+ setValue={form.setValue}
146
+ />
147
+ <Input
148
+ {...args}
149
+ name="prop6"
150
+ inputType="checkbox"
151
+ setValue={form.setValue}
152
+ />{' '}
153
+ <Input
154
+ {...args}
155
+ inputType="multi-select"
156
+ setValue={form.setValue}
157
+ setError={form.setError}
158
+ clearErrors={form.clearErrors}
159
+ isInvalid={!!form.formState.errors['prop5']?.message}
160
+ errorText={form.formState.errors['prop5']?.message}
161
+ name="prop5"
162
+ />
163
+ <Input
164
+ {...args}
165
+ inputType="select"
166
+ setValue={form.setValue}
167
+ name="prop4"
168
+ />
169
+ </Box>
153
170
  </Form>
154
171
  );
155
172
  };
@@ -177,7 +194,7 @@ Default.args = {
177
194
  },
178
195
  {
179
196
  value: 'value4',
180
- label: 'Value 4',
197
+ label: 'Value 4 Which is very long',
181
198
  sortValue: 4,
182
199
  },
183
200
  { value: 'section_header', label: 'Section 2', sortValue: 5 },
@@ -191,6 +208,31 @@ Default.args = {
191
208
  label: 'Value 6',
192
209
  sortValue: 7,
193
210
  },
211
+ {
212
+ value: 'value7',
213
+ label: 'Value 7',
214
+ sortValue: 8,
215
+ },
216
+ {
217
+ value: 'value8',
218
+ label: 'Value 8',
219
+ sortValue: 9,
220
+ },
221
+ {
222
+ value: 'value9',
223
+ label: 'Value 9',
224
+ sortValue: 10,
225
+ },
226
+ {
227
+ value: 'value10',
228
+ label: 'Balue 10',
229
+ sortValue: 11,
230
+ },
231
+ {
232
+ value: 'value11',
233
+ label: 'C 11',
234
+ sortValue: 12,
235
+ },
194
236
  ],
195
237
  isRequired: true,
196
238
  isInvalid: false,
@@ -1,5 +1,12 @@
1
1
  import React, { useEffect, useRef, useState } from 'react';
2
- import { Box, Flex, Text, Image, useOutsideClick } from '@chakra-ui/react';
2
+ import {
3
+ Box,
4
+ Flex,
5
+ Text,
6
+ Image,
7
+ useOutsideClick,
8
+ Input,
9
+ } from '@chakra-ui/react';
3
10
  import {
4
11
  FieldOption,
5
12
  FieldOptions,
@@ -24,8 +31,6 @@ export interface StackedMultiSelectProps extends ReactSelectFieldProps {
24
31
  setError: UseFormSetError<FieldValues>;
25
32
  clearErrors: UseFormClearErrors<FieldValues>;
26
33
  control: Control<FieldValues, any>;
27
- // Number of allowed options
28
- maxLength?: number;
29
34
  }
30
35
 
31
36
  /**
@@ -34,160 +39,198 @@ export interface StackedMultiSelectProps extends ReactSelectFieldProps {
34
39
  const StackedMultiSelect = React.forwardRef<
35
40
  HTMLInputElement,
36
41
  StackedMultiSelectProps
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
- },
69
- });
70
-
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`,
42
+ >(({ options, setValue, control, name, placeholder, disabled }, _ref) => {
43
+ const watchedValue = useWatch({ control, name: name as string });
44
+ const dropdownRef = useRef<HTMLDivElement>(null);
45
+ const dropdownMenuRef = useRef<HTMLDivElement>(null);
46
+ const scrollRef = useRef<HTMLDivElement>(null);
47
+ const inputRef = useRef<HTMLInputElement>(null);
48
+
49
+ const [localValues, setLocalValues] = useState<FieldOptions>([]);
50
+ const [localOptions, setLocalOptions] = useState<FieldOptions>(options);
51
+ const [isFocussed, setIsFocussed] = useState(false);
52
+ const [shouldSideScroll, setShouldSideScroll] = useState(false);
53
+ const [position, setPosition] = useState<'top' | 'bottom'>('top');
54
+
55
+ const boundingClientRect = dropdownRef.current?.getBoundingClientRect() as DOMRect;
56
+
57
+ useEffect(() => {
58
+ if (window.innerHeight - (boundingClientRect?.y + 240) >= 0) {
59
+ setPosition('top');
60
+ } else {
61
+ setPosition('bottom');
62
+ }
63
+ }, [boundingClientRect]);
64
+
65
+ useOutsideClick({
66
+ ref: dropdownRef,
67
+ handler: () => setIsFocussed(false),
68
+ });
69
+
70
+ // gets latest watched form value (common delimited) from RHF state and creates a list
71
+ useEffect(() => {
72
+ if (watchedValue !== undefined && !watchedValue.length) {
73
+ setLocalValues([]);
74
+ }
75
+
76
+ if (watchedValue !== undefined && watchedValue?.length) {
77
+ if (shouldSideScroll) {
78
+ (scrollRef.current as HTMLDivElement).scrollTo({
79
+ left: scrollRef.current?.scrollWidth,
80
+ behavior: 'smooth',
100
81
  });
82
+ setShouldSideScroll(false);
101
83
  }
102
84
 
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)
85
+ setLocalValues(
86
+ watchedValue
87
+ .split(',')
88
+ .filter(Boolean)
89
+ .map((value: string) =>
90
+ options.find(option => option.value === value)
91
+ )
114
92
  );
93
+ }
94
+ }, [options, shouldSideScroll, watchedValue]);
95
+
96
+ const handleChange = (option: FieldOption) => {
97
+ setShouldSideScroll(true);
98
+ const newValue = [...localValues, option]
99
+ .map(({ value }) => value)
100
+ .join(',');
101
+
102
+ setValue(name as string, newValue, {
103
+ shouldValidate: true,
104
+ shouldDirty: true,
105
+ });
115
106
 
116
- setLocalValues(prevLocalValues => [...prevLocalValues, option]);
117
- };
107
+ setLocalOptions(prevLocalOptions =>
108
+ prevLocalOptions.filter(prevLocalOption => prevLocalOption !== option)
109
+ );
118
110
 
119
- const handleDelete = (option: FieldOption) => {
120
- const newValue = localValues
121
- .filter(localValue => localValue !== option)
122
- .map(({ value }) => value)
123
- .join(',');
111
+ setLocalValues(prevLocalValues => [...prevLocalValues, option]);
112
+ };
124
113
 
125
- setValue(name as string, newValue, {
126
- shouldValidate: true,
127
- shouldDirty: true,
128
- });
114
+ const handleDelete = (option: FieldOption) => {
115
+ const newValue = localValues
116
+ .filter(localValue => localValue !== option)
117
+ .map(({ value }) => value)
118
+ .join(',');
129
119
 
130
- setLocalOptions(prevLocalOptions =>
131
- [...prevLocalOptions, option].sort((a, b) => a.sortValue - b.sortValue)
132
- );
120
+ setValue(name as string, newValue, {
121
+ shouldValidate: true,
122
+ shouldDirty: true,
123
+ });
133
124
 
134
- setLocalValues(prevLocalValues =>
135
- prevLocalValues.filter(prevLocalValue => prevLocalValue !== option)
136
- );
137
- };
125
+ setLocalOptions(prevLocalOptions =>
126
+ [...prevLocalOptions, option].sort((a, b) => a.sortValue - b.sortValue)
127
+ );
138
128
 
139
- return (
140
- <Box ref={dropdownRef} position="relative">
129
+ setLocalValues(prevLocalValues =>
130
+ prevLocalValues.filter(prevLocalValue => prevLocalValue !== option)
131
+ );
132
+ };
133
+
134
+ return (
135
+ <Box
136
+ ref={dropdownRef}
137
+ position="relative"
138
+ onKeyDown={e => {
139
+ if (isFocussed) {
140
+ if (e.key === 'Tab') {
141
+ return setIsFocussed(false);
142
+ }
143
+
144
+ const idx = options.findIndex(
145
+ option => option.label[0].toLocaleLowerCase() === e.key
146
+ );
147
+
148
+ dropdownMenuRef.current?.scrollTo({
149
+ top: idx * 27,
150
+ behavior: 'smooth',
151
+ });
152
+ }
153
+ }}
154
+ >
155
+ <Flex
156
+ fontSize="13px"
157
+ h="26px"
158
+ border={isFocussed ? '2px solid' : '1px solid'}
159
+ borderColor={isFocussed ? colors.border.focus : colors.border.default}
160
+ py="5px"
161
+ pl="8px"
162
+ borderRadius="4px"
163
+ alignItems="center"
164
+ justifyContent="space-between"
165
+ onClick={() => {
166
+ if (!disabled) {
167
+ if (isFocussed) {
168
+ return setIsFocussed(false);
169
+ }
170
+
171
+ inputRef.current?.focus();
172
+ setIsFocussed(true);
173
+ }
174
+ }}
175
+ bg={disabled ? colors.fill.light.quaternary : '#ffffff'}
176
+ cursor={disabled ? 'not-allowed' : 'pointer'}
177
+ >
141
178
  <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
179
  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'}
180
+ h="inherit"
181
+ width="90%"
182
+ overflowX="scroll"
183
+ style={{
184
+ scrollbarWidth: 'none' /* Firefox */,
185
+ msOverflowStyle: 'none',
186
+ }}
187
+ sx={{
188
+ '::-webkit-scrollbar': {
189
+ display: 'none',
190
+ },
191
+ }}
192
+ ref={scrollRef}
153
193
  >
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>
194
+ {localValues.length ? (
195
+ localValues.map(option => (
196
+ <Box mr="4px" width="fit-content" h="16px" borderRadius="full">
197
+ <Token
198
+ label={option.label}
199
+ onDelete={() => handleDelete(option)}
200
+ />
201
+ </Box>
202
+ ))
203
+ ) : (
204
+ <Text color={colors.label.secondary.light} fontSize="13px">
205
+ {placeholder}
206
+ </Text>
207
+ )}
181
208
  </Flex>
182
- {isFocussed && (
183
- <Dropdown
184
- onSelectItem={option => handleChange(option)}
185
- options={localOptions}
186
- />
187
- )}
188
- </Box>
189
- );
190
- }
191
- );
209
+ <Input
210
+ padding={0}
211
+ border="none"
212
+ height="0"
213
+ width="0"
214
+ autoComplete="off"
215
+ type="text"
216
+ ref={inputRef}
217
+ tabIndex={-1}
218
+ _focus={{ boxShadow: 'none !important' }}
219
+ />
220
+ <Flex mr="4px" justifyContent="center" alignItems="center">
221
+ <Image src={SubtractIcon} alt="subtract" boxSize="16px" />
222
+ </Flex>
223
+ </Flex>
224
+ {isFocussed && (
225
+ <Dropdown
226
+ dropdownRef={dropdownMenuRef}
227
+ onSelectItem={option => handleChange(option)}
228
+ options={localOptions}
229
+ position={position}
230
+ />
231
+ )}
232
+ </Box>
233
+ );
234
+ });
192
235
 
193
236
  export default StackedMultiSelect;