import React, {createRef, FC, ReactNode, useEffect, useRef, useState} from "react"; import classNames from "classnames"; import {debounce} from 'lodash' import {lastChanged, NumberFieldType} from "./LastChanged"; import getSymbolFromCurrency from "currency-symbol-map"; import {__, CouponsPlus} from "../../globals"; import input from "../../fields/Input"; import {renderCurrencyValue} from "./helpers"; export type NumberInputProps = { id?: string, // this needs to be unique if you have several instances at once, for focusing the input on change numberId: string, // this may be repeated from a group fieldType: NumberFieldType, // even this, but this is enough for single ranges (no extra instances) value: number, onChange: (value: number) => void, type?: 'currency' | 'percentage' | 'free' | 'custom', customRenderer?: ReactNode, // for 'custom' === type (unexpected yoda) labels?: { inputRightOutside: string | ReactNode; inputLeftOutside: string | ReactNode; top?: string, bottom?: string, beforeSymbol?: string, afterSymbol?: string, inputLeft?: string | ReactNode, inputRight?: string | ReactNode, lineHeight?: { top?: string, bottom?: string, } }, input?: { top?: FC }, size?: 'normal' | 'small', } type ButtonAction = 'increase' | 'decrease'; function convert(value: string | number) { return typeof value === 'string'? value.includes('.') ? parseFloat(value) : parseInt(value) : value } export const NumberInput: FC = ({ id, numberId, fieldType, value, onChange, type, labels, input, size = 'normal', customRenderer }) => { const [pressed, setPressed] = useState(null); const [focused, setFocused] = useState(false); const inputRef = useRef(null) // @ts-ignore useEffect(() => { if (value == 0) { return } if (!value) { onChange(0) } }, [value]) const increaseOrDecrease = (action: ButtonAction) => () => onChange(action === 'increase' ? value + 1 : value - 1); if (pressed !== null) { debounce(increaseOrDecrease(pressed), 80)() } /** * Because the re-rendering seems to be too slow (that's my guess) * Everytime the user enters 2 or more digits at once, the input gets blurred and the second number never gets captured * so let's focus this everytime the value changes so that the user may add several numbers at once (eg: type the number "20") */ useEffect(() => { /** * Unfortunately, due to re-renders we have to use this hack. otherwise, the range field would always focus the second input since * for some reason this component is unmounting and mounting on every update. * I know i should probably fox the root, but that could probably take DAYS which I don't have rn. */ if (lastChanged.fieldType === 'text') { return } if (false && typeof lastChanged.id !== 'undefined') { // only there's an id, halt execution if its not this one if (lastChanged.id !== id) { return } } if (lastChanged.numberId === numberId && lastChanged.fieldType === fieldType) { inputRef.current?.focus() } }, [value, pressed]) const fontSizes = { normal: 'text-5x', small: 'text-3x', } const isCustomRender = type === 'custom' || type === 'free'; return <> {labels?.beforeSymbol} {input?.top && }
{labels?.top &&

{labels.top}

}
{labels?.inputLeftOutside} {!isCustomRender && { // we need to overhaul it here. // so first /** * 1 - add a settimeout listener, to only trigger this after about 150ms * 2- then check if after that its still pressed, if so, start the autp-icnrease * 3- if not, that means it was a simple clieck, so abandon it. * */ // lets ignore it for now as it is clashing with regular clicks //setPressed('increase') increaseOrDecrease('decrease')() }} onMouseUpCapture={() => setPressed(null)}> }
{labels?.inputLeft} {(type === 'currency' ? renderCurrencyValue : v => v)(<> {type === 'free' && {__('FREE')}} {type === 'custom' && customRenderer} {!isCustomRender && { onChange(convert(value)) }} onClick={() => { inputRef.current?.select() }} onFocus={({target}) => { const input = target as HTMLInputElement //input.select() setFocused(true) }} onBlur={({target}) => { setFocused(false) }} style={{ width: 16 * (value == 0 ? 1 : (`${value}`.trim().length || 1)) }} />} )} {type === 'percentage' && %} {labels?.inputRight}
{!isCustomRender && { // we need to overhaul it here. // so first /** * 1 - add a settimeout listener, to only trigger this after about 150ms * 2- then check if after that its still pressed, if so, start the autp-icnrease * 3- if not, that means it was a simple clieck, so abandon it. * */ // lets ignore it for now as it is clashing with regular clicks //setPressed('increase') increaseOrDecrease('increase')() }} onMouseUpCapture={() => setPressed(null)}> } {labels?.inputRightOutside}
{labels?.bottom &&

{labels.bottom}

}
{labels?.afterSymbol} ; }