feat(NumberInput): added NumberInput common component

This commit is contained in:
Botzy 2025-02-07 17:06:26 +02:00
parent 27b6942fcd
commit e8a6e72b13
4 changed files with 166 additions and 0 deletions

View file

@ -0,0 +1,84 @@
// Copyright (C) 2017-2025 Smart code 203358507
import React, { forwardRef, useCallback, useState } from 'react';
import { type KeyboardEvent, type InputHTMLAttributes } from 'react';
import classnames from 'classnames';
import styles from './styles.less';
import Button from '../Button';
import Icon from '@stremio/stremio-icons/react';
type Props = InputHTMLAttributes<HTMLInputElement> & {
containerClassName?: string;
className?: string;
disabled?: boolean;
showButtons?: boolean;
defaultValue?: number;
label?: string;
min?: number;
max?: number;
onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
onSubmit?: (event: KeyboardEvent<HTMLInputElement>) => void;
};
const NumberInput = forwardRef<HTMLInputElement, Props>(({ defaultValue, ...props }, ref) => {
const [value, setValue] = useState(defaultValue || 1);
const onKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) => {
props.onKeyDown && props.onKeyDown(event);
if (event.key === 'Enter' ) {
props.onSubmit && props.onSubmit(event);
}
}, [props.onKeyDown, props.onSubmit]);
const handleIncrease = () => {
const { max } = props;
if (max) {
return setValue((prevVal) =>
prevVal + 1 > max ? max : prevVal + 1
);
}
setValue((prevVal) => prevVal + 1);
};
const handleDecrease = () => {
const { min } = props;
if (min) {
return setValue((prevVal) =>
prevVal - 1 < min ? min : prevVal - 1
);
}
setValue((prevVal) => prevVal - 1);
};
return (
<div className={classnames(props.containerClassName, styles['number-input'])}>
{props.showButtons ? <Button
className={styles['btn']}
onClick={handleDecrease}
disabled={props.disabled || props.min ? value <= props.min : false}>
<Icon className={styles['icon']} name={'remove'} />
</Button> : null}
<div className={classnames(styles['number-display'], props.showButtons ? styles['with-btns'] : '')}>
{props.label && <div className={styles['label']}>{props.label}</div>}
<input
ref={ref}
type="number"
tabIndex={0}
value={value}
{...props}
className={classnames(props.className, styles['value'], { 'disabled': props.disabled })}
onChange={(event) => setValue(event.value)}
onKeyDown={onKeyDown}
/>
</div>
{props.showButtons ? <Button
className={styles['btn']} onClick={handleIncrease} disabled={props.disabled || props.max ? value >= props.max : false}>
<Icon className={styles['icon']} name={'add'} />
</Button> : null}
</div>
);
});
NumberInput.displayName = 'NumberInput';
export default NumberInput;

View file

@ -0,0 +1,5 @@
// Copyright (C) 2017-2025 Smart code 203358507
import NumberInput from './NumberInput';
export default NumberInput;

View file

@ -0,0 +1,75 @@
// Copyright (C) 2017-2025 Smart code 203358507
.number-input {
user-select: text;
display: flex;
max-width: 12rem;
margin-bottom: 1rem;
color: var(--primary-foreground-color);
background: var(--overlay-color);
border-radius: 100rem;
.btn {
width: 2.875rem;
height: 2.875rem;
background: var(--overlay-color);
border: none;
border-radius: 50%;
color: white;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background 0.3s;
z-index: 1;
svg {
width: 1.625rem;
}
}
.number-display {
height: 2.875rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 0 1rem;
&::-moz-focus-inner {
border: none;
}
&.with-btns {
padding: 0 1.9375rem;
margin-left: -1.4375rem;
margin-right: -1.4375rem;
max-width: 9.125rem;
}
}
/* Label */
.number-display .label {
font-size: 0.625rem;
font-weight: 400;
opacity: 0.7;
}
/* Value */
.number-display .value {
font-size: 1.3125rem;
display: flex;
width: 100%;
color: white;
text-align: center;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
-moz-appearance: textfield;
margin: 0;
}
}
}

View file

@ -17,6 +17,7 @@ import ModalDialog from './ModalDialog';
import Multiselect from './Multiselect';
import MultiselectMenu from './MultiselectMenu';
import { HorizontalNavBar, VerticalNavBar } from './NavBar';
import NumberInput from './NumberInput';
import Popup from './Popup';
import RadioButton from './RadioButton';
import SearchBar from './SearchBar';
@ -48,6 +49,7 @@ export {
MultiselectMenu,
HorizontalNavBar,
VerticalNavBar,
NumberInput,
Popup,
RadioButton,
SearchBar,