Files
aurora-admin/frameworks/Input.jsx
T

133 lines
3.8 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useRef } from 'react';
import './Input.css';
/**
* Aurora Admin Input(React)
* 对齐 input.json 契约:
* - size: large | default | small
* - state: default | focus | error | disabled
* - feature: prefix | suffix | clearable | number | password
* 受控用法:<Input value={v} onChange={setV} />
* 非受控用法:<Input defaultValue="..." />
*/
const sizeMap = { large: 'au-input-lg', default: 'au-input-md', small: 'au-input-sm' };
const EyeIcon = () => (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z" />
<circle cx="12" cy="12" r="3" />
</svg>
);
const EyeOffIcon = () => (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M17.94 17.94A10 10 0 0 1 12 19c-6.5 0-10-7-10-7a18 18 0 0 1 5.06-5.94M9.9 4.24A9 9 0 0 1 12 4c6.5 0 10 7 10 7a18 18 0 0 1-2.16 3.19M1 1l22 22" />
</svg>
);
const CloseIcon = () => (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M18 6L6 18M6 6l12 12" />
</svg>
);
export default function Input({
value,
defaultValue = '',
size = 'default',
type = 'text',
placeholder = '',
disabled = false,
error = false,
errorText = '',
clearable = false,
className = '',
prefix,
suffix,
onChange,
onFocus,
onBlur,
...rest
}) {
const isControlled = value !== undefined;
const [inner, setInner] = useState(defaultValue);
const [showPassword, setShowPassword] = useState(false);
const inputRef = useRef(null);
const current = isControlled ? value : inner;
const handleInput = (e) => {
const next = e.target.value;
if (!isControlled) setInner(next);
onChange && onChange(next, e);
};
const handleClear = () => {
if (!isControlled) setInner('');
onChange && onChange('');
requestAnimationFrame(() => inputRef.current && inputRef.current.focus());
};
const resolvedType = type === 'password' ? (showPassword ? 'text' : 'password') : type;
const classes = [
'au-input',
sizeMap[size],
error ? 'is-error' : '',
disabled ? 'is-disabled' : '',
className
].filter(Boolean).join(' ');
return (
<div className={`au-field${error ? ' has-error' : ''}`}>
<div className={classes}>
{prefix && <span className="affix prefix">{prefix}</span>}
<input
ref={inputRef}
type={resolvedType}
value={current}
placeholder={placeholder}
disabled={disabled}
onChange={handleInput}
onFocus={onFocus}
onBlur={onBlur}
{...rest}
/>
{clearable && current && !disabled && (
<button className="action clear" type="button" aria-label="清除" onClick={handleClear}>
<CloseIcon />
</button>
)}
{type === 'password' && (
<button
className="action toggle"
type="button"
aria-label={showPassword ? '隐藏密码' : '显示密码'}
onClick={() => setShowPassword((v) => !v)}
>
{showPassword ? <EyeOffIcon /> : <EyeIcon />}
</button>
)}
{suffix && <span className="affix suffix">{suffix}</span>}
</div>
{error && errorText && <span className="error-text">{errorText}</span>}
</div>
);
}
/*
用法示例:
import Input from './Input';
<Input placeholder="请输入客户名称" />
<Input size="large" placeholder="大尺寸" />
<Input error errorText="联系电话格式不正确" defaultValue="abc" />
<Input clearable defaultValue="可清除文本" />
<Input type="password" defaultValue="secret123" />
<Input prefix={<SearchIcon />} placeholder="搜索订单编号" />
<Input value={name} onChange={setName} />
*/