mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-07 19:28:44 +01:00
refactor input element
This commit is contained in:
parent
2bc995a87a
commit
0e81c7fe46
@ -1,13 +1,25 @@
|
|||||||
import { clsx } from "clsx";
|
import clsx from "clsx";
|
||||||
import React, { forwardRef, useEffect, useRef, useState } from "react";
|
import React, { forwardRef, ReactNode } from "react";
|
||||||
|
|
||||||
import "./input.less";
|
import "./input.less";
|
||||||
|
|
||||||
interface InputDecorationProps {
|
interface InputLeftElementProps {
|
||||||
startDecoration?: React.ReactNode;
|
children: React.ReactNode;
|
||||||
endDecoration?: React.ReactNode;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const InputLeftElement = ({ children, className }: InputLeftElementProps) => {
|
||||||
|
return <div className={clsx("input-left-element", className)}>{children}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface InputRightElementProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const InputRightElement = ({ children, className }: InputRightElementProps) => {
|
||||||
|
return <div className={clsx("input-right-element", className)}>{children}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
interface InputProps {
|
interface InputProps {
|
||||||
label?: string;
|
label?: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
@ -18,13 +30,13 @@ interface InputProps {
|
|||||||
onBlur?: () => void;
|
onBlur?: () => void;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
defaultValue?: string;
|
defaultValue?: string;
|
||||||
decoration?: InputDecorationProps;
|
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
maxLength?: number;
|
maxLength?: number;
|
||||||
autoFocus?: boolean;
|
autoFocus?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
isNumber?: boolean;
|
isNumber?: boolean;
|
||||||
inputRef?: React.MutableRefObject<HTMLInputElement>;
|
inputRef?: React.MutableRefObject<HTMLInputElement>;
|
||||||
|
children?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Input = forwardRef<HTMLDivElement, InputProps>(
|
const Input = forwardRef<HTMLDivElement, InputProps>(
|
||||||
@ -39,65 +51,17 @@ const Input = forwardRef<HTMLDivElement, InputProps>(
|
|||||||
onBlur,
|
onBlur,
|
||||||
placeholder,
|
placeholder,
|
||||||
defaultValue = "",
|
defaultValue = "",
|
||||||
decoration,
|
|
||||||
required,
|
required,
|
||||||
maxLength,
|
maxLength,
|
||||||
autoFocus,
|
autoFocus,
|
||||||
disabled,
|
disabled,
|
||||||
isNumber,
|
isNumber,
|
||||||
inputRef,
|
inputRef,
|
||||||
|
children,
|
||||||
}: InputProps,
|
}: InputProps,
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
const [focused, setFocused] = useState(false);
|
const [internalValue, setInternalValue] = React.useState(defaultValue);
|
||||||
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
||||||
const [error, setError] = useState(false);
|
|
||||||
const [hasContent, setHasContent] = useState(Boolean(value || defaultValue));
|
|
||||||
const internalInputRef = useRef<HTMLInputElement>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (value !== undefined) {
|
|
||||||
setFocused(Boolean(value));
|
|
||||||
}
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
const handleComponentFocus = () => {
|
|
||||||
if (internalInputRef.current && !internalInputRef.current.contains(document.activeElement)) {
|
|
||||||
internalInputRef.current.focus();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleComponentBlur = () => {
|
|
||||||
if (internalInputRef.current?.contains(document.activeElement)) {
|
|
||||||
internalInputRef.current.blur();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSetInputRef = (elem: HTMLInputElement) => {
|
|
||||||
if (inputRef) {
|
|
||||||
inputRef.current = elem;
|
|
||||||
}
|
|
||||||
internalInputRef.current = elem;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFocus = () => {
|
|
||||||
setFocused(true);
|
|
||||||
onFocus && onFocus();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBlur = () => {
|
|
||||||
if (internalInputRef.current) {
|
|
||||||
const inputValue = internalInputRef.current.value;
|
|
||||||
if (required && !inputValue) {
|
|
||||||
setError(true);
|
|
||||||
setFocused(false);
|
|
||||||
} else {
|
|
||||||
setError(false);
|
|
||||||
setFocused(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onBlur && onBlur();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const inputValue = e.target.value;
|
const inputValue = e.target.value;
|
||||||
@ -106,14 +70,6 @@ const Input = forwardRef<HTMLDivElement, InputProps>(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (required && !inputValue) {
|
|
||||||
setError(true);
|
|
||||||
setHasContent(false);
|
|
||||||
} else {
|
|
||||||
setError(false);
|
|
||||||
setHasContent(Boolean(inputValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
setInternalValue(inputValue);
|
setInternalValue(inputValue);
|
||||||
}
|
}
|
||||||
@ -123,54 +79,49 @@ const Input = forwardRef<HTMLDivElement, InputProps>(
|
|||||||
|
|
||||||
const inputValue = value ?? internalValue;
|
const inputValue = value ?? internalValue;
|
||||||
|
|
||||||
|
let leftElement = null;
|
||||||
|
let rightElement = null;
|
||||||
|
React.Children.forEach(children, (child) => {
|
||||||
|
if (React.isValidElement(child)) {
|
||||||
|
if (child.type === InputLeftElement) {
|
||||||
|
leftElement = child;
|
||||||
|
} else if (child.type === InputRightElement) {
|
||||||
|
rightElement = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={clsx("input", className, {
|
className={clsx("input-wrapper", className, {
|
||||||
focused: focused,
|
|
||||||
error: error,
|
|
||||||
disabled: disabled,
|
disabled: disabled,
|
||||||
"no-label": !label,
|
|
||||||
})}
|
})}
|
||||||
onFocus={handleComponentFocus}
|
|
||||||
onBlur={handleComponentBlur}
|
|
||||||
tabIndex={-1}
|
|
||||||
>
|
>
|
||||||
{decoration?.startDecoration && <>{decoration.startDecoration}</>}
|
|
||||||
<div className="input-inner">
|
<div className="input-inner">
|
||||||
{label && (
|
{leftElement && <div className="input-left-decoration">{leftElement}</div>}
|
||||||
<label
|
|
||||||
className={clsx("input-inner-label", {
|
|
||||||
float: hasContent || focused || placeholder,
|
|
||||||
"offset-left": decoration?.startDecoration,
|
|
||||||
})}
|
|
||||||
htmlFor={label}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
<input
|
<input
|
||||||
className={clsx("input-inner-input", {
|
className={clsx("input-inner-input", {
|
||||||
"offset-left": decoration?.startDecoration,
|
"with-left-element": leftElement,
|
||||||
|
"with-right-element": rightElement,
|
||||||
})}
|
})}
|
||||||
ref={handleSetInputRef}
|
ref={inputRef}
|
||||||
id={label}
|
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
onFocus={handleFocus}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
|
onFocus={onFocus}
|
||||||
|
onBlur={onBlur}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
maxLength={maxLength}
|
maxLength={maxLength}
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
|
{rightElement && <div className="input-right-decoration">{rightElement}</div>}
|
||||||
</div>
|
</div>
|
||||||
{decoration?.endDecoration && <>{decoration.endDecoration}</>}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export { Input };
|
export { Input, InputLeftElement, InputRightElement };
|
||||||
export type { InputDecorationProps, InputProps };
|
export type { InputLeftElementProps, InputProps, InputRightElementProps };
|
||||||
|
@ -129,4 +129,5 @@ Palette.displayName = "Palette";
|
|||||||
PaletteButton.displayName = "PaletteButton";
|
PaletteButton.displayName = "PaletteButton";
|
||||||
PaletteContent.displayName = "PaletteContent";
|
PaletteContent.displayName = "PaletteContent";
|
||||||
|
|
||||||
export { Palette, PaletteButton, PaletteContent, type PaletteButtonProps, type PaletteContentProps };
|
export { Palette, PaletteButton, PaletteContent };
|
||||||
|
export type { PaletteButtonProps, PaletteContentProps };
|
||||||
|
Loading…
Reference in New Issue
Block a user