import { useRef, useState } from "react";
import { InputGroup, Loader } from "@lark-ui/core";
import { CheckCircleIcon } from "@phosphor-icons/react";
export function InputGroupDemo() {
const [status, setStatus] = useState<"idle" | "loading" | "success">(
"success",
);
const [value, setValue] = useState("lark");
const timerRef = useRef<ReturnType<typeof setTimeout>>(null);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const next = e.target.value;
setValue(next);
if (timerRef.current) clearTimeout(timerRef.current);
if (next.length > 0) {
setStatus("loading");
timerRef.current = setTimeout(() => setStatus("success"), 1500);
} else {
setStatus("idle");
}
};
return (
<div className="w-full max-w-2xs">
<InputGroup>
<InputGroup.Input
maxLength={20}
onChange={handleChange}
value={value}
/>
<InputGroup.Suffix>.workers.dev</InputGroup.Suffix>
{status !== "idle" && (
<InputGroup.Addon align="end">
{status === "loading" ? (
<Loader />
) : (
<CheckCircleIcon weight="duotone" className="text-lark-success" />
)}
</InputGroup.Addon>
)}
</InputGroup>
</div>
);
}安装
统一导出
import { InputGroup } from "@lark-ui/core";按需导出
import { InputGroup } from "@lark-ui/core/components/input-group";用法
使用内置 Field(推荐)
为 InputGroup 传入 label 属性可启用内置的 Field 包装器,获得标签、描述和错误提示支持。
import { InputGroup } from "@lark-ui/core";
import { MagnifyingGlassIcon } from "@phosphor-icons/react";
export default function Example() {
return (
<InputGroup label="Search" description="Find pages, components, and more">
<InputGroup.Addon>
<MagnifyingGlassIcon />
</InputGroup.Addon>
<InputGroup.Input placeholder="Search..." />
</InputGroup>
);
}裸用 InputGroup(自定义布局)
在自定义表单布局中,请不要使用 label,直接使用 InputGroup。出于无障碍考虑,必须在 InputGroup.Input 上提供 aria-label。
import { InputGroup } from "@lark-ui/core";
import { MagnifyingGlassIcon } from "@phosphor-icons/react";
export default function Example() {
return (
<InputGroup>
<InputGroup.Addon>
<MagnifyingGlassIcon />
</InputGroup.Addon>
<InputGroup.Input placeholder="Search..." aria-label="Search" />
</InputGroup>
);
}示例
图标
使用 Addon 在输入框起始处放置图标,作为视觉标识。
import { InputGroup } from "@lark-ui/core";
import { LinkIcon } from "@phosphor-icons/react";
export function InputGroupIconsDemo() {
return (
<InputGroup className="w-full max-w-3xs">
<InputGroup.Addon>
<LinkIcon />
</InputGroup.Addon>
<InputGroup.Input placeholder="粘贴链接…" aria-label="链接" />
</InputGroup>
);
}文本
使用 Addon 在输入框旁放置文本前缀或后缀。
import { InputGroup } from "@lark-ui/core";
export function InputGroupTextDemo() {
return (
<div className="flex flex-col gap-4">
<InputGroup className="w-full max-w-3xs">
<InputGroup.Addon>@</InputGroup.Addon>
<InputGroup.Input placeholder="用户名" aria-label="用户名" />
</InputGroup>
<InputGroup className="w-full max-w-3xs">
<InputGroup.Input placeholder="邮箱" aria-label="邮箱" />
<InputGroup.Addon align="end">@example.com</InputGroup.Addon>
</InputGroup>
<InputGroup className="w-full max-w-3xs">
<InputGroup.Addon>/api/</InputGroup.Addon>
<InputGroup.Input placeholder="端点" aria-label="API 路径" />
<InputGroup.Addon align="end">.json</InputGroup.Addon>
</InputGroup>
</div>
);
}按钮
将 InputGroup.Button 放在 Addon 内,用于直接操作输入值的动作,例如显示/隐藏或清空。
import { useState } from "react";
import { InputGroup } from "@lark-ui/core";
import { MagnifyingGlassIcon, EyeIcon, EyeSlashIcon, XIcon } from "@phosphor-icons/react";
export function InputGroupButtonsDemo() {
const [show, setShow] = useState(false);
const [searchValue, setSearchValue] = useState("search");
return (
<div className="flex flex-col gap-4">
<InputGroup className="w-full max-w-3xs">
<InputGroup.Input
type={show ? "text" : "password"}
defaultValue="password"
aria-label="密码"
/>
<InputGroup.Addon align="end">
<InputGroup.Button
shape="square"
className="text-lark-subtle"
icon={show ? EyeSlashIcon : EyeIcon}
aria-label={show ? "隐藏密码" : "显示密码"}
onClick={() => setShow(!show)}
/>
</InputGroup.Addon>
</InputGroup>
<InputGroup className="w-full max-w-3xs">
<InputGroup.Addon>
<MagnifyingGlassIcon />
</InputGroup.Addon>
<InputGroup.Input
value={searchValue}
placeholder="搜索"
aria-label="搜索"
onChange={(e) => setSearchValue(e.target.value)}
/>
{searchValue && (
<InputGroup.Addon align="end" className="pr-1">
<InputGroup.Button
shape="square"
icon={XIcon}
aria-label="清除搜索"
onClick={() => setSearchValue("")}
/>
</InputGroup.Addon>
)}
<InputGroup.Button variant="secondary" onClick={() => {}}>
搜索
</InputGroup.Button>
</InputGroup>
</div>
);
}带工具提示的按钮
为 InputGroup.Button 传入 tooltip 属性即可在悬停时显示工具提示。未显式提供 aria-label 时,按钮会从字符串类型的 tooltip 值中生成它。
import { InputGroup } from "@lark-ui/core";
import { MagnifyingGlassIcon, QuestionIcon } from "@phosphor-icons/react";
export function InputGroupTooltipButtonDemo() {
return (
<InputGroup className="w-full max-w-2xs">
<InputGroup.Addon>
<MagnifyingGlassIcon />
</InputGroup.Addon>
<InputGroup.Input placeholder="使用查询语言搜索…" aria-label="搜索" />
<InputGroup.Addon align="end">
<InputGroup.Button
shape="square"
className="text-lark-subtle"
icon={QuestionIcon}
aria-label="查询语言帮助"
tooltip="查询语言帮助"
onClick={() => {}}
/>
</InputGroup.Addon>
</InputGroup>
);
}快捷键
在末尾的 Addon 内放置键盘快捷键提示。
import { InputGroup } from "@lark-ui/core";
import { MagnifyingGlassIcon } from "@phosphor-icons/react";
export function InputGroupKbdDemo() {
return (
<InputGroup className="w-full max-w-3xs">
<InputGroup.Addon>
<MagnifyingGlassIcon />
</InputGroup.Addon>
<InputGroup.Input placeholder="搜索…" aria-label="搜索" />
<InputGroup.Addon align="end">
<kbd className="border-none! bg-none!">⌘K</kbd>
</InputGroup.Addon>
</InputGroup>
);
}加载中
在校验输入值期间,可在末尾的 Addon 内放置 Loader 作为状态指示器。
import { InputGroup, Loader } from "@lark-ui/core";
export function InputGroupLoadingDemo() {
return (
<InputGroup className="w-full max-w-3xs">
<InputGroup.Input defaultValue="lark" aria-label="lark" />
<InputGroup.Addon align="end">
<Loader />
</InputGroup.Addon>
</InputGroup>
);
}行内后缀
Suffix 会渲染与输入内容自然衔接的文本——适合 .workers.dev 这类域名输入。可搭配状态图标 Addon 来展示校验状态。
import { InputGroup } from "@lark-ui/core";
import { CheckCircleIcon, XCircleIcon } from "@phosphor-icons/react";
export function InputGroupSuffixDemo() {
return (
<div className="flex w-full max-w-2xs flex-col gap-4">
<InputGroup label="子域名">
<InputGroup.Input
aria-label="子域名"
defaultValue="lark"
maxLength={20}
/>
<InputGroup.Suffix>.workers.dev</InputGroup.Suffix>
<InputGroup.Addon align="end">
<CheckCircleIcon weight="duotone" className="text-lark-success" />
</InputGroup.Addon>
</InputGroup>
<InputGroup
label="子域名"
error={{ message: "该子域名不可用", match: true }}
>
<InputGroup.Input
aria-label="子域名"
defaultValue="lark"
maxLength={20}
/>
<InputGroup.Suffix>.workers.dev</InputGroup.Suffix>
<InputGroup.Addon align="end">
<XCircleIcon weight="duotone" className="text-lark-danger" />
</InputGroup.Addon>
</InputGroup>
</div>
);
}尺寸
提供四种尺寸:xs、sm、base(默认)和 lg。尺寸作用于整个输入框组。
import { InputGroup } from "@lark-ui/core";
import { MagnifyingGlassIcon, QuestionIcon } from "@phosphor-icons/react";
export function InputGroupSizesDemo() {
return (
<div className="flex w-full max-w-3xs flex-col gap-4">
<InputGroup size="xs" label="超小">
<InputGroup.Addon>
<MagnifyingGlassIcon />
</InputGroup.Addon>
<InputGroup.Input placeholder="超小尺寸输入框" />
<InputGroup.Addon align="end">
<InputGroup.Button
className="text-lark-subtle"
icon={QuestionIcon}
shape="square"
aria-label="帮助"
/>
</InputGroup.Addon>
</InputGroup>
<InputGroup size="sm" label="小">
<InputGroup.Addon>
<MagnifyingGlassIcon />
</InputGroup.Addon>
<InputGroup.Input placeholder="小尺寸输入框" />
<InputGroup.Addon align="end">
<InputGroup.Button
className="text-lark-subtle"
icon={QuestionIcon}
shape="square"
aria-label="帮助"
/>
</InputGroup.Addon>
</InputGroup>
<InputGroup label="基础(默认)">
<InputGroup.Addon>
<MagnifyingGlassIcon />
</InputGroup.Addon>
<InputGroup.Input placeholder="基础尺寸输入框" />
<InputGroup.Addon align="end">
<InputGroup.Button
className="text-lark-subtle"
icon={QuestionIcon}
shape="square"
aria-label="帮助"
/>
</InputGroup.Addon>
</InputGroup>
<InputGroup size="lg" label="大">
<InputGroup.Addon>
<MagnifyingGlassIcon />
</InputGroup.Addon>
<InputGroup.Input placeholder="大尺寸输入框" />
<InputGroup.Addon align="end">
<InputGroup.Button
className="text-lark-subtle"
icon={QuestionIcon}
shape="square"
aria-label="帮助"
/>
</InputGroup.Addon>
</InputGroup>
</div>
);
}状态
支持多种输入状态,包括错误、禁用和带描述。将 label、error 和 description 属性直接传给 InputGroup 即可。
至少需要 8 个字符
import { useState } from "react";
import { InputGroup } from "@lark-ui/core";
import { MagnifyingGlassIcon, EyeIcon, EyeSlashIcon } from "@phosphor-icons/react";
export function InputGroupStatesDemo() {
const [show, setShow] = useState(false);
return (
<div className="flex w-full max-w-3xs flex-col gap-4">
<InputGroup
label="错误状态"
error={{ message: "请输入有效的邮箱地址", match: true }}
>
<InputGroup.Input type="email" defaultValue="invalid-email" />
<InputGroup.Addon align="end">@example.com</InputGroup.Addon>
</InputGroup>
<InputGroup label="禁用" disabled>
<InputGroup.Addon>
<MagnifyingGlassIcon />
</InputGroup.Addon>
<InputGroup.Input placeholder="搜索…" />
</InputGroup>
<InputGroup label="可选字段" required={false}>
<InputGroup.Addon>$</InputGroup.Addon>
<InputGroup.Input placeholder="0.00" />
</InputGroup>
<InputGroup
label="带描述"
description="至少需要 8 个字符"
labelTooltip="你的密码会被安全存储"
>
<InputGroup.Input
type={show ? "text" : "password"}
placeholder="密码"
/>
<InputGroup.Addon align="end">
<InputGroup.Button
shape="square"
className="text-lark-subtle"
icon={show ? EyeSlashIcon : EyeIcon}
aria-label={show ? "隐藏密码" : "显示密码"}
onClick={() => setShow(!show)}
/>
</InputGroup.Addon>
</InputGroup>
</div>
);
}API 参考
InputGroup
为所有子组件提供上下文的根容器。接受 Field 属性(label、description、error),在提供 label 时会用 Field 包裹内容。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| label | ReactNode | - | 标签内容——可以是字符串或任意 React 节点。 |
| description | ReactNode | - | 显示在控件下方的辅助说明文字(存在 `error` 时隐藏)。 |
| error | object | - | 带错误信息和浏览器 `ValidityState` 匹配键的校验错误。 |
| required | boolean | - | 显式设为 `false` 时,在标签后显示灰色的“(optional)”文字。设为 `true` 或 `undefined` 时不显示任何标识。 |
| labelTooltip | ReactNode | - | 通过信息图标显示在标签旁的工具提示内容。 |
| children | ReactNode | - | - |
| className | string | - | - |
| id | string | - | - |
| lang | string | - | - |
| title | string | - | - |
| size | "xs" | "sm" | "base" | "lg" | "base" | - |
| disabled | boolean | - | - |
InputGroup.Input
文本输入元素。从 InputGroup 上下文继承 size、disabled 和 error。接受所有标准 input 属性,但与 Field 相关的属性除外(由父组件处理)。
| 属性 | 类型 | 默认值 |
|---|
该组件没有专有属性,支持标准 HTML 属性。
InputGroup.Addon
位于输入框起始或末尾的容器,用于放置图标、文本或紧凑按钮。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| align | "start" | "end" | "start" | 相对于输入框的位置。 |
| className | string | - | 额外的 CSS 类名。 |
InputGroup.Button
用于切换、复制、帮助等次要操作的按钮。渲染在 Addon 内部。传入 tooltip 属性即可在悬停时显示工具提示。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| tooltip | ReactNode | - | 提供时用 Tooltip 包裹按钮。若传入字符串值,会自动设置 aria-label。 |
| tooltipSide | "top" | "right" | "bottom" | "left" | "bottom" | 工具提示弹出层偏好的显示位置。 |
| variant | "primary" | "secondary" | "ghost" | "destructive" | "secondary-destructive" | "outline" | "ghost" | 按钮的视觉样式。默认为 ghost。 |
| size | "xs" | "sm" | "base" | "lg" | "sm" | 按钮尺寸。 |
InputGroup.Suffix
与输入内容自然衔接的行内文本(例如 .workers.dev)。输入框宽度会随着用户输入自动调整。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| className | string | - | 额外的 CSS 类名。 |
校验错误类型
将 error 用作对象时,match 属性对应 HTML5 ValidityState 的取值:
| 匹配值 | 说明 |
|---|---|
| valueMissing | 必填字段为空 |
| typeMismatch | 值与类型不匹配(例如邮箱格式无效) |
| patternMismatch | 值与 pattern 属性不匹配 |
| tooShort | 值短于 minLength |
| tooLong | 值长于 maxLength |
| rangeUnderflow | 值小于 min |
| rangeOverflow | 值大于 max |
| true | 始终显示错误(用于服务端校验) |