UNPKG

@coinmeca/ui

Version:

This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

211 lines 10.6 kB
"use client"; import { Controls, Elements } from "../../../components"; import { format } from "../../../lib/utils"; import { useCallback, useEffect, useRef, useState } from "react"; import Style, { Dot, Inner, Side, Wrapper } from "./Input.styled"; export default function Input(props) { const wrapper = useRef(null); const input = useRef(null); const placeholder = props?.placeholder?.toString() || ""; const type = props?.type === "password" ? "password" : props?.type || "text"; const fold = props?.fold || false; const step = (typeof props?.step === "number" && !isNaN(props?.step) && props?.step) || 1; const scale = props?.scale || 1; const min = props?.min || 0; const align = props?.align || "left"; const gap = props?.gap || 1; const clearPosition = props?.clearPosition || "right"; const formatter = useCallback((value) => { if (!value || value === "") return ""; if (value?.toString() === "NaN") value = ""; if (type === "number" || type === "currency") { if (!props.readOnly && !value.toString().endsWith(".")) { let copy = parseFloat(format(value, "number", props?.lock, props?.fix, props?.max)); value = typeof props?.min === "number" && props?.min >= copy ? props?.min : typeof props?.max === "number" && props?.max <= copy ? props?.max : value; } } // else if (typeof props?.length === 'number' && props?.length > 0 && props?.length > value?.toString()?.length) value = value?.toString()?.substring(0, props?.length) return format(value, type, props?.lock, props?.fix, props?.max); }, [type, props.min, props.max, props.lock, props.fix, props?.length, props?.readOnly]); const [focus, setFocus] = useState(false); const [expand, setExpand] = useState(!fold); const [value, setValue] = useState(formatter(props?.value)); const [error, setError] = useState(props?.error || false); const handleClick = (e) => { if (props?.lock || props?.disabled) return; input.current.focus(); if (typeof props?.onClick === "function") { props?.onClick(e); } }; const handleClickOutside = (e) => { if (fold && wrapper.current && !wrapper.current.contains(e.target)) setExpand(false); }; const handleChange = (e) => { if (props?.lock || props?.disabled) return; const v = formatter(typeof e === "object" ? e?.target?.value : e); if (v !== value) { setValue(v); if (typeof props?.onChange === "function") props?.onChange(e?.target || input?.current, v); } }; const handleFocus = (e) => { if (props?.lock || props?.disabled) return; if (typeof props?.onFocus === "function") props?.onFocus(e); }; const handleClear = () => { input.current.focus(); handleChange(""); if (typeof props?.onClear === "function") props?.onClear(); setError(false); }; const handleFocusOut = () => { if (typeof props?.onFocusOut === "function") props?.onFocusOut(); setFocus(false); }; const handleBlur = () => { if (typeof props?.onBlur === "function") props?.onBlur(); setFocus(false); }; const handleKeyDown = (e) => { if (props?.lock || props?.disabled) return; const key = e.keyCode; if (((type === "currency" || type === "number") && ((key >= 48 && key <= 57) || (key >= 96 && key <= 105) || (key === 110 && key === 190))) || key === 38 || key === 107 || key === 187 || key === 40 || key === 109 || key === 189) { let copy = 0; if (key === 38 || key === 107 || key === 187) { if (e.shiftKey && e.ctrlKey) { copy = value.toString() === "" ? step : parseFloat(value.toString().replaceAll(",", "")) + Math.abs(step * 100); } else if (e.shiftKey) { copy = value.toString() === "" ? step : parseFloat(value.toString().replaceAll(",", "")) + Math.abs(step * 10); } else { copy = value.toString() === "" ? step : parseFloat(value.toString().replaceAll(",", "")) + Math.abs(step); } copy = formatter(copy); setValue(copy); if (typeof props?.onChange === "function") props?.onChange(e, copy); } if (key === 40 || key === 109 || key === 189) { if (e.shiftKey && e.ctrlKey) { copy = value.toString() === "" ? 0 : parseFloat(value.toString().replaceAll(",", "")) - Math.abs(step * 100); } else if (e.shiftKey) { copy = value.toString() === "" ? 0 : parseFloat(value.toString().replaceAll(",", "")) - Math.abs(step * 10); } else { copy = value.toString() === "" ? 0 : parseFloat(value.toString().replaceAll(",", "")) - Math.abs(step); } copy = formatter(copy); setValue(copy); if (typeof props?.onChange === "function") props?.onChange(e, copy); } } if (typeof props?.onKeyDown === "function") { props?.onKeyDown(key); } }; useEffect(() => { const v = formatter(props?.value); setValue(v); if (typeof props?.onChange === "function") props?.onChange(input?.current, v); }, [props?.value, type, props?.fix, props?.min, props?.max, props?.readOnly, props?.lock, props?.disabled, formatter]); useEffect(() => { if (typeof props?.error === "boolean") setError(props?.error); }, [props?.error]); useEffect(() => { if (fold && expand) input?.current?.focus?.(); }, [fold, expand]); useEffect(() => { if (fold) document.addEventListener("mousedown", handleClickOutside); else setExpand(true); return () => { if (fold) document.removeEventListener("mousedown", handleClickOutside); else setExpand(true); }; }, [fold]); useEffect(() => { if ((fold && expand) || focus || props?.autoFocus) input?.current?.focus(); }, [fold, expand, focus, props?.autoFocus]); const Input = (<Style tabIndex={5} ref={wrapper} style={props?.style?.wrapper} $clearable={props?.clearable} $scale={scale} $gap={gap} $type={type} $fold={fold} $expand={expand} $focus={focus} $align={align} $error={error} $lock={props?.lock} $disabled={props?.disabled} // onClick={() => !(props?.lock || props?.disabled) && setFocus(true)} onBlur={() => !fold && handleBlur()} data-active={focus} data-show={props?.show} data-hide={props?.hide}> <div> <div style={props?.style?.input || props?.style}> {props?.left && (<Side $width={props?.left?.width} $gap={gap} style={props?.left?.style}> {props?.left?.children} </Side>)} <Inner $gap={gap} $expand={expand}> {props?.clearable && clearPosition === "left" && (<Controls.Button icon={"x"} style={{ marginRight: ".5rem" }} hide={value.toString().length === 0} onClick={handleClear} fit/>)} <input ref={input} style={{ textAlign: align }} placeholder={placeholder} type={type === "currency" ? "currency" : type} inputMode={props?.inputMode ? props?.inputMode : type === "number" || type === "currency" ? "numeric" : undefined} min={min} max={props?.max} size={props?.length} maxLength={props?.length} step={props?.step} value={formatter(value)} onClick={handleClick} onInput={handleChange} onChange={handleChange} onFocus={handleFocus} onBlur={handleFocusOut} onKeyDown={handleKeyDown} autoFocus={(fold && expand) || focus || props?.autoFocus} disabled={props?.disabled} readOnly={props?.lock || props?.disabled}/> {props?.clearable && clearPosition === "right" && (<Controls.Button icon={"x"} style={{ marginLeft: ".5rem" }} hide={value.toString().length === 0} onClick={handleClear} fit/>)} </Inner> {(props?.unit || props?.right) && (<Side $width={props?.right?.width} $gap={gap} style={props?.right?.style}> {props?.right?.children} {props?.unit && <span>{props?.unit}</span>} </Side>)} </div> {fold && <Dot $active={!!value?.toString()?.length} $expand={expand}/>} </div> {typeof props?.message === "string" || typeof props?.message === "number" ? (<Elements.Text type={"desc"}>{props?.message}</Elements.Text>) : props?.message?.$$typeof ? (props?.message) : typeof props?.message === "object" && props?.message?.children ? ((typeof props?.message?.children === "string" || typeof props?.message?.children === "number") && (<Elements.Text {...props?.message} align={props?.message?.align || "left"} type={props?.message?.type || "desc"} style={{ ...props?.message?.style, marginTop: `${props?.message?.gap || 0.5}em` }}> {props?.message?.children} </Elements.Text>)) : (props?.message?.children?.$$typeof && { ...props?.message?.children, style: { ...props?.message?.children?.style, ...props?.message?.style, marginTop: `${props?.message?.gap || 1}em`, }, })} </Style>); if (fold) { return (<Wrapper ref={wrapper} $foldPosition={props?.foldPosition} $expand={expand} onClick={() => setExpand(true)} onBlur={handleBlur} data-show={props?.show} data-hide={props?.hide}> <div>{Input}</div> </Wrapper>); } return <>{Input}</>; } //# sourceMappingURL=Input.jsx.map