UNPKG

@chayns-components/core

Version:

A set of beautiful React components for developing your own applications with chayns.

275 lines (273 loc) • 8.79 kB
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