@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/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.26.2",
2
+ "version": "0.27.0",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -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
@@ -39,6 +39,7 @@ const customXQChakraTheme = extendTheme({
39
39
  FormError,
40
40
  FormLabel,
41
41
  Input,
42
+ NumberInput: Input,
42
43
  Link,
43
44
  Menu,
44
45
  Modal,