@xqmsg/ui-core 0.26.2 → 0.27.0
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/Input.stories.d.ts +1 -0
- package/dist/components/input/InputTypes.d.ts +2 -2
- package/dist/components/input/StackedNumberInput/StackedNumberInput.d.ts +20 -0
- package/dist/components/input/index.d.ts +10 -2
- package/dist/ui-core.cjs.development.js +94 -1
- 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 +95 -2
- package/dist/ui-core.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/input/Input.stories.tsx +19 -0
- package/src/components/input/InputTypes.ts +5 -1
- package/src/components/input/StackedNumberInput/StackedNumberInput.tsx +121 -0
- package/src/components/input/index.tsx +43 -3
- package/src/theme/customXQChakraTheme.ts +1 -0
package/package.json
CHANGED
|
@@ -76,6 +76,7 @@ interface StoryFormSchema {
|
|
|
76
76
|
prop4?: string;
|
|
77
77
|
prop5?: string;
|
|
78
78
|
prop6?: boolean;
|
|
79
|
+
prop7?: number;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
const onStubbedSubmit = () => null;
|
|
@@ -87,6 +88,7 @@ const storyFormDefaultValues: StoryFormSchema = {
|
|
|
87
88
|
prop4: '',
|
|
88
89
|
prop5: '',
|
|
89
90
|
prop6: true,
|
|
91
|
+
prop7: undefined,
|
|
90
92
|
};
|
|
91
93
|
|
|
92
94
|
const storyFormSchema: Yup.SchemaOf<StoryFormSchema> = Yup.object().shape({
|
|
@@ -96,6 +98,7 @@ const storyFormSchema: Yup.SchemaOf<StoryFormSchema> = Yup.object().shape({
|
|
|
96
98
|
prop4: Yup.string(),
|
|
97
99
|
prop5: Yup.string(),
|
|
98
100
|
prop6: Yup.boolean(),
|
|
101
|
+
prop7: Yup.number(),
|
|
99
102
|
});
|
|
100
103
|
|
|
101
104
|
export default meta;
|
|
@@ -175,6 +178,22 @@ const Template: Story<InputProps<StoryFormSchema>> = args => {
|
|
|
175
178
|
name="prop3"
|
|
176
179
|
onChange={e => form.setValue('prop3', e.target.value)}
|
|
177
180
|
/>
|
|
181
|
+
<Input
|
|
182
|
+
{...args}
|
|
183
|
+
label={args.label + ' - number'}
|
|
184
|
+
inputType="number"
|
|
185
|
+
name="prop7"
|
|
186
|
+
ariaLabel="number input"
|
|
187
|
+
min={0}
|
|
188
|
+
max={100}
|
|
189
|
+
step={1}
|
|
190
|
+
precision={2}
|
|
191
|
+
setValue={form.setValue}
|
|
192
|
+
setError={form.setError}
|
|
193
|
+
clearErrors={form.clearErrors}
|
|
194
|
+
isInvalid={!!form.formState.errors['prop7']?.message}
|
|
195
|
+
errorText={form.formState.errors['prop7']?.message}
|
|
196
|
+
/>
|
|
178
197
|
<Input
|
|
179
198
|
{...args}
|
|
180
199
|
inputType="text"
|
|
@@ -16,7 +16,8 @@ export type InputType =
|
|
|
16
16
|
| 'textarea'
|
|
17
17
|
| 'radio'
|
|
18
18
|
| 'checkbox'
|
|
19
|
-
| 'switch'
|
|
19
|
+
| 'switch'
|
|
20
|
+
| 'number';
|
|
20
21
|
|
|
21
22
|
export type FieldOption = {
|
|
22
23
|
label: string;
|
|
@@ -43,6 +44,9 @@ export interface InputFieldProps
|
|
|
43
44
|
| 'ref'
|
|
44
45
|
| 'onChange'
|
|
45
46
|
| 'defaultValue'
|
|
47
|
+
| 'min'
|
|
48
|
+
| 'max'
|
|
49
|
+
| 'step'
|
|
46
50
|
>,
|
|
47
51
|
FieldProps,
|
|
48
52
|
Partial<InputProps> {}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
InputGroup,
|
|
4
|
+
NumberInput,
|
|
5
|
+
NumberInputField,
|
|
6
|
+
NumberInputStepper,
|
|
7
|
+
NumberIncrementStepper,
|
|
8
|
+
NumberDecrementStepper,
|
|
9
|
+
} from '@chakra-ui/react';
|
|
10
|
+
import { InputFieldProps } from '../InputTypes';
|
|
11
|
+
|
|
12
|
+
export interface StackedNumberInputProps extends InputFieldProps {
|
|
13
|
+
isRequired?: boolean;
|
|
14
|
+
leftElement?: React.ReactNode;
|
|
15
|
+
rightElement?: React.ReactNode;
|
|
16
|
+
variant?: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
min?: number;
|
|
19
|
+
max?: number;
|
|
20
|
+
step?: number;
|
|
21
|
+
precision?: number;
|
|
22
|
+
showStepper?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A functional React component that renders Chakra UI's `NumberInput` wrapped
|
|
27
|
+
* in an `InputGroup`, following the same pattern as `StackedInput`.
|
|
28
|
+
*/
|
|
29
|
+
const StackedNumberInput = React.forwardRef<
|
|
30
|
+
HTMLInputElement,
|
|
31
|
+
StackedNumberInputProps
|
|
32
|
+
>(
|
|
33
|
+
(
|
|
34
|
+
{
|
|
35
|
+
name,
|
|
36
|
+
id,
|
|
37
|
+
placeholder,
|
|
38
|
+
isRequired,
|
|
39
|
+
isInvalid,
|
|
40
|
+
disabled,
|
|
41
|
+
value,
|
|
42
|
+
defaultValue,
|
|
43
|
+
min,
|
|
44
|
+
max,
|
|
45
|
+
step = 1,
|
|
46
|
+
precision = 0,
|
|
47
|
+
showStepper = false,
|
|
48
|
+
variant,
|
|
49
|
+
className,
|
|
50
|
+
label,
|
|
51
|
+
leftElement,
|
|
52
|
+
rightElement,
|
|
53
|
+
onChange,
|
|
54
|
+
onBlur,
|
|
55
|
+
},
|
|
56
|
+
ref
|
|
57
|
+
) => {
|
|
58
|
+
const isMobile = variant === 'mobile';
|
|
59
|
+
const resolvedPlaceholder = isMobile && label ? label : placeholder;
|
|
60
|
+
const inputValue =
|
|
61
|
+
typeof value === 'string' || typeof value === 'number' ? value : undefined;
|
|
62
|
+
const normalizedValue =
|
|
63
|
+
precision === 0 && inputValue !== undefined && inputValue !== ''
|
|
64
|
+
? Number.isNaN(Number(inputValue))
|
|
65
|
+
? inputValue
|
|
66
|
+
: String(Math.round(Number(inputValue)))
|
|
67
|
+
: inputValue;
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<InputGroup>
|
|
71
|
+
{leftElement && leftElement}
|
|
72
|
+
{label && !isMobile && label}
|
|
73
|
+
<NumberInput
|
|
74
|
+
id={id}
|
|
75
|
+
name={name}
|
|
76
|
+
isRequired={isRequired}
|
|
77
|
+
isInvalid={isInvalid}
|
|
78
|
+
isDisabled={disabled}
|
|
79
|
+
value={normalizedValue}
|
|
80
|
+
defaultValue={defaultValue}
|
|
81
|
+
min={min}
|
|
82
|
+
max={max}
|
|
83
|
+
step={step}
|
|
84
|
+
precision={precision}
|
|
85
|
+
onChange={(_: string, valueAsNumber: number) =>
|
|
86
|
+
onChange?.({
|
|
87
|
+
target: {
|
|
88
|
+
name,
|
|
89
|
+
value: Number.isNaN(valueAsNumber)
|
|
90
|
+
? ''
|
|
91
|
+
: precision === 0
|
|
92
|
+
? String(Math.round(valueAsNumber))
|
|
93
|
+
: String(valueAsNumber),
|
|
94
|
+
},
|
|
95
|
+
} as React.ChangeEvent<HTMLInputElement>)
|
|
96
|
+
}
|
|
97
|
+
className={className}
|
|
98
|
+
variant={variant === 'mobile' ? 'mobile' : 'default'}
|
|
99
|
+
width="100%"
|
|
100
|
+
>
|
|
101
|
+
<NumberInputField
|
|
102
|
+
ref={ref}
|
|
103
|
+
placeholder={resolvedPlaceholder}
|
|
104
|
+
onBlur={onBlur}
|
|
105
|
+
/>
|
|
106
|
+
{showStepper && (
|
|
107
|
+
<NumberInputStepper>
|
|
108
|
+
<NumberIncrementStepper />
|
|
109
|
+
<NumberDecrementStepper />
|
|
110
|
+
</NumberInputStepper>
|
|
111
|
+
)}
|
|
112
|
+
</NumberInput>
|
|
113
|
+
{rightElement && rightElement}
|
|
114
|
+
</InputGroup>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
StackedNumberInput.displayName = 'StackedNumberInput';
|
|
120
|
+
|
|
121
|
+
export default StackedNumberInput;
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
UseFormSetValue,
|
|
23
23
|
} from 'react-hook-form';
|
|
24
24
|
import StackedMultiSelect from './StackedMultiSelect';
|
|
25
|
+
import StackedNumberInput from './StackedNumberInput/StackedNumberInput';
|
|
25
26
|
import StackedPilledInput from './StackedPilledInput';
|
|
26
27
|
import StackedSwitch from './StackedSwitch';
|
|
27
28
|
import { Label } from './components/label';
|
|
@@ -32,7 +33,7 @@ export interface InputProps<T extends FieldValues = FieldValues>
|
|
|
32
33
|
name: string;
|
|
33
34
|
ariaLabel: string;
|
|
34
35
|
placeholder?: string;
|
|
35
|
-
defaultValue?: string;
|
|
36
|
+
defaultValue?: string | number;
|
|
36
37
|
label?: string;
|
|
37
38
|
className?: string;
|
|
38
39
|
options?: FieldOption[];
|
|
@@ -54,6 +55,14 @@ export interface InputProps<T extends FieldValues = FieldValues>
|
|
|
54
55
|
truncatePillLength?: number;
|
|
55
56
|
searchable?: boolean;
|
|
56
57
|
overflowMode?: 'scroll' | 'wrap';
|
|
58
|
+
/** Minimum value (numberInput only) */
|
|
59
|
+
min?: number;
|
|
60
|
+
/** Maximum value (numberInput only) */
|
|
61
|
+
max?: number;
|
|
62
|
+
/** Increment/decrement step (numberInput only) */
|
|
63
|
+
step?: number;
|
|
64
|
+
/** Number of decimal places (numberInput only) */
|
|
65
|
+
precision?: number;
|
|
57
66
|
}
|
|
58
67
|
|
|
59
68
|
/**
|
|
@@ -90,12 +99,16 @@ export function Input<T extends FieldValues>({
|
|
|
90
99
|
truncatePillLength,
|
|
91
100
|
searchable,
|
|
92
101
|
overflowMode = 'scroll',
|
|
102
|
+
min,
|
|
103
|
+
max,
|
|
104
|
+
step,
|
|
105
|
+
precision,
|
|
93
106
|
}: InputProps<T>) {
|
|
94
107
|
function selectedInputField<T extends Element = Element>(
|
|
95
|
-
onChange: ((e: ChangeEvent<T>) => void) | ((v?: string) => void),
|
|
108
|
+
onChange: ((e: ChangeEvent<T>) => void) | ((v?: string | number) => void),
|
|
96
109
|
onBlur: () => void,
|
|
97
110
|
ref: RefCallBack,
|
|
98
|
-
value: string
|
|
111
|
+
value: string | number
|
|
99
112
|
) {
|
|
100
113
|
switch (inputType) {
|
|
101
114
|
case 'text':
|
|
@@ -121,6 +134,33 @@ export function Input<T extends FieldValues>({
|
|
|
121
134
|
label={label as string}
|
|
122
135
|
/>
|
|
123
136
|
);
|
|
137
|
+
case 'number':
|
|
138
|
+
return (
|
|
139
|
+
<StackedNumberInput
|
|
140
|
+
className={`input-${inputType} ${className ?? ''}`}
|
|
141
|
+
aria-label={ariaLabel}
|
|
142
|
+
name={name}
|
|
143
|
+
id={name}
|
|
144
|
+
placeholder={placeholder}
|
|
145
|
+
maxLength={maxLength}
|
|
146
|
+
isRequired={isRequired}
|
|
147
|
+
isInvalid={isInvalid}
|
|
148
|
+
onChange={onChange as (e: ChangeEvent) => void}
|
|
149
|
+
onBlur={onBlur}
|
|
150
|
+
ref={ref}
|
|
151
|
+
rightElement={rightElement}
|
|
152
|
+
leftElement={leftElement}
|
|
153
|
+
disabled={disabled}
|
|
154
|
+
defaultValue={defaultValue}
|
|
155
|
+
value={value}
|
|
156
|
+
variant={variant}
|
|
157
|
+
label={label as string}
|
|
158
|
+
min={min}
|
|
159
|
+
max={max}
|
|
160
|
+
step={step}
|
|
161
|
+
precision={precision}
|
|
162
|
+
/>
|
|
163
|
+
);
|
|
124
164
|
case 'radio':
|
|
125
165
|
return (
|
|
126
166
|
<StackedRadioGroup
|