可编辑文本
概述
用于在预览态与编辑态之间切换的内联文本编辑组件。预览态以可聚焦文本呈现(或占位符),聚焦/双击/点击编辑按钮后切换为编辑态,提交(回车/blur/提交按钮)或取消(Esc/取消按钮)后回到预览态。采用 Aria 核心 + 样式封装的结构:@vean/aria 中的 EditableRoot 等 8 个组件(零样式)实现完整状态机,SEditable 封装注入 editableVariants 样式(8 个插槽:root/area/preview/input/controls/editTrigger/submitTrigger/cancelTrigger)。
用法
特性
- 🔀 双态状态机 —— 预览(preview)/编辑(edit)切换,
update:state事件上报edit/submit/cancel - 🖱️ 多种激活方式 ——
activationMode:focus(聚焦激活,默认)、dblclick(双击激活)、none(仅经编辑按钮或edit()激活) - ⌨️ 多种提交方式 ——
submitMode:blur(失焦提交,默认)、enter(回车提交)、both、none(失焦仅取消);Esc 始终取消 - 🔁 受控/非受控 ——
modelValue+update:modelValue受控,defaultValue非受控;submit事件携带提交值 - 🎛️ 编辑体验 ——
selectOnFocus(聚焦全选)、startWithEditMode(挂载即编辑)、maxLength、autoResize(inline-grid 自适应宽度) - 🧩 Aria/styled 分离 ——
EditableCompact聚合 8 个原语并暴露 6 个*Props通道(area/preview/input/editTrigger/submitTrigger/cancelTrigger) - ♿ 无障碍 —— 预览态
tabindex="0"可聚焦、完整键盘操作(Enter 提交 / Esc 取消)、触发按钮本地化aria-label、输入与表单的隐藏代理(VisuallyHiddenInput) - 🎨 8 槽样式定制 —— root/area/preview/input/controls/editTrigger/submitTrigger/cancelTrigger +
size变体(xs~2xl)
组件家族
SEditable(styled)—— 入口封装;纯defineProps+editableVariants,8 槽动态转发,useForwardListeners合并事件EditableRoot(Aria)—— 状态机属主:持有modelValue/inputValue/isEditing/placeholder/isEmpty,暴露edit/cancel/submit;渲染data-dismissable-layer处理失焦/外部点击;表单内(.form)且传name时渲染隐藏输入代理EditableArea(Aria)—— 区域容器;autoResize时以 inline-grid 布局自适应输入宽度EditablePreview(Aria)—— 预览文本;tabindex = disabled ? -1 : 0,focusin/dblclick 触发激活EditableInput(Aria)—— 真实输入;激活/取消/提交键盘处理,selectOnFocus聚焦全选EditableEditTrigger/EditableSubmitTrigger/EditableCancelTrigger(Aria)—— Button 基座触发按钮;aria-label回退到useLocaleMessages本地化消息;disabled || readonly时禁用EditableCompact(Aria)—— 聚合根/区/预览/输入/三触发按钮的复合组件,slot props 提供modelValue/inputValue/isEditing/isEmpty/edit/cancel/submit
演示
基础
Value: Click to edit your display name
State: preview
自定义样式
- 01 基础 —— 受控
v-model+ placeholder +submit/update:state事件 - 02 尺寸 ——
size变体 - 03 禁用 ——
disabled - 04 激活模式 ——
focus与dblclick - 05 自定义样式 —— 8 槽
ui定制
API
Aria 层
9Editable
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
class | ClassValue | — | 应用于根元素的额外类名。 |
size | ThemeSize | — | 组件的视觉尺寸。 |
ui | Partial<EditableUi> | — | 该组件的按槽类重写。 |
areaProps | EditableAreaProps | — | 转发给 area 元素的属性。 |
previewProps | EditablePreviewProps | — | 转发至预览元素的属性。 |
inputProps | EditableInputProps | — | 转发给输入元素的属性。 |
editTriggerProps | EditableEditTriggerProps | — | 转发给编辑触发元素的属性。 |
submitTriggerProps | EditableSubmitTriggerProps | — | 转发给提交触发元素的属性。 |
cancelTriggerProps | EditableCancelTriggerProps | — | 转发给取消触发器元素的属性。 |
defaultValue | string | — | 可编辑字段的默认值。 |
modelValue | string | — | 可编辑字段的受控值。 |
placeholder | string | EditablePlaceholder | — | 可编辑字段的占位符。 |
dir | Direction | — | 如适用,请标注可编辑字段的阅读方向。 |
disabled | boolean | — | 当值为 `true` 时,禁止用户与可编辑字段进行交互。 |
readonly | boolean | — | 当值为 `true` 时,禁止用户编辑该值。 |
activationMode | EditableActivationMode | — | 可编辑字段的激活事件。 |
selectOnFocus | boolean | — | 当输入框获得焦点时,是否选中其中的文本。 |
submitMode | EditableSubmitMode | — | 可编辑字段的提交事件。 |
startWithEditMode | boolean | — | 是否以编辑模式开启状态开始。 |
maxLength | number | — | 允许的最大字符数。 |
autoResize | boolean | — | 可编辑字段是否应自动调整大小。 |
id | string | — | 字段的 ID。 |
name | string | — | 字段的名称。作为名称/值对的一部分,与其所属表单一同提交。 |
required | boolean | — | 当值为 `true` 时,表示用户必须先设置该值,所属表单才能提交。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | string | 每当模型值发生变化时,都会调用该事件处理程序。 |
submit | string | 当值被提交时调用的事件处理程序。 |
update:state | EditableEventState | 当可编辑字段的状态发生变化时调用的事件处理程序。 |
插槽
| 插槽名 | 参数 | 描述 |
|---|---|---|
default | EditableCompactSlotProps | 默认插槽的自定义内容。 |
preview | EditableCompactSlotProps | 预览区域的自定义内容。 |
input | EditableCompactSlotProps | 输入槽位的自定义内容。 |
edit-trigger | EditableCompactSlotProps | 编辑触发器插槽的自定义内容。 |
submit-trigger | EditableCompactSlotProps | 提交触发器插槽的自定义内容。 |
cancel-trigger | EditableCompactSlotProps | “取消”触发槽的自定义内容。 |
EditableArea
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
EditableCancelTrigger
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
type | ButtonType | 'button' | 按钮元素的类型。可以是 'button'、'submit' 或 'reset' 之一。 |
disabled | boolean | — | 该组件是否已禁用。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
EditableCompact
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
areaProps | EditableAreaProps | — | 转发给 area 元素的属性。 |
previewProps | EditablePreviewProps | — | 转发至预览元素的属性。 |
inputProps | EditableInputProps | — | 转发给输入元素的属性。 |
editTriggerProps | EditableEditTriggerProps | — | 转发给编辑触发元素的属性。 |
submitTriggerProps | EditableSubmitTriggerProps | — | 转发给提交触发元素的属性。 |
cancelTriggerProps | EditableCancelTriggerProps | — | 转发给取消触发器元素的属性。 |
defaultValue | string | — | 可编辑字段的默认值。 |
modelValue | string | — | 可编辑字段的受控值。 |
placeholder | string | EditablePlaceholder | — | 可编辑字段的占位符。 |
dir | Direction | — | 如适用,请标注可编辑字段的阅读方向。 |
disabled | boolean | — | 当值为 `true` 时,禁止用户与可编辑字段进行交互。 |
readonly | boolean | — | 当值为 `true` 时,禁止用户编辑该值。 |
activationMode | EditableActivationMode | — | 可编辑字段的激活事件。 |
selectOnFocus | boolean | — | 当输入框获得焦点时,是否选中其中的文本。 |
submitMode | EditableSubmitMode | — | 可编辑字段的提交事件。 |
startWithEditMode | boolean | — | 是否以编辑模式开启状态开始。 |
maxLength | number | — | 允许的最大字符数。 |
autoResize | boolean | — | 可编辑字段是否应自动调整大小。 |
id | string | — | 字段的 ID。 |
name | string | — | 字段的名称。作为名称/值对的一部分,与其所属表单一同提交。 |
required | boolean | — | 当值为 `true` 时,表示用户必须先设置该值,所属表单才能提交。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | string | 每当模型值发生变化时,都会调用该事件处理程序。 |
submit | string | 当值被提交时调用的事件处理程序。 |
update:state | EditableEventState | 当可编辑字段的状态发生变化时调用的事件处理程序。 |
插槽
| 插槽名 | 参数 | 描述 |
|---|---|---|
default | EditableCompactSlotProps | 默认插槽的自定义内容。 |
preview | EditableCompactSlotProps | 预览区域的自定义内容。 |
input | EditableCompactSlotProps | 输入槽位的自定义内容。 |
edit-trigger | EditableCompactSlotProps | 编辑触发器插槽的自定义内容。 |
submit-trigger | EditableCompactSlotProps | 提交触发器插槽的自定义内容。 |
cancel-trigger | EditableCompactSlotProps | “取消”触发槽的自定义内容。 |
EditableEditTrigger
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
type | ButtonType | 'button' | 按钮元素的类型。可以是 'button'、'submit' 或 'reset' 之一。 |
disabled | boolean | — | 该组件是否已禁用。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
EditableInput
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
id | string | — | 同上。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
EditablePreview
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
EditableRoot
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
defaultValue | string | — | 可编辑字段的默认值。 |
modelValue | string | — | 可编辑字段的受控值。 |
placeholder | string | EditablePlaceholder | — | 可编辑字段的占位符。 |
dir | Direction | — | 如适用,请标注可编辑字段的阅读方向。 |
disabled | boolean | — | 当值为 `true` 时,禁止用户与可编辑字段进行交互。 |
readonly | boolean | — | 当值为 `true` 时,禁止用户编辑该值。 |
activationMode | EditableActivationMode | — | 可编辑字段的激活事件。 |
selectOnFocus | boolean | — | 当输入框获得焦点时,是否选中其中的文本。 |
submitMode | EditableSubmitMode | — | 可编辑字段的提交事件。 |
startWithEditMode | boolean | — | 是否以编辑模式开启状态开始。 |
maxLength | number | — | 允许的最大字符数。 |
autoResize | boolean | — | 可编辑字段是否应自动调整大小。 |
id | string | — | 字段的 ID。 |
name | string | — | 字段的名称。作为名称/值对的一部分,与其所属表单一同提交。 |
required | boolean | — | 当值为 `true` 时,表示用户必须先设置该值,所属表单才能提交。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | string | 每当模型值发生变化时,都会调用该事件处理程序。 |
submit | string | 当值被提交时调用的事件处理程序。 |
update:state | EditableEventState | 当可编辑字段的状态发生变化时调用的事件处理程序。 |
EditableSubmitTrigger
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
type | ButtonType | 'button' | 按钮元素的类型。可以是 'button'、'submit' 或 'reset' 之一。 |
disabled | boolean | — | 该组件是否已禁用。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
备注
架构与竞品差异
EditableRoot 用 useControllableState 统一受控/非受控;inputValue(编辑缓冲)与 modelValue(提交值)分离,currentValue 按状态取其一。编辑态下外部 modelValue 推送不会覆盖正在输入的内容(提交/取消时才回写),避免受控父组件更新打断输入。激活(focus/dblclick/edit trigger)与提交(blur/enter/提交按钮)双向解耦,useFocusOutside/usePointerdownOutside 以 isEditing 为门控实现失焦提交。autoResize 用 inline-grid + grid-template-columns: auto 让输入框贴内容宽度。与 reka-ui Editable 同源设计(激活/提交模式、selectOnFocus、startWithEditMode、表单代理一应俱全);Ant Design 的 Typography editable 仅按钮激活 + 受控 editing;Element Plus 与 shadcn/ui 无对应组件;Mantine EditableText 覆盖激活/提交的子集。
| 能力 | VeanUI | reka-ui | Ant Design Typography | Mantine EditableText | Element Plus | shadcn/ui |
|---|---|---|---|---|---|---|
| Aria/styled 分离 | ✅ | ✅ | — | — | — | — |
| 激活模式(focus/dblclick/none) | ✅ | ✅ | — | ⚠️ | — | — |
| 提交模式(blur/enter/none/both) | ✅ | ✅ | — | ⚠️ | — | — |
| Esc 取消 | ✅ | ✅ | ⚠️ | ✅ | — | — |
selectOnFocus | ✅ | ✅ | — | ✅ | — | — |
startWithEditMode | ✅ | ✅ | ⚠️ | ✅ | — | — |
autoResize(自适应宽度) | ✅ | ✅ | — | — | — | — |
| 表单代理(name + form 隐藏输入) | ✅ | ✅ | — | — | — | — |
触发按钮本地化 aria-label | ✅ | ⚠️ | — | — | — | — |
| 受控/非受控 + 提交事件 | ✅ | ✅ | ✅ | ✅ | — | — |
⚠️ = 部分支持(Mantine 经 activateOnFocus/submitOnBlur 覆盖激活与提交子集;AntD 经受控 editing 覆盖 startWithEditMode 类行为;reka-ui 触发按钮 aria-label 硬编码,VeanUI 用 useLocaleMessages 本地化)。
注意事项
- 编辑态下外部受控更新不会覆盖输入内容:
modelValue的推送不会写入正在编辑的缓冲,输入在提交/取消时统一回写——避免受控场景下「打字被覆盖」;非编辑态仍保持同步。 - blur 提交依赖焦点移出组件根:
useFocusOutside监听 documentfocusin,焦点移到组件外才触发submitMode逻辑;submitMode: 'none'时失焦仅取消。 - 表单集成:只有当组件位于带
.formclass 的容器内(isFormControl判定)且传入name时才渲染VisuallyHiddenInput代理,值随表单提交;否则不会输出隐藏输入。 placeholder支持字符串或{ edit, preview }对象——可分别指定编辑态与预览态占位文案。readonly与disabled有别:readonly阻止进入编辑(edit()守卫 + 触发按钮禁用 + 输入只读),但不改变值本身;disabled额外禁用预览聚焦与隐藏代理。- 自定义
preview插槽时默认无tabindex——需自行提供可聚焦元素或改由EditableEditTrigger激活。 activationMode: 'none'时聚焦不会进入编辑——必须经编辑按钮或暴露的edit()(defineExpose)激活。- 受控场景请监听
submit取最终提交值(回车/blur/提交按钮路径一致),update:modelValue在提交时同步触发。
常见问题
如何改为双击激活而不是聚焦激活?
设置 activationMode="dblclick":
<SEditable activation-mode="dblclick" default-value="双击编辑" />如何让失焦取消而不是提交?
设置 submitMode="none"——失焦时执行 cancel()(不触发 submit/update:modelValue),仅 Esc 可取消,回车仍提交:
<SEditable submit-mode="none" default-value="失焦取消" />如何用按钮显式触发编辑?
使用 EditableEditTrigger(Aria 复合下可直接声明),或通过 ref 调用暴露方法:
<SEditable ref="editableRef" default-value="按钮编辑" />
<button @click="editableRef?.edit()">编辑</button>如何拿到编辑提交后的值?
监听 submit 事件(与 update:modelValue 同步触发):
<SEditable v-model="value" @submit="onSubmit" />function onSubmit(value: string) {
// 提交后的最终值
}如何让输入框随内容自适应宽度?
设置 autoResize——输入容器以 inline-grid 布局,宽度跟随内容:
<SEditable auto-resize default-value="自适应宽度" />如何与原生表单一起提交?
将组件放入带 .form class 的表单容器并传入 name,组件渲染隐藏输入代理随表单提交:
<form class="form" @submit.prevent>
<SEditable name="nickname" default-value="昵称" />
<button type="submit">提交</button>
</form>如何在编辑开始时全选文本?
设置 selectOnFocus——进入编辑后聚焦输入并全选既有内容,便于直接覆盖:
<SEditable select-on-focus default-value="聚焦全选" />