Updates
This commit is contained in:
@@ -8,7 +8,7 @@ let pressInterval: any;
|
||||
let pressTimeout: any;
|
||||
|
||||
type Props = Pick<InputProps<any>, "min" | "max" | "step"> & {
|
||||
updateValue: (v: string) => void;
|
||||
setValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
getNormalizedValue: (v: string) => void;
|
||||
buttonDownRef: React.MutableRefObject<boolean>;
|
||||
inputRef: React.RefObject<HTMLInputElement | null>;
|
||||
@@ -19,7 +19,7 @@ type Props = Pick<InputProps<any>, "min" | "max" | "step"> & {
|
||||
*/
|
||||
export default function NumberInputButtons({
|
||||
getNormalizedValue,
|
||||
updateValue,
|
||||
setValue,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
@@ -65,12 +65,14 @@ export default function NumberInputButtons({
|
||||
const existingNumberValue = twuiNumberfy(existingValue);
|
||||
|
||||
if (max && existingNumberValue >= twuiNumberfy(max)) {
|
||||
return updateValue(String(max));
|
||||
return setValue(String(max));
|
||||
} else if (min && existingNumberValue < twuiNumberfy(min)) {
|
||||
return updateValue(String(min));
|
||||
return setValue(String(min));
|
||||
} else {
|
||||
updateValue(
|
||||
String(existingNumberValue + twuiNumberfy(step || DEFAULT_STEP))
|
||||
setValue(
|
||||
String(
|
||||
existingNumberValue + twuiNumberfy(step || DEFAULT_STEP),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -80,10 +82,12 @@ export default function NumberInputButtons({
|
||||
const existingNumberValue = twuiNumberfy(existingValue);
|
||||
|
||||
if (min && existingNumberValue <= twuiNumberfy(min)) {
|
||||
updateValue(String(min));
|
||||
setValue(String(min));
|
||||
} else {
|
||||
updateValue(
|
||||
String(existingNumberValue - twuiNumberfy(step || DEFAULT_STEP))
|
||||
setValue(
|
||||
String(
|
||||
existingNumberValue - twuiNumberfy(step || DEFAULT_STEP),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,13 +27,16 @@ let timeout: any;
|
||||
let validationFnTimeout: any;
|
||||
let externalValueChangeTimeout: any;
|
||||
|
||||
export type InputProps<KeyType extends string> = DetailedHTMLProps<
|
||||
InputHTMLAttributes<HTMLInputElement>,
|
||||
HTMLInputElement
|
||||
export type InputProps<KeyType extends string> = Omit<
|
||||
DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>,
|
||||
"prefix" | "suffix"
|
||||
> &
|
||||
DetailedHTMLProps<
|
||||
TextareaHTMLAttributes<HTMLTextAreaElement>,
|
||||
HTMLTextAreaElement
|
||||
Omit<
|
||||
DetailedHTMLProps<
|
||||
TextareaHTMLAttributes<HTMLTextAreaElement>,
|
||||
HTMLTextAreaElement
|
||||
>,
|
||||
"prefix" | "suffix"
|
||||
> & {
|
||||
label?: string;
|
||||
variant?: "normal" | "warning" | "error" | "inactive";
|
||||
@@ -60,16 +63,14 @@ export type InputProps<KeyType extends string> = DetailedHTMLProps<
|
||||
invalidMessage?: string;
|
||||
validationFunction?: (
|
||||
value: string,
|
||||
element?: HTMLInputElement | HTMLTextAreaElement
|
||||
element?: HTMLInputElement | HTMLTextAreaElement,
|
||||
) => Promise<TWUISelectValidityObject>;
|
||||
changeHandler?: (
|
||||
value: string,
|
||||
element?: HTMLInputElement | HTMLTextAreaElement
|
||||
) => void;
|
||||
changeHandler?: (value: string) => void;
|
||||
autoComplete?: (typeof AutocompleteOptions)[number];
|
||||
name?: KeyType;
|
||||
valueUpdate?: string;
|
||||
numberText?: boolean;
|
||||
rawNumber?: boolean;
|
||||
setReady?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
decimal?: number;
|
||||
info?: string | ReactNode;
|
||||
@@ -91,7 +92,7 @@ let refreshes = 0;
|
||||
* @className twui-clear-input-field-button
|
||||
*/
|
||||
export default function Input<KeyType extends string>(
|
||||
inputProps: InputProps<KeyType>
|
||||
inputProps: InputProps<KeyType>,
|
||||
) {
|
||||
const {
|
||||
label,
|
||||
@@ -119,10 +120,12 @@ export default function Input<KeyType extends string>(
|
||||
changeHandler,
|
||||
validity: existingValidity,
|
||||
clearInputProps,
|
||||
rawNumber,
|
||||
...props
|
||||
} = inputProps;
|
||||
|
||||
function getFinalValue(v: any) {
|
||||
if (rawNumber) return twuiNumberfy(v);
|
||||
if (numberText) {
|
||||
return (
|
||||
twuiNumberfy(v, decimal).toLocaleString() +
|
||||
@@ -141,16 +144,19 @@ export default function Input<KeyType extends string>(
|
||||
const [validity, setValidity] = React.useState<TWUISelectValidityObject>(
|
||||
existingValidity || {
|
||||
isValid: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const inputRef = componentRef || React.useRef<HTMLInputElement>(null);
|
||||
const textAreaRef = componentRef || React.useRef<HTMLTextAreaElement>(null);
|
||||
const buttonDownRef = React.useRef(false);
|
||||
|
||||
const [value, setValue] = React.useState(
|
||||
props.defaultValue ? String(props.defaultValue) : "",
|
||||
);
|
||||
const [focus, setFocus] = React.useState(false);
|
||||
const [inputType, setInputType] = React.useState(
|
||||
numberText ? "text" : props.type
|
||||
numberText ? "text" : props.type,
|
||||
);
|
||||
|
||||
const DEFAULT_DEBOUNCE = 500;
|
||||
@@ -180,23 +186,20 @@ export default function Input<KeyType extends string>(
|
||||
setValidity(existingValidity);
|
||||
}, [existingValidity]);
|
||||
|
||||
const updateValueFn = (
|
||||
val: string,
|
||||
el?: HTMLInputElement | HTMLTextAreaElement
|
||||
) => {
|
||||
const updateValueFn = (val: string) => {
|
||||
if (buttonDownRef.current) return;
|
||||
|
||||
if (changeHandler) {
|
||||
window.clearTimeout(externalValueChangeTimeout);
|
||||
externalValueChangeTimeout = setTimeout(() => {
|
||||
changeHandler(val, el);
|
||||
changeHandler(val);
|
||||
}, finalDebounce);
|
||||
}
|
||||
|
||||
if (typeof val == "string") {
|
||||
if (!val.match(/./)) {
|
||||
setValidity({ isValid: true });
|
||||
props.value = "";
|
||||
setValue("");
|
||||
if (istextarea && textAreaRef.current) {
|
||||
textAreaRef.current.value = "";
|
||||
} else if (inputRef?.current) {
|
||||
@@ -223,7 +226,7 @@ export default function Input<KeyType extends string>(
|
||||
if (validationRegex && !validationRegex.test(val)) {
|
||||
return;
|
||||
}
|
||||
validationFunction(val, el).then((res) => {
|
||||
validationFunction(val).then((res) => {
|
||||
setValidity(res);
|
||||
});
|
||||
}, finalDebounce);
|
||||
@@ -233,28 +236,36 @@ export default function Input<KeyType extends string>(
|
||||
|
||||
React.useEffect(() => {
|
||||
if (typeof props.value !== "string" || !props.value.match(/./)) return;
|
||||
updateValueFn(String(props.value));
|
||||
setValue(String(props.value));
|
||||
}, [props.value]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (istextarea && textAreaRef.current) {
|
||||
} else if (inputRef?.current) {
|
||||
inputRef.current.value = getFinalValue(value);
|
||||
}
|
||||
updateValueFn(value);
|
||||
}, [value]);
|
||||
|
||||
function handleValueChange(
|
||||
e: React.ChangeEvent<HTMLInputElement> &
|
||||
React.ChangeEvent<HTMLTextAreaElement>
|
||||
React.ChangeEvent<HTMLTextAreaElement>,
|
||||
) {
|
||||
const newValue = e.target.value;
|
||||
updateValue(newValue, e.target);
|
||||
setValue(newValue);
|
||||
props.onChange?.(e);
|
||||
}
|
||||
|
||||
function updateValue(
|
||||
v: string,
|
||||
el?: HTMLInputElement | HTMLTextAreaElement
|
||||
) {
|
||||
if (istextarea && textAreaRef.current) {
|
||||
} else if (inputRef?.current) {
|
||||
inputRef.current.value = getFinalValue(v);
|
||||
}
|
||||
updateValueFn(v, el);
|
||||
}
|
||||
// function updateValue(
|
||||
// v: string,
|
||||
// el?: HTMLInputElement | HTMLTextAreaElement,
|
||||
// ) {
|
||||
// if (istextarea && textAreaRef.current) {
|
||||
// } else if (inputRef?.current) {
|
||||
// inputRef.current.value = getFinalValue(v);
|
||||
// }
|
||||
// updateValueFn(v);
|
||||
// }
|
||||
|
||||
const targetComponent = istextarea ? (
|
||||
<textarea
|
||||
@@ -265,7 +276,7 @@ export default function Input<KeyType extends string>(
|
||||
className={twMerge(
|
||||
"w-full outline-none bg-transparent grow",
|
||||
"twui-textarea",
|
||||
props.className
|
||||
props.className,
|
||||
)}
|
||||
ref={textAreaRef}
|
||||
onFocus={(e) => {
|
||||
@@ -294,7 +305,7 @@ export default function Input<KeyType extends string>(
|
||||
"dark:bg-transparent dark:outline-none dark:border-none",
|
||||
"p-0 grow",
|
||||
"twui-input",
|
||||
props.className
|
||||
props.className,
|
||||
)}
|
||||
ref={inputRef}
|
||||
onFocus={(e) => {
|
||||
@@ -318,8 +329,8 @@ export default function Input<KeyType extends string>(
|
||||
title={`${finalLabel}${props.required ? " (Required)" : ""}`}
|
||||
{...wrapperWrapperProps}
|
||||
className={twMerge(
|
||||
"w-full gap-1.5 relative z-0 hover:z-10",
|
||||
wrapperWrapperProps?.className
|
||||
"w-full gap-1.5 relative z-0 hover:z-100",
|
||||
wrapperWrapperProps?.className,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
@@ -353,7 +364,7 @@ export default function Input<KeyType extends string>(
|
||||
: "opacity-50 pointer-events-none"
|
||||
: undefined,
|
||||
"twui-input-wrapper",
|
||||
wrapperProps?.className
|
||||
wrapperProps?.className,
|
||||
)}
|
||||
>
|
||||
{showLabel && (
|
||||
@@ -365,7 +376,7 @@ export default function Input<KeyType extends string>(
|
||||
"dark:text-foreground-dark/80 dark:bg-background-dark whitespace-nowrap",
|
||||
"overflow-hidden overflow-ellipsis z-20 px-1.5 rounded-t-default",
|
||||
"twui-input-label",
|
||||
labelProps?.className
|
||||
labelProps?.className,
|
||||
)}
|
||||
>
|
||||
{finalLabel}
|
||||
@@ -378,11 +389,7 @@ export default function Input<KeyType extends string>(
|
||||
</label>
|
||||
)}
|
||||
|
||||
{prefix && (
|
||||
<div className="opacity-60 pointer-events-none whitespace-nowrap">
|
||||
{prefix}
|
||||
</div>
|
||||
)}
|
||||
{prefix && prefix}
|
||||
|
||||
{targetComponent}
|
||||
|
||||
@@ -394,7 +401,7 @@ export default function Input<KeyType extends string>(
|
||||
"p-1 -my-2 -mx-1 opacity-0 cursor-pointer w-7 h-7",
|
||||
"bg-background-light dark:bg-background-dark",
|
||||
"twui-clear-input-field-button",
|
||||
clearInputProps?.className
|
||||
clearInputProps?.className,
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
@@ -406,7 +413,7 @@ export default function Input<KeyType extends string>(
|
||||
textAreaRef.current.value = "";
|
||||
}
|
||||
|
||||
updateValue("");
|
||||
setValue("");
|
||||
clearInputProps?.onClick?.(e);
|
||||
}}
|
||||
>
|
||||
@@ -439,21 +446,11 @@ export default function Input<KeyType extends string>(
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{suffix ? (
|
||||
<div
|
||||
{...suffixProps}
|
||||
className={twMerge(
|
||||
"opacity-60 pointer-events-none whitespace-nowrap",
|
||||
suffixProps?.className
|
||||
)}
|
||||
>
|
||||
{suffix}
|
||||
</div>
|
||||
) : null}
|
||||
{suffix ? suffix : null}
|
||||
|
||||
{numberText ? (
|
||||
<NumberInputButtons
|
||||
updateValue={updateValue}
|
||||
setValue={setValue}
|
||||
inputRef={inputRef}
|
||||
getNormalizedValue={getNormalizedValue}
|
||||
max={props.max}
|
||||
@@ -468,18 +465,22 @@ export default function Input<KeyType extends string>(
|
||||
target={
|
||||
<Row className="gap-1">
|
||||
<Info size={12} className="opacity-40" />
|
||||
<Span size="smaller" className="opacity-70">
|
||||
<Span
|
||||
size="smaller"
|
||||
className="opacity-70 hover:opacity-100"
|
||||
>
|
||||
{info}
|
||||
</Span>
|
||||
</Row>
|
||||
}
|
||||
openDebounce={700}
|
||||
className="z-1000"
|
||||
hoverOpen
|
||||
>
|
||||
<Paper
|
||||
className={twMerge(
|
||||
"min-w-[250px] shadow-lg shadow-slate-200 dark:shadow-white/10",
|
||||
"max-w-[300px] w-full"
|
||||
"max-w-[300px] w-full bg-background-light! dark:bg-background-dark! z-1000",
|
||||
)}
|
||||
>
|
||||
<Stack className="gap-2 items-center">
|
||||
|
||||
Reference in New Issue
Block a user