From 56762353f284016d959e776b87feba5bd2c5d1fc Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Tue, 25 Mar 2025 18:03:36 +0200 Subject: [PATCH] refactor(NumberInput): simplify --- src/components/NumberInput/NumberInput.tsx | 82 ++++++++++++---------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/src/components/NumberInput/NumberInput.tsx b/src/components/NumberInput/NumberInput.tsx index 29070c70c..90cb565ac 100644 --- a/src/components/NumberInput/NumberInput.tsx +++ b/src/components/NumberInput/NumberInput.tsx @@ -1,7 +1,7 @@ // Copyright (C) 2017-2025 Smart code 203358507 import Icon from '@stremio/stremio-icons/react'; -import React, { ChangeEvent, forwardRef, useCallback, useState } from 'react'; +import React, { ChangeEvent, forwardRef, memo, useCallback, useMemo, useState } from 'react'; import { type KeyboardEvent, type InputHTMLAttributes } from 'react'; import classnames from 'classnames'; import styles from './NumberInput.less'; @@ -12,55 +12,61 @@ type Props = InputHTMLAttributes & { className?: string; disabled?: boolean; showButtons?: boolean; - defaultValue?: number; label?: string; min?: number; max?: number; + value?: number; + defaultValue?: number; onKeyDown?: (event: KeyboardEvent) => void; onSubmit?: (event: KeyboardEvent) => void; onUpdate?: (value: number) => void; + onChange?: (event: ChangeEvent) => void; }; -const NumberInput = forwardRef(({ defaultValue, showButtons, onUpdate, ...props }, ref) => { - const [value, setValue] = useState(defaultValue || 0); - const onKeyDown = useCallback((event: KeyboardEvent) => { - props.onKeyDown && props.onKeyDown(event); +const NumberInput = forwardRef(({ defaultValue = 1, showButtons, onUpdate, onKeyDown, onSubmit, min, max, onChange, ...props }, ref) => { + const [value, setValue] = useState(defaultValue); + const displayValue = useMemo(() => props.value ?? value, [props.value, value]); + + const handleKeyDown = useCallback((event: KeyboardEvent) => { + onKeyDown && onKeyDown(event); if (event.key === 'Enter') { - props.onSubmit && props.onSubmit(event); + onSubmit && onSubmit(event); } - }, [props.onKeyDown, props.onSubmit]); + }, [onKeyDown, onSubmit]); - const updateValueAndNotify = (valueAsNumber: number) => { - setValue(valueAsNumber); - onUpdate?.(valueAsNumber); + const handleValueChange = (value: number) => { + if (props.value === undefined) { + setValue(value); + } + onUpdate?.(value); + onChange?.({ target: { value: value.toString() }} as ChangeEvent); }; - const handleIncrease = () => { - if (props.max !== undefined) { - updateValueAndNotify(Math.min(props.max, (value || 0) + 1)); - return; - } - updateValueAndNotify((value || 0) + 1); + const handleIncrement = () => { + handleValueChange(clampValueToRange((displayValue || 0) + 1)); }; - const handleDecrease = () => { - if (props.min !== undefined) { - updateValueAndNotify(Math.max(props.min, (value || 0) - 1)); - return; - } - updateValueAndNotify((value || 0) - 1); + const handleDecrement = () => { + handleValueChange(clampValueToRange((displayValue || 0) - 1)); }; - const handleChange = ({ target: { valueAsNumber }}: ChangeEvent) => { - const min = props.min || 0; - if (valueAsNumber && valueAsNumber < min) { - valueAsNumber = min; + const clampValueToRange = (value: number): number => { + const minValue = min ?? 0; + + if (value < minValue) { + return minValue; } - if (props.max !== undefined && valueAsNumber && valueAsNumber > props.max) { - valueAsNumber = props.max; + + if (max !== undefined && value > max) { + return max; } - updateValueAndNotify(valueAsNumber); + + return value; + }; + + const handleInputChange = ({ target: { valueAsNumber }}: ChangeEvent) => { + handleValueChange(clampValueToRange(valueAsNumber || 0)); }; return ( @@ -69,8 +75,8 @@ const NumberInput = forwardRef(({ defaultValue, showBut showButtons ? : null @@ -85,17 +91,17 @@ const NumberInput = forwardRef(({ defaultValue, showBut ref={ref} type={'number'} tabIndex={0} - value={value} + value={displayValue} {...props} - className={classnames(props.className, styles['value'], { 'disabled': props.disabled })} - onChange={handleChange} - onKeyDown={onKeyDown} + className={classnames(props.className, styles['value'], { [styles.disabled]: props.disabled })} + onChange={handleInputChange} + onKeyDown={handleKeyDown} /> { showButtons ? : null @@ -106,4 +112,4 @@ const NumberInput = forwardRef(({ defaultValue, showBut NumberInput.displayName = 'NumberInput'; -export default NumberInput; +export default memo(NumberInput);