@chayns-components/core
Version:
A set of beautiful React components for developing your own applications with chayns.
275 lines (273 loc) • 8.79 kB
JavaScript
import { AnimatePresence } from 'motion/react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { checkForValidAmount } from '../../utils/amountControl';
import Icon from '../icon/Icon';
import { StyledAmountControl, StyledAmountControlInput, StyledAmountControlPseudoInput, StyledMotionAmountControlButton } from './AmountControl.styles';
const AmountControl = _ref => {
let {
amount,
icon,
iconColor,
isDisabled = false,
label,
maxAmount,
minAmount = 0,
onChange,
shouldShowAddIconOnMinAmount = false,
shouldShowIcon = true,
shouldShowWideInput = false,
step: stepProp = 1
} = _ref;
const [amountValue, setAmountValue] = useState(minAmount);
const [inputValue, setInputValue] = useState(minAmount.toString());
const [displayState, setDisplayState] = useState('default');
const step = useMemo(() => Number.isSafeInteger(stepProp) && stepProp >= 1 ? stepProp : 1, [stepProp]);
const inputRef = useRef(null);
/**
* This function controls the displayState
*/
useEffect(() => {
switch (true) {
case maxAmount === 1 && amountValue === 1:
setDisplayState('delete');
return;
case maxAmount && amountValue >= maxAmount:
setDisplayState('maxAmount');
return;
case amountValue > minAmount:
setDisplayState('normal');
return;
case amountValue === minAmount && amountValue >= 0 && shouldShowAddIconOnMinAmount:
setDisplayState('minAmount');
return;
default:
setDisplayState('default');
}
}, [amountValue, maxAmount, minAmount, shouldShowAddIconOnMinAmount]);
const hasFocus = useMemo(() => displayState !== 'default', [displayState]);
/**
* Function that sets the amountValue to the amount
*/
useEffect(() => {
if (typeof amount !== 'number') {
return;
}
setAmountValue(checkForValidAmount({
amount,
maxAmount,
minAmount
}));
setInputValue(checkForValidAmount({
amount,
maxAmount,
minAmount
}).toString());
}, [amount, maxAmount, minAmount]);
const handleAmountAdd = useCallback(() => {
setAmountValue(prevState => {
const newAmount = checkForValidAmount({
amount: prevState + step,
minAmount,
maxAmount
});
if (typeof onChange === 'function') {
onChange(newAmount);
}
return typeof amount === 'number' ? prevState : newAmount;
});
setInputValue(prevState => checkForValidAmount({
amount: Number(prevState) + step,
minAmount,
maxAmount
}).toString());
}, [amount, maxAmount, minAmount, onChange, step]);
const handleAmountRemove = useCallback(() => {
if (displayState === 'default') {
return;
}
setAmountValue(prevState => {
const newAmount = checkForValidAmount({
amount: prevState - step,
minAmount,
maxAmount
});
if (typeof onChange === 'function') {
onChange(newAmount);
}
return newAmount;
});
setInputValue(prevState => checkForValidAmount({
amount: Number(prevState) - step,
minAmount,
maxAmount
}).toString());
}, [displayState, maxAmount, minAmount, onChange, step]);
const handleFirstAmount = useCallback(() => {
if (amountValue !== minAmount) {
return;
}
if (typeof onChange === 'function') {
onChange(minAmount + step);
}
setAmountValue(minAmount + step);
setInputValue((minAmount + step).toString());
}, [amountValue, minAmount, onChange, step]);
const handleDeleteIconClick = useCallback(() => {
if (inputValue === '0') {
window.setTimeout(() => {
inputRef.current?.focus();
}, 500);
} else {
handleAmountRemove();
}
}, [handleAmountRemove, inputValue]);
const handleInputBlur = useCallback(() => {
const checkedValue = checkForValidAmount({
minAmount,
maxAmount,
amount: Math.round(Number(inputValue) / step) * step
});
setAmountValue(checkedValue);
setInputValue(checkedValue.toString());
if (typeof onChange === 'function') {
onChange(checkedValue);
}
if (inputValue === '') {
setInputValue(minAmount.toString());
}
}, [inputValue, maxAmount, minAmount, onChange, step]);
const handleInputChange = useCallback(event => {
const {
value
} = event.target;
const valueBefore = Number(value.replace(/\D/g, ''));
if (valueBefore < minAmount && minAmount === 0) {
setInputValue('0');
return;
}
setInputValue(valueBefore === 0 ? '' : valueBefore.toString());
}, [minAmount]);
const leftIcon = useMemo(() => {
let item = null;
switch (displayState) {
case 'default':
item = /*#__PURE__*/React.createElement(Icon, {
icons: [icon ?? 'fa fa-cart-shopping'],
size: 15,
color: iconColor
});
break;
case 'delete':
item = /*#__PURE__*/React.createElement(Icon, {
icons: ['fa ts-check'],
size: 20,
color: "white"
});
break;
case 'normal':
item = /*#__PURE__*/React.createElement(Icon, {
icons: ['fa fa-minus'],
size: 15,
color: "red"
});
break;
case 'maxAmount':
item = /*#__PURE__*/React.createElement(Icon, {
icons: ['fa fa-minus'],
size: 15,
color: "red"
});
break;
default:
break;
}
return item;
}, [displayState, icon, iconColor]);
const shouldShowLeftIcon = useMemo(() => {
if (shouldShowAddIconOnMinAmount && displayState === 'minAmount') {
return false;
}
if (shouldShowIcon) {
return true;
}
return !((displayState === 'default' || displayState === 'minAmount') && !shouldShowIcon);
}, [displayState, shouldShowAddIconOnMinAmount, shouldShowIcon]);
return useMemo(() => /*#__PURE__*/React.createElement(StyledAmountControl, {
onClick: handleFirstAmount,
$isDisabled: isDisabled
}, /*#__PURE__*/React.createElement(AnimatePresence, {
initial: false
}, shouldShowLeftIcon && /*#__PURE__*/React.createElement(StyledMotionAmountControlButton, {
key: "right_button",
initial: {
width: 0,
opacity: 0,
padding: 0
},
animate: {
width: displayState === 'normal' || displayState === 'maxAmount' ? 40 : 28,
opacity: 1,
padding: 0
},
exit: {
width: 0,
opacity: 0,
padding: 0
},
$isWide: displayState === 'normal' || displayState === 'maxAmount',
transition: {
duration: 0.2,
type: 'tween'
},
onClick: handleAmountRemove,
$color: displayState === 'delete' ? 'rgb(32, 198, 90)' : undefined,
disabled: amountValue !== 0 && amountValue <= minAmount,
$isDisabled: amountValue !== 0 && amountValue <= minAmount
}, leftIcon)), displayState === 'delete' || inputValue === '0' ? /*#__PURE__*/React.createElement(StyledAmountControlPseudoInput, {
onClick: handleDeleteIconClick,
$shouldShowWideInput: shouldShowWideInput
}, displayState === 'default' && label ? label : inputValue) : /*#__PURE__*/React.createElement(StyledAmountControlInput, {
ref: inputRef,
$displayState: displayState,
$shouldShowIcon: shouldShowIcon,
$shouldShowWideInput: shouldShowWideInput,
$hasFocus: hasFocus,
onBlur: handleInputBlur,
onChange: handleInputChange,
value: displayState === 'default' && label ? label : inputValue
}), /*#__PURE__*/React.createElement(AnimatePresence, {
initial: false
}, (displayState === 'normal' || displayState === 'minAmount') && /*#__PURE__*/React.createElement(StyledMotionAmountControlButton, {
key: "right_button",
initial: {
width: 0,
opacity: 0,
padding: 0
},
animate: {
width: 40,
opacity: 1,
padding: 0
},
exit: {
width: 0,
opacity: 0,
padding: 0
},
transition: {
duration: 0.2,
type: 'tween'
},
onClick: handleAmountAdd,
$isWide: true,
disabled: maxAmount ? amountValue >= maxAmount : false,
$isDisabled: maxAmount ? amountValue >= maxAmount : false
}, /*#__PURE__*/React.createElement(Icon, {
icons: ['fa fa-plus'],
size: 15,
color: "green"
})))), [amountValue, displayState, handleAmountAdd, handleAmountRemove, handleDeleteIconClick, handleFirstAmount, handleInputBlur, handleInputChange, hasFocus, inputValue, isDisabled, label, leftIcon, maxAmount, minAmount, shouldShowIcon, shouldShowLeftIcon, shouldShowWideInput]);
};
AmountControl.displayName = 'AmountControl';
export default AmountControl;
//# sourceMappingURL=AmountControl.js.map