1 | import * as React from 'react';
|
2 | import { FormControlState } from '../FormControl';
|
3 | import { NumberInputAction } from './numberInputAction.types';
|
4 | import { ActionWithContext } from '../utils/useControllableReducer.types';
|
5 | export type StepDirection = 'up' | 'down';
|
6 | /**
|
7 | * The internal state of the NumberInput.
|
8 | * Modify via the reducer only.
|
9 | */
|
10 | export interface NumberInputState {
|
11 | /**
|
12 | * The clamped `value` of the `input` element.
|
13 | */
|
14 | value: number | null;
|
15 | /**
|
16 | * The dirty `value` of the `input` element when it is in focus.
|
17 | */
|
18 | inputValue: string;
|
19 | }
|
20 | /**
|
21 | * Additional props passed to the number input reducer actions.
|
22 | */
|
23 | export type NumberInputActionContext = {
|
24 | min?: number;
|
25 | max?: number;
|
26 | step?: number;
|
27 | shiftMultiplier: number;
|
28 | /**
|
29 | * A function that parses the raw input value
|
30 | */
|
31 | getInputValueAsString: (val: string) => string;
|
32 | };
|
33 | export type NumberInputReducerAction = ActionWithContext<NumberInputAction, NumberInputActionContext>;
|
34 | export interface UseNumberInputParameters {
|
35 | /**
|
36 | * The minimum value.
|
37 | */
|
38 | min?: number;
|
39 | /**
|
40 | * The maximum value.
|
41 | */
|
42 | max?: number;
|
43 | /**
|
44 | * The amount that the value changes on each increment or decrement.
|
45 | */
|
46 | step?: number;
|
47 | /**
|
48 | * Multiplier applied to `step` if the shift key is held while incrementing
|
49 | * or decrementing the value. Defaults to `10`.
|
50 | */
|
51 | shiftMultiplier?: number;
|
52 | /**
|
53 | * The default value. Use when the component is not controlled.
|
54 | */
|
55 | defaultValue?: number | null;
|
56 | /**
|
57 | * If `true`, the component is disabled.
|
58 | * The prop defaults to the value (`false`) inherited from the parent FormControl component.
|
59 | */
|
60 | disabled?: boolean;
|
61 | /**
|
62 | * If `true`, the `input` will indicate an error by setting the `aria-invalid` attribute.
|
63 | * The prop defaults to the value (`false`) inherited from the parent FormControl component.
|
64 | */
|
65 | error?: boolean;
|
66 | onBlur?: (event?: React.FocusEvent<HTMLInputElement>) => void;
|
67 | onClick?: React.MouseEventHandler;
|
68 | /**
|
69 | * Callback fired when the `input` value changes after each keypress, before clamping is applied.
|
70 | * Note that `event.target.value` may contain values that fall outside of `min` and `max` or
|
71 | * are otherwise "invalid".
|
72 | *
|
73 | * @param {React.ChangeEvent<HTMLInputElement>} event The event source of the callback.
|
74 | */
|
75 | onInputChange?: React.ChangeEventHandler<HTMLInputElement>;
|
76 | onFocus?: React.FocusEventHandler;
|
77 | /**
|
78 | * Callback fired after the value is clamped and changes - when the `input` is blurred or when
|
79 | * the stepper buttons are triggered.
|
80 | * Called with `undefined` when the value is unset.
|
81 | *
|
82 | * @param {React.FocusEvent<HTMLInputElement>|React.PointerEvent|React.KeyboardEvent} event The event source of the callback
|
83 | * @param {number|undefined} value The new value of the component
|
84 | */
|
85 | onChange?: (event: React.FocusEvent<HTMLInputElement> | React.PointerEvent | React.KeyboardEvent, value: number | null) => void;
|
86 | /**
|
87 | * The `id` attribute of the input element.
|
88 | */
|
89 | inputId?: string;
|
90 | /**
|
91 | * The ref of the input element.
|
92 | */
|
93 | inputRef?: React.Ref<HTMLInputElement>;
|
94 | /**
|
95 | * If `true`, the `input` element is required.
|
96 | * The prop defaults to the value (`false`) inherited from the parent FormControl component.
|
97 | */
|
98 | required?: boolean;
|
99 | /**
|
100 | * If `true`, the `input` element becomes read-only. The stepper buttons remain active,
|
101 | * with the addition that they are now keyboard focusable.
|
102 | * @default false
|
103 | */
|
104 | readOnly?: boolean;
|
105 | /**
|
106 | * The current value. Use when the component is controlled.
|
107 | * @default null
|
108 | */
|
109 | value?: number | null;
|
110 | /**
|
111 | * The name of the component using useNumberInput.
|
112 | * For debugging purposes.
|
113 | * @default 'useNumberInput'
|
114 | */
|
115 | componentName?: string;
|
116 | }
|
117 | export interface UseNumberInputRootSlotOwnProps {
|
118 | onClick: React.MouseEventHandler | undefined;
|
119 | }
|
120 | export type UseNumberInputRootSlotProps<ExternalProps = {}> = Omit<ExternalProps, keyof UseNumberInputRootSlotOwnProps | 'onBlur' | 'onInputChange' | 'onFocus'> & UseNumberInputRootSlotOwnProps;
|
121 | export interface UseNumberInputInputSlotOwnProps {
|
122 | defaultValue: number | undefined;
|
123 | id: string | undefined;
|
124 | ref: React.RefCallback<HTMLInputElement> | null;
|
125 | value: number | undefined;
|
126 | role?: React.AriaRole;
|
127 | 'aria-disabled': React.AriaAttributes['aria-disabled'];
|
128 | 'aria-valuemax': React.AriaAttributes['aria-valuemax'];
|
129 | 'aria-valuemin': React.AriaAttributes['aria-valuemin'];
|
130 | 'aria-valuenow': React.AriaAttributes['aria-valuenow'];
|
131 | 'aria-valuetext': React.AriaAttributes['aria-valuetext'];
|
132 | tabIndex?: number;
|
133 | onBlur: React.FocusEventHandler;
|
134 | onChange: React.ChangeEventHandler<HTMLInputElement>;
|
135 | onFocus: React.FocusEventHandler;
|
136 | required: boolean;
|
137 | disabled: boolean;
|
138 | }
|
139 | export type UseNumberInputInputSlotProps<ExternalProps = {}> = Omit<ExternalProps, keyof UseNumberInputInputSlotOwnProps> & UseNumberInputInputSlotOwnProps;
|
140 | export interface UseNumberInputIncrementButtonSlotOwnProps {
|
141 | 'aria-controls': React.AriaAttributes['aria-controls'];
|
142 | 'aria-disabled': React.AriaAttributes['aria-disabled'];
|
143 | disabled: boolean;
|
144 | tabIndex?: number;
|
145 | }
|
146 | export type UseNumberInputIncrementButtonSlotProps<ExternalProps = {}> = Omit<ExternalProps, keyof UseNumberInputIncrementButtonSlotOwnProps> & UseNumberInputIncrementButtonSlotOwnProps;
|
147 | export interface UseNumberInputDecrementButtonSlotOwnProps {
|
148 | 'aria-controls': React.AriaAttributes['aria-controls'];
|
149 | 'aria-disabled': React.AriaAttributes['aria-disabled'];
|
150 | disabled: boolean;
|
151 | tabIndex?: number;
|
152 | }
|
153 | export type UseNumberInputDecrementButtonSlotProps<ExternalProps = {}> = Omit<ExternalProps, keyof UseNumberInputDecrementButtonSlotOwnProps> & UseNumberInputDecrementButtonSlotOwnProps;
|
154 | export interface UseNumberInputReturnValue {
|
155 | /**
|
156 | * If `true`, the component will be disabled.
|
157 | * @default false
|
158 | */
|
159 | disabled: boolean;
|
160 | /**
|
161 | * If `true`, the `input` will indicate an error by setting the `aria-invalid` attribute.
|
162 | * @default false
|
163 | */
|
164 | error: boolean;
|
165 | /**
|
166 | * If `true`, the `input` will be focused.
|
167 | * @default false
|
168 | */
|
169 | focused: boolean;
|
170 | /**
|
171 | * Return value from the `useFormControlContext` hook.
|
172 | */
|
173 | formControlContext: FormControlState | undefined;
|
174 | /**
|
175 | * Resolver for the decrement button slot's props.
|
176 | * @param externalProps props for the decrement button slot
|
177 | * @returns props that should be spread on the decrement button slot
|
178 | */
|
179 | getDecrementButtonProps: <ExternalProps extends Record<string, unknown> = {}>(externalProps?: ExternalProps) => UseNumberInputDecrementButtonSlotProps<ExternalProps>;
|
180 | /**
|
181 | * Resolver for the increment button slot's props.
|
182 | * @param externalProps props for the increment button slot
|
183 | * @returns props that should be spread on the increment button slot
|
184 | */
|
185 | getIncrementButtonProps: <ExternalProps extends Record<string, unknown> = {}>(externalProps?: ExternalProps) => UseNumberInputIncrementButtonSlotProps<ExternalProps>;
|
186 | /**
|
187 | * Resolver for the input slot's props.
|
188 | * @param externalProps props for the input slot
|
189 | * @returns props that should be spread on the input slot
|
190 | */
|
191 | getInputProps: <ExternalProps extends Record<string, unknown> = {}>(externalProps?: ExternalProps) => UseNumberInputInputSlotProps<ExternalProps>;
|
192 | /**
|
193 | * Resolver for the root slot's props.
|
194 | * @param externalProps props for the root slot
|
195 | * @returns props that should be spread on the root slot
|
196 | */
|
197 | getRootProps: <ExternalProps extends Record<string, unknown> = {}>(externalProps?: ExternalProps) => UseNumberInputRootSlotProps<ExternalProps>;
|
198 | /**
|
199 | * If `true`, the `input` will indicate that it's required.
|
200 | * @default false
|
201 | */
|
202 | required: boolean;
|
203 | /**
|
204 | * The clamped `value` of the `input` element.
|
205 | */
|
206 | value: number | null;
|
207 | /**
|
208 | * The dirty `value` of the `input` element when it is in focus.
|
209 | */
|
210 | inputValue: string;
|
211 | /**
|
212 | * If `true`, the increment button will be disabled.
|
213 | * e.g. when the `value` is already at `max`
|
214 | * @default false
|
215 | */
|
216 | isIncrementDisabled: boolean;
|
217 | /**
|
218 | * If `true`, the decrement button will be disabled.
|
219 | * e.g. when the `value` is already at `min`
|
220 | * @default false
|
221 | */
|
222 | isDecrementDisabled: boolean;
|
223 | }
|