Skip to content

Commit

Permalink
Add keyboard handlers and shift multiplier
Browse files Browse the repository at this point in the history
  • Loading branch information
mj12albert committed Mar 2, 2023
1 parent 81afe0c commit 90777ca
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 8 deletions.
1 change: 1 addition & 0 deletions docs/pages/base/api/use-number-input.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
}
},
"required": { "type": { "name": "boolean", "description": "boolean" } },
"shiftMultiplier": { "type": { "name": "number", "description": "number" } },
"step": { "type": { "name": "number", "description": "number" } },
"value": { "type": { "name": "unknown", "description": "unknown" } }
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"defaultValue": "The default value. Use when the component is not controlled.",
"disabled": "If <code>true</code>, the component is disabled.\nThe prop defaults to the value (<code>false</code>) inherited from the parent FormControl component.",
"error": "If <code>true</code>, the <code>input</code> will indicate an error by setting the <code>aria-invalid</code> attribute.\nThe prop defaults to the value (<code>false</code>) inherited from the parent FormControl component.",
"required": "If <code>true</code>, the <code>input</code> element is required.\nThe prop defaults to the value (<code>false</code>) inherited from the parent FormControl component."
"required": "If <code>true</code>, the <code>input</code> element is required.\nThe prop defaults to the value (<code>false</code>) inherited from the parent FormControl component.",
"shiftMultiplier": "Multiplier applied to <code>step</code> if the shift key is held while incrementing\nor decrementing the value. Defaults to </code>10</code>."
},
"returnValueDescriptions": {
"disabled": "If <code>true</code>, the component will be disabled.",
Expand Down
38 changes: 31 additions & 7 deletions packages/mui-base/src/NumberInputUnstyled/useNumberInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
import clamp from './clamp';
import extractEventHandlers from '../utils/extractEventHandlers';

type StepDirection = 'up' | 'down';

// TODO
// 1 - make a proper parser
// 2 - accept a parser (func) prop
Expand All @@ -35,6 +37,7 @@ export default function useNumberInput(
min,
max,
step,
shiftMultiplier = 10,
defaultValue: defaultValueProp,
disabled: disabledProp = false,
error: errorProp = false,
Expand Down Expand Up @@ -109,7 +112,10 @@ export default function useNumberInput(

const handleValueChange =
() =>
(event: React.FocusEvent<HTMLInputElement> | React.PointerEvent, val: number | undefined) => {
(
event: React.FocusEvent<HTMLInputElement> | React.PointerEvent | React.KeyboardEvent,
val: number | undefined,
) => {
// 1. clamp the number
// 2. setInputValue(clamped_value)
// 3. call onValueChange(newValue)
Expand Down Expand Up @@ -201,16 +207,15 @@ export default function useNumberInput(
};

const handleStep =
(direction: 'up' | 'down') =>
(
event: React.PointerEvent, // TODO: this could also be a keyboard event: arrow up/down or enter on the button
) => {
(direction: StepDirection) => (event: React.PointerEvent | React.KeyboardEvent) => {
let newValue;

const stepMultiplier = event.shiftKey ? shiftMultiplier : 1;

if (typeof value === 'number') {
newValue = {
up: value + (step ?? 1),
down: value - (step ?? 1),
up: value + (step ?? 1) * stepMultiplier,
down: value - (step ?? 1) * stepMultiplier,
}[direction];
} else {
// no value
Expand All @@ -222,6 +227,24 @@ export default function useNumberInput(
handleValueChange()(event, newValue);
};

const handleKeyDown =
(otherHandlers: Record<string, React.EventHandler<any> | undefined>) =>
(event: React.KeyboardEvent<HTMLInputElement>) => {
otherHandlers.onKeyDown?.(event);

if (event.defaultPrevented) {
return;
}

if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
const direction = {
ArrowUp: 'up',
ArrowDown: 'down',
}[event.key] as StepDirection;
handleStep(direction)(event);
}
};

const getRootProps = <TOther extends Record<string, any> = {}>(
externalProps: TOther = {} as TOther,
): UseNumberInputRootSlotProps<TOther> => {
Expand Down Expand Up @@ -258,6 +281,7 @@ export default function useNumberInput(
onFocus: handleFocus(externalEventHandlers),
onChange: handleInputChange(externalEventHandlers),
onBlur: handleBlur(externalEventHandlers),
onKeyDown: handleKeyDown(externalEventHandlers),
};

const displayValue = (focused ? inputValue : value) ?? '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export interface UseNumberInputParameters {
min?: number;
max?: number;
step?: number;
/**
* Multiplier applied to `step` if the shift key is held while incrementing
* or decrementing the value. Defaults to `10`.
*/
shiftMultiplier?: number;
/**
* The default value. Use when the component is not controlled.
*/
Expand Down

0 comments on commit 90777ca

Please sign in to comment.