diff --git a/docs/design-system.md b/docs/design-system.md index 4c28a54533..5d0f74ebad 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -618,7 +618,7 @@ | `useDcFormSubmit` | `@dc-ui/components/form` | 提交状态机 composable:`{ status, run, reset }`;`run(fn)` 驱动 idle→submitting→success/error,成功/失败按时长自动回退 | | `useDcForm` | `@dc-ui/components/form` | 读取 `DcForm` 注入上下文(`DC_FORM_INJECTION_KEY`),供自定义提交控件使用 | | `DcBadge` | `@dc-ui/components/badge` | shadcn Badge 语义化封装:default/secondary/outline/destructive 透传 + `success`/`warning`/`danger`/`active`/`neutral`(带 dark 变体,与 DcStatusPill 色系一致) | -| `DcCopyButton` | `@dc-ui/components/copy-button` | 复制按钮:`copy-text` + `navigator.clipboard`,成功图标短暂变 ✅(`success-duration`);`label`/`show-toast` 可选;`@copied`/`@error` 事件 | +| `DcCopyButton` | `@dc-ui/components/button` | 复制按钮:复用 `DcButton` 契约,`variant` 缺省 `ghost`;`copy-text` 必填触发复制(`useClipboard`),成功图标切 ✅(1200ms 自动回退、色值变 emerald);`@copied`/`@error` 事件;`label`/`tooltip` 提供可访问名,`copy-text` 兜底 | | `DcDropdownActionItem` | `@dc-ui/components/dropdown-action-item` | 菜单动作项:`icon`/`label`/`danger`(destructive 变体)/`disabled`/`inset` + `@select`,收敛 `Icon mr-2 + span` 散写 | | `DcFormActions` | `@dc-ui/components/form-actions` | 表单底部操作组:「取消 + 提交(DcSubmitButton)」+ 中间插槽;`submit-status`/`cancel-label`/`submit-label`/`danger-submit` | @@ -635,10 +635,11 @@ - **tooltip 补齐边界**:只为可操作的 icon-only 控件补齐;文字按钮、纯展示 Icon、以及说明型 Switch/Checkbox/链接提示维持原行为,不为统一形式强行增加 tooltip。 - 新增 dc-ui 组件需先证明既有组件无法覆盖;在本文档登记聚合后的完整契约,并符合 §20/§21 的密度与 token 规则。 -### 22.3 迁移状态(2026-08 批次,未提交) +### 22.3 迁移状态(2026-08 批次) - **`DcButton` 聚合**:`DcIconButton` 已并入并删除(13 处调用迁入);手写 Tooltip+按钮迁移 10 文件 30 处(MessageToolbar 12 / WindowSideBar 8 / ChatStatusBar 2 / ModelConfigItem / MessageBlockImage / MessageBlockToolCallImagePreview / SkillsIndicator / SkillsPanel / ChatInputToolbar 4)。`DcButton` 已补齐 tooltip 定位、延迟、content class 与 keyboard-focus 参数,保留复杂提示契约。 +- **`DcCopyButton` 归位**:由 `@dc-ui/components/copy-button` 并入 `@dc-ui/components/button`(旧目录删除),消费方(MessageToolbar / ArtifactBlock / CodeArtifact / McpJsonViewer / McpServers / TraceDialog / MessageBlockToolCall)全部经 `copy-text` 传入复制内容,MessageToolbar 增加 `copy-text` prop。 - **`DcConfirmDialog`**:迁移 5 文件 7 框(ChatPage / ModelConfigDialog ×2 / KnowledgeFileItem ×2 / SkillInstallDialog / SkillDetailDialog ×2)+ ChatPage.test.ts 适配。 - **`DcEmpty`**:迁移 10 文件 12 处空态(含 MemoryEmptyState 用 `#action` 插槽,组件接口不变)。 - **`DcSectionCard`** 4 文件 11 卡(MemoryDiagnosticsPanel / MemoryDirectivesPanel / MemoryConfigInlinePanel / OfficialPluginDetailPage);**`DcStatusPill`** 2 文件(RemoteSettings 5 渠道 / AcpDebugDialog);**`DcInlineError`** 12 文件(McpServerForm 5 处等)。 -- **有意保留(记录在案)**:WindowSideBar remote-control(多行 tooltip)、WindowSideBarSessionItem pin/delete(依赖 scoped CSS)、MessageDialog / UpdateTaskCheckDialog(动态多按钮)、SettingsLeaveGuardDialog(路由守卫)、DataSettings 单按钮错误弹窗、desc-only 空态提示、带重试按钮的错误横幅。 \ No newline at end of file +- **有意保留(记录在案)**:WindowSideBar remote-control(多行 tooltip)、WindowSideBarSessionItem pin/delete(依赖 scoped CSS)、MessageDialog / UpdateTaskCheckDialog(动态多按钮)、SettingsLeaveGuardDialog(路由守卫)、DataSettings 单按钮错误弹窗、desc-only 空态提示、带重试按钮的错误横幅。 diff --git a/src/dc-ui/components/button/DcButton.vue b/src/dc-ui/components/button/DcButton.vue index 7f5f5c7a48..23d17bae8c 100644 --- a/src/dc-ui/components/button/DcButton.vue +++ b/src/dc-ui/components/button/DcButton.vue @@ -1,6 +1,4 @@ diff --git a/src/dc-ui/components/button/index.ts b/src/dc-ui/components/button/index.ts index 27c43dc106..45a7628e0b 100644 --- a/src/dc-ui/components/button/index.ts +++ b/src/dc-ui/components/button/index.ts @@ -1,38 +1,3 @@ -import type { VariantProps } from 'class-variance-authority' -import { cva } from 'class-variance-authority' - export { default as DcButton } from './DcButton.vue' - -export const dcButtonVariants = cva( - 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-[color,background-color,border-color,scale] duration-[var(--dc-motion-fast)] ease-[var(--dc-ease-out-soft)] active:scale-[0.97] motion-reduce:active:scale-100 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*="size-"])]:size-4 [&_svg]:shrink-0 shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]', - { - variants: { - variant: { - default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90', - destructive: - 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', - outline: - 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50', - secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80', - ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50', - link: 'text-primary underline-offset-4 hover:underline' - }, - size: { - default: 'h-9 gap-2 px-4 py-2 has-[>svg]:px-3', - sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5', - xs: 'h-7 gap-1.5 rounded-md px-2.5 text-xs has-[>svg]:px-2', - lg: 'h-10 gap-2 rounded-md px-6 has-[>svg]:px-4', - icon: 'size-8', - 'icon-sm': 'size-7', - 'icon-xs': 'size-6', - 'icon-lg': 'size-10' - } - }, - defaultVariants: { - variant: 'default', - size: 'default' - } - } -) - -export type DcButtonVariants = VariantProps +export { default as DcCopyButton } from './DcCopyButton.vue' +export * from './props' diff --git a/src/dc-ui/components/button/props.ts b/src/dc-ui/components/button/props.ts new file mode 100644 index 0000000000..3e54df4b21 --- /dev/null +++ b/src/dc-ui/components/button/props.ts @@ -0,0 +1,62 @@ +import type { PrimitiveProps } from 'reka-ui' +import type { HTMLAttributes } from 'vue' + +export interface DcButtonProps extends PrimitiveProps { + variant?: DcButtonVariants['variant'] + size?: DcButtonVariants['size'] + icon?: string + iconSize?: DcIconSize + iconClass?: HTMLAttributes['class'] + loading?: boolean + disabled?: boolean + active?: boolean + /** Visible tooltip. `label` alone only provides the accessible name. */ + tooltip?: string + tooltipSide?: DcTooltipSide + tooltipSideOffset?: number + tooltipDelayDuration?: number + tooltipContentClass?: HTMLAttributes['class'] + tooltipIgnoreNonKeyboardFocus?: boolean + label?: string + class?: HTMLAttributes['class'] +} + +import type { VariantProps } from 'class-variance-authority' +import { cva } from 'class-variance-authority' + +type DcIconSize = '3' | '3.5' | '4' +type DcTooltipSide = 'top' | 'bottom' | 'left' | 'right' + +export const dcButtonVariants = cva( + 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-[color,background-color,border-color,scale] duration-[var(--dc-motion-fast)] ease-[var(--dc-ease-out-soft)] active:scale-[0.97] motion-reduce:active:scale-100 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*="size-"])]:size-4 [&_svg]:shrink-0 shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]', + { + variants: { + variant: { + default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90', + destructive: + 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', + outline: + 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50', + secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50', + link: 'text-primary underline-offset-4 hover:underline' + }, + size: { + default: 'h-9 gap-2 px-4 py-2 has-[>svg]:px-3', + sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5', + xs: 'h-7 gap-1.5 rounded-md px-2.5 text-xs has-[>svg]:px-2', + lg: 'h-10 gap-2 rounded-md px-6 has-[>svg]:px-4', + icon: 'size-8', + 'icon-sm': 'size-7', + 'icon-xs': 'size-6', + 'icon-lg': 'size-10' + } + }, + defaultVariants: { + variant: 'default', + size: 'default' + } + } +) + +export type DcButtonVariants = VariantProps diff --git a/src/dc-ui/components/copy-button/DcCopyButton.vue b/src/dc-ui/components/copy-button/DcCopyButton.vue deleted file mode 100644 index 296dd1ef9a..0000000000 --- a/src/dc-ui/components/copy-button/DcCopyButton.vue +++ /dev/null @@ -1,142 +0,0 @@ - - - diff --git a/src/dc-ui/components/copy-button/index.ts b/src/dc-ui/components/copy-button/index.ts deleted file mode 100644 index ef90f141d8..0000000000 --- a/src/dc-ui/components/copy-button/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as DcCopyButton } from './DcCopyButton.vue' diff --git a/src/dc-ui/components/index.ts b/src/dc-ui/components/index.ts new file mode 100644 index 0000000000..723978ff93 --- /dev/null +++ b/src/dc-ui/components/index.ts @@ -0,0 +1,16 @@ +export * from './button' +export * from './status-pill' +export * from './confirm-dialog' +export * from './toggle-row' +export * from './empty' +export * from './skeleton' +export * from './toast' +export * from './tooltip' +export * from './sheet-panel' +export * from './popover' +export * from './section-card' +export * from './inline-error' +export * from './form' +export * from './badge' +export * from './dropdown-action-item' +export * from './form-actions' diff --git a/src/dc-ui/components/toggle-row/DcToggleRow.vue b/src/dc-ui/components/toggle-row/DcToggleRow.vue index 803aaf6459..6bb45a807c 100644 --- a/src/dc-ui/components/toggle-row/DcToggleRow.vue +++ b/src/dc-ui/components/toggle-row/DcToggleRow.vue @@ -35,10 +35,13 @@ const handleChecked = (value: boolean | 'indeterminate') => {