@wordpress/components
Version:
UI components for WordPress.
233 lines (202 loc) • 7.58 kB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
import { createElement, Fragment } from "@wordpress/element";
/**
* External dependencies
*/
import classNames from 'classnames';
/**
* WordPress dependencies
*/
import { useRef, forwardRef } from '@wordpress/element';
import { isRTL, __ } from '@wordpress/i18n';
import { plus as plusIcon, reset as resetIcon } from '@wordpress/icons';
import { useMergeRefs } from '@wordpress/compose';
import deprecated from '@wordpress/deprecated';
/**
* Internal dependencies
*/
import { Input, SpinButton } from './styles/number-control-styles';
import * as inputControlActionTypes from '../input-control/reducer/actions';
import { add, subtract, roundClamp } from '../utils/math';
import { ensureNumber, isValueEmpty } from '../utils/values';
import { HStack } from '../h-stack';
import { Spacer } from '../spacer';
const noop = () => {};
function UnforwardedNumberControl(_ref, forwardedRef) {
let {
__unstableStateReducer: stateReducerProp,
className,
dragDirection = 'n',
hideHTMLArrows = false,
spinControls = 'native',
isDragEnabled = true,
isShiftStepEnabled = true,
label,
max = Infinity,
min = -Infinity,
required = false,
shiftStep = 10,
step = 1,
type: typeProp = 'number',
value: valueProp,
size = 'default',
suffix,
onChange = noop,
...props
} = _ref;
if (hideHTMLArrows) {
deprecated('wp.components.NumberControl hideHTMLArrows prop ', {
alternative: 'spinControls="none"',
since: '6.2',
version: '6.3'
});
spinControls = 'none';
}
const inputRef = useRef();
const mergedRef = useMergeRefs([inputRef, forwardedRef]);
const isStepAny = step === 'any';
const baseStep = isStepAny ? 1 : ensureNumber(step);
const baseValue = roundClamp(0, min, max, baseStep);
const constrainValue = (value, stepOverride) => {
// When step is "any" clamp the value, otherwise round and clamp it.
return isStepAny ? Math.min(max, Math.max(min, ensureNumber(value))) : roundClamp(value, min, max, stepOverride !== null && stepOverride !== void 0 ? stepOverride : baseStep);
};
const autoComplete = typeProp === 'number' ? 'off' : undefined;
const classes = classNames('components-number-control', className);
const spinValue = (value, direction, event) => {
event === null || event === void 0 ? void 0 : event.preventDefault();
const shift = (event === null || event === void 0 ? void 0 : event.shiftKey) && isShiftStepEnabled;
const delta = shift ? ensureNumber(shiftStep) * baseStep : baseStep;
let nextValue = isValueEmpty(value) ? baseValue : value;
if (direction === 'up') {
nextValue = add(nextValue, delta);
} else if (direction === 'down') {
nextValue = subtract(nextValue, delta);
}
return constrainValue(nextValue, shift ? delta : undefined);
};
/**
* "Middleware" function that intercepts updates from InputControl.
* This allows us to tap into actions to transform the (next) state for
* InputControl.
*
* @return The updated state to apply to InputControl
*/
const numberControlStateReducer = (state, action) => {
const nextState = { ...state
};
const {
type,
payload
} = action;
const event = payload.event;
const currentValue = nextState.value;
/**
* Handles custom UP and DOWN Keyboard events
*/
if (type === inputControlActionTypes.PRESS_UP || type === inputControlActionTypes.PRESS_DOWN) {
// @ts-expect-error TODO: Resolve discrepancy between `value` types in InputControl based components
nextState.value = spinValue(currentValue, type === inputControlActionTypes.PRESS_UP ? 'up' : 'down', event);
}
/**
* Handles drag to update events
*/
if (type === inputControlActionTypes.DRAG && isDragEnabled) {
const [x, y] = payload.delta;
const enableShift = payload.shiftKey && isShiftStepEnabled;
const modifier = enableShift ? ensureNumber(shiftStep) * baseStep : baseStep;
let directionModifier;
let delta;
switch (dragDirection) {
case 'n':
delta = y;
directionModifier = -1;
break;
case 'e':
delta = x;
directionModifier = isRTL() ? -1 : 1;
break;
case 's':
delta = y;
directionModifier = 1;
break;
case 'w':
delta = x;
directionModifier = isRTL() ? 1 : -1;
break;
}
if (delta !== 0) {
delta = Math.ceil(Math.abs(delta)) * Math.sign(delta);
const distance = delta * modifier * directionModifier; // @ts-expect-error TODO: Resolve discrepancy between `value` types in InputControl based components
nextState.value = constrainValue( // @ts-expect-error TODO: Investigate if it's ok for currentValue to be undefined
add(currentValue, distance), enableShift ? modifier : undefined);
}
}
/**
* Handles commit (ENTER key press or blur)
*/
if (type === inputControlActionTypes.PRESS_ENTER || type === inputControlActionTypes.COMMIT) {
const applyEmptyValue = required === false && currentValue === ''; // @ts-expect-error TODO: Resolve discrepancy between `value` types in InputControl based components
nextState.value = applyEmptyValue ? currentValue : // @ts-expect-error TODO: Investigate if it's ok for currentValue to be undefined
constrainValue(currentValue);
}
return nextState;
};
const buildSpinButtonClickHandler = direction => event => onChange(String(spinValue(valueProp, direction, event)), {
// Set event.target to the <input> so that consumers can use
// e.g. event.target.validity.
event: { ...event,
target: inputRef.current
}
});
return createElement(Input, _extends({
autoComplete: autoComplete,
inputMode: "numeric"
}, props, {
className: classes,
dragDirection: dragDirection,
hideHTMLArrows: spinControls !== 'native',
isDragEnabled: isDragEnabled,
label: label,
max: max,
min: min,
ref: mergedRef,
required: required,
step: step,
type: typeProp // @ts-expect-error TODO: Resolve discrepancy between `value` types in InputControl based components
,
value: valueProp,
__unstableStateReducer: (state, action) => {
var _stateReducerProp;
const baseState = numberControlStateReducer(state, action);
return (_stateReducerProp = stateReducerProp === null || stateReducerProp === void 0 ? void 0 : stateReducerProp(baseState, action)) !== null && _stateReducerProp !== void 0 ? _stateReducerProp : baseState;
},
size: size,
suffix: spinControls === 'custom' ? createElement(Fragment, null, suffix, createElement(Spacer, {
marginBottom: 0,
marginRight: 2
}, createElement(HStack, {
spacing: 1
}, createElement(SpinButton, {
icon: plusIcon,
isSmall: true,
"aria-hidden": "true",
"aria-label": __('Increment'),
tabIndex: -1,
onClick: buildSpinButtonClickHandler('up'),
size: size
}), createElement(SpinButton, {
icon: resetIcon,
isSmall: true,
"aria-hidden": "true",
"aria-label": __('Decrement'),
tabIndex: -1,
onClick: buildSpinButtonClickHandler('down'),
size: size
})))) : suffix,
onChange: onChange
}));
}
export const NumberControl = forwardRef(UnforwardedNumberControl);
export default NumberControl;
//# sourceMappingURL=index.js.map