fix(ui): PasswordInput 眼睛移入输入框,隐藏 Edge 原生显隐按钮
- Input 新增 suffix 插槽,眼睛不再并排在外 - 隐藏 ::-ms-reveal / ::-ms-clear,避免 Edge 聚焦时出现两个眼睛 - 展示页补 suffix 示例;COMPONENTS.md / README 同步;版本 0.3.1
This commit is contained in:
@@ -62,6 +62,19 @@
|
||||
color: var(--su-text-muted);
|
||||
}
|
||||
|
||||
.suffix {
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Edge 会给 type=password 的输入框塞一个原生显隐按钮(聚焦时出现),
|
||||
和 PasswordInput 自己的按钮叠成两个眼睛;这里把它隐藏。 */
|
||||
.input::-ms-reveal,
|
||||
.input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.clear {
|
||||
--clear-size: 20px;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CSSProperties, InputHTMLAttributes, Ref } from 'react'
|
||||
import type { CSSProperties, InputHTMLAttributes, ReactNode, Ref } from 'react'
|
||||
import { Icon, type IconName } from './Icon'
|
||||
import styles from './Input.module.css'
|
||||
|
||||
@@ -23,6 +23,8 @@ export type InputProps = {
|
||||
onClear?: () => void
|
||||
/** 右侧提示,如 `⌘K`;仅作展示,不参与键盘处理 */
|
||||
shortcut?: string
|
||||
/** 右侧插槽:放图标按钮、单位等(例如密码显隐、复制)。与 `clearable` 同时存在时排在清除按钮之前 */
|
||||
suffix?: ReactNode
|
||||
inputRef?: Ref<HTMLInputElement>
|
||||
className?: string
|
||||
style?: CSSProperties
|
||||
@@ -34,6 +36,7 @@ export function Input({
|
||||
clearable = false,
|
||||
onClear,
|
||||
shortcut,
|
||||
suffix,
|
||||
inputRef,
|
||||
className,
|
||||
style,
|
||||
@@ -52,6 +55,8 @@ export function Input({
|
||||
|
||||
<input ref={inputRef} className={styles.input} value={value} {...rest} />
|
||||
|
||||
{suffix !== undefined && <span className={styles.suffix}>{suffix}</span>}
|
||||
|
||||
{shortcut !== undefined && <kbd className={styles.shortcut}>{shortcut}</kbd>}
|
||||
|
||||
{showClear && (
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
.root {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--su-space-2);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 输入框吃掉剩余宽度,眼睛按钮保持自身尺寸 */
|
||||
.input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
@@ -2,15 +2,17 @@ import { useState, type CSSProperties, type InputHTMLAttributes } from 'react'
|
||||
import { Icon, type IconName } from './Icon'
|
||||
import { IconButton } from './IconButton'
|
||||
import { Input, type InputSize } from './Input'
|
||||
import styles from './PasswordInput.module.css'
|
||||
|
||||
/**
|
||||
* 密码输入框:`Input` + 右侧显隐切换。
|
||||
* 密码输入框:`Input` + 输入框内部的显隐切换。
|
||||
*
|
||||
* 为什么单独成组件而不是每个页面拼一遍:密码框的「锁图标 + 眼睛按钮 +
|
||||
* aria 名称 + type 切换」是固定搭配,手拼时很容易漏掉显隐按钮(Stash 的
|
||||
* 系统改密页就漏过一次),而漏掉之后用户无法确认自己输的是什么。
|
||||
*
|
||||
* 眼睛放在输入框内部(`Input` 的 suffix 槽)而不是外面并排:
|
||||
* 并排时会和 Edge 原生的密码显隐按钮叠成两个眼睛,视觉上也更像一个控件。
|
||||
*
|
||||
* 事件语义与 `Input` 一致:`onChange` 收到的是原生事件,Enter 等键盘逻辑
|
||||
* 用 `onKeyDown`。输入框本身不知道「密码是否正确」这类业务状态。
|
||||
*/
|
||||
@@ -39,28 +41,25 @@ export function PasswordInput({
|
||||
}: PasswordInputProps) {
|
||||
const [visible, setVisible] = useState(false)
|
||||
|
||||
// 眼睛按钮的高度跟随输入框,避免并排时上下错位
|
||||
const buttonSize = size === 'lg' ? 'md' : size === 'sm' ? 'xs' : 'sm'
|
||||
|
||||
return (
|
||||
<div className={[styles.root, className].filter(Boolean).join(' ')} style={style}>
|
||||
<Input
|
||||
{...rest}
|
||||
className={styles.input}
|
||||
size={size}
|
||||
type={visible ? 'text' : 'password'}
|
||||
icon={icon ?? undefined}
|
||||
/>
|
||||
|
||||
{toggle && (
|
||||
<IconButton
|
||||
label={visible ? '隐藏密码' : '显示密码'}
|
||||
aria-pressed={visible}
|
||||
size={buttonSize}
|
||||
icon={<Icon name={visible ? 'eye_close_line' : 'eye_line'} />}
|
||||
onClick={() => setVisible((current) => !current)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<Input
|
||||
{...rest}
|
||||
className={className}
|
||||
style={style}
|
||||
size={size}
|
||||
type={visible ? 'text' : 'password'}
|
||||
icon={icon ?? undefined}
|
||||
suffix={
|
||||
toggle ? (
|
||||
<IconButton
|
||||
label={visible ? '隐藏密码' : '显示密码'}
|
||||
aria-pressed={visible}
|
||||
size="xs"
|
||||
icon={<Icon name={visible ? 'eye_close_line' : 'eye_line'} />}
|
||||
onClick={() => setVisible((current) => !current)}
|
||||
/>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user