Add improvements to NumberControl (#47121)
* Use min and max values if provided when sanitizing values * Pass min and max attributes to NumberControl * Use 'min' and 'max' values when incrementing through + and - buttons * Add changelog * Handle increment when pressing buttons * Show + and - buttons disabled when max/min has been reached * Prevent suffix from being selected
This commit is contained in:
parent
d0a8477b51
commit
7c7d9837a4
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Update number control to not allow invalid values through arrow keys or buttons
|
|
@ -95,6 +95,8 @@ export function Edit( {
|
|||
tooltip={ tooltip }
|
||||
disabled={ disabled }
|
||||
step={ step }
|
||||
min={ min }
|
||||
max={ max }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -38,6 +38,8 @@ export type NumberProps = {
|
|||
tooltip?: string;
|
||||
disabled?: boolean;
|
||||
step?: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
};
|
||||
|
||||
const MEDIUM_DELAY = 500;
|
||||
|
@ -57,6 +59,8 @@ export const NumberControl: React.FC< NumberProps > = ( {
|
|||
placeholder,
|
||||
disabled,
|
||||
step = 1,
|
||||
min = -Infinity,
|
||||
max = +Infinity,
|
||||
}: NumberProps ) => {
|
||||
const id = useInstanceId( BaseControl, 'product_number_field' ) as string;
|
||||
const [ isFocused, setIsFocused ] = useState( false );
|
||||
|
@ -74,6 +78,8 @@ export const NumberControl: React.FC< NumberProps > = ( {
|
|||
value: value || '',
|
||||
onChange,
|
||||
onFocus: () => setIsFocused( true ),
|
||||
min,
|
||||
max,
|
||||
} );
|
||||
|
||||
const [ increment, setIncrement ] = useState( 0 );
|
||||
|
@ -82,8 +88,11 @@ export const NumberControl: React.FC< NumberProps > = ( {
|
|||
|
||||
const isInitialClick = useRef< boolean >( false );
|
||||
|
||||
const incrementValue = () =>
|
||||
onChange( String( parseFloat( value || '0' ) + increment ) );
|
||||
const incrementValue = () => {
|
||||
const newValue = parseFloat( value || '0' ) + increment;
|
||||
if ( newValue >= min && newValue <= max )
|
||||
onChange( String( newValue ) );
|
||||
};
|
||||
|
||||
useEffect( () => {
|
||||
if ( increment !== 0 ) {
|
||||
|
@ -104,6 +113,15 @@ export const NumberControl: React.FC< NumberProps > = ( {
|
|||
|
||||
const resetIncrement = () => setIncrement( 0 );
|
||||
|
||||
const handleIncrement = ( thisStep: number ) => {
|
||||
const newValue = parseFloat( value || '0' ) + thisStep;
|
||||
if ( newValue >= min && newValue <= max ) {
|
||||
onChange( String( parseFloat( value || '0' ) + thisStep ) );
|
||||
setIncrement( thisStep );
|
||||
isInitialClick.current = true;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseControl
|
||||
className={ classNames( {
|
||||
|
@ -134,16 +152,12 @@ export const NumberControl: React.FC< NumberProps > = ( {
|
|||
<Button
|
||||
className="woocommerce-number-control__increment"
|
||||
icon={ plus }
|
||||
onMouseDown={ () => {
|
||||
onChange(
|
||||
String(
|
||||
parseFloat( value || '0' ) +
|
||||
step
|
||||
)
|
||||
);
|
||||
setIncrement( step );
|
||||
isInitialClick.current = true;
|
||||
} }
|
||||
disabled={
|
||||
parseFloat( value || '0' ) >= max
|
||||
}
|
||||
onMouseDown={ () =>
|
||||
handleIncrement( step )
|
||||
}
|
||||
onMouseLeave={ resetIncrement }
|
||||
onMouseUp={ resetIncrement }
|
||||
onBlur={ unfocusIfOutside }
|
||||
|
@ -157,18 +171,14 @@ export const NumberControl: React.FC< NumberProps > = ( {
|
|||
/>
|
||||
<Button
|
||||
icon={ reset }
|
||||
disabled={
|
||||
parseFloat( value || '0' ) <= min
|
||||
}
|
||||
className="woocommerce-number-control__decrement"
|
||||
onBlur={ unfocusIfOutside }
|
||||
onMouseDown={ () => {
|
||||
onChange(
|
||||
String(
|
||||
parseFloat( value || '0' ) -
|
||||
step
|
||||
)
|
||||
);
|
||||
setIncrement( -step );
|
||||
isInitialClick.current = true;
|
||||
} }
|
||||
onMouseDown={ () =>
|
||||
handleIncrement( -step )
|
||||
}
|
||||
onMouseLeave={ resetIncrement }
|
||||
onMouseUp={ resetIncrement }
|
||||
isSmall
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
.woocommerce-number-control {
|
||||
.components-input-control__suffix {
|
||||
user-select: none;
|
||||
}
|
||||
}
|
|
@ -17,6 +17,8 @@ type Props = {
|
|||
onChange: ( value: string ) => void;
|
||||
onFocus?: ( event: React.FocusEvent< HTMLInputElement > ) => void;
|
||||
onKeyDown?: ( event: React.KeyboardEvent< HTMLInputElement > ) => void;
|
||||
min?: number;
|
||||
max?: number;
|
||||
};
|
||||
|
||||
const NOT_NUMBERS_OR_SEPARATORS_REGEX = /[^0-9,.]/g;
|
||||
|
@ -26,6 +28,8 @@ export const useNumberInputProps = ( {
|
|||
onChange,
|
||||
onFocus,
|
||||
onKeyDown,
|
||||
min = -Infinity,
|
||||
max = +Infinity,
|
||||
}: Props ) => {
|
||||
const { formatNumber, parseNumber } = useProductHelper();
|
||||
|
||||
|
@ -47,20 +51,27 @@ export const useNumberInputProps = ( {
|
|||
const step = Number( event.currentTarget.step || '1' );
|
||||
if ( event.code === 'ArrowUp' ) {
|
||||
event.preventDefault();
|
||||
onChange( String( amount + step ) );
|
||||
if ( amount + step <= max ) onChange( String( amount + step ) );
|
||||
}
|
||||
if ( event.code === 'ArrowDown' ) {
|
||||
event.preventDefault();
|
||||
onChange( String( amount - step ) );
|
||||
if ( amount - step >= min ) onChange( String( amount - step ) );
|
||||
}
|
||||
if ( onKeyDown ) {
|
||||
onKeyDown( event );
|
||||
}
|
||||
},
|
||||
onChange( newValue: string ) {
|
||||
const sanitizeValue = parseNumber(
|
||||
let sanitizeValue = parseNumber(
|
||||
newValue.replace( NOT_NUMBERS_OR_SEPARATORS_REGEX, '' )
|
||||
);
|
||||
const numberValue = Number( sanitizeValue );
|
||||
if ( sanitizeValue && numberValue >= max ) {
|
||||
sanitizeValue = String( max );
|
||||
}
|
||||
if ( sanitizeValue && numberValue <= min ) {
|
||||
sanitizeValue = String( min );
|
||||
}
|
||||
onChange( sanitizeValue );
|
||||
},
|
||||
};
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
@import "components/custom-fields/style.scss";
|
||||
@import "components/text-control/style.scss";
|
||||
@import "components/attribute-combobox-field/styles.scss";
|
||||
@import "components/number-control/style.scss";
|
||||
|
||||
/* Field Blocks */
|
||||
|
||||
|
|
Loading…
Reference in New Issue