iter(g63): card 支持标题操作区、底部操作区与悬停态
This commit is contained in:
+25
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "ui-arch-registry-index/0.1",
|
"$schema": "ui-arch-registry-index/0.1",
|
||||||
"generatedAt": "2026-10-26T04:26:29.979Z",
|
"generatedAt": "2026-10-26T04:28:43.453Z",
|
||||||
"repoRoot": ".",
|
"repoRoot": ".",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
@@ -827,13 +827,36 @@
|
|||||||
"n": "...children",
|
"n": "...children",
|
||||||
"t": "unknown[]",
|
"t": "unknown[]",
|
||||||
"d": "任意子节点:字符串 / DOM / 响应式函数"
|
"d": "任意子节点:字符串 / DOM / 响应式函数"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "extra",
|
||||||
|
"t": "unknown",
|
||||||
|
"d": "标题行右侧操作区(按钮 / 统计文案);缺省不渲染"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "footer",
|
||||||
|
"t": "unknown",
|
||||||
|
"d": "底部操作区;缺省不渲染"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "bordered",
|
||||||
|
"t": "boolean",
|
||||||
|
"d": "是否显示边框;缺省 true"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "hoverable",
|
||||||
|
"t": "boolean",
|
||||||
|
"d": "悬停浮起(可点击卡片);缺省 false"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "card({ title: '用户表' }, dataTable({ data, columns }))",
|
"example": "card({ title: '用户表' }, dataTable({ data, columns }))",
|
||||||
"steps": [
|
"steps": [
|
||||||
"card 是容器组件:第一个参数是 props,其余参数是任意子节点",
|
"card 是容器组件:第一个参数是 props,其余参数是任意子节点",
|
||||||
"传 title 显示标题行,否则只有内容区",
|
"传 title 显示标题行,否则只有内容区",
|
||||||
"子节点可以混合字符串、DOM 节点与响应式函数"
|
"子节点可以混合字符串、DOM 节点与响应式函数",
|
||||||
|
"title 与 extra 同处标题行:有 extra 时右侧对齐,没标题也能只放操作区",
|
||||||
|
"footer 渲染在内容之后的底部区,右对齐并带上分隔线,适合放主次按钮",
|
||||||
|
"bordered:false 去边框(嵌在已有边界的容器里),hoverable 让整卡悬停浮起(自带 reduced-motion 兜底)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+10
-1
@@ -1083,8 +1083,17 @@ const content = h(
|
|||||||
section(
|
section(
|
||||||
'sec-card',
|
'sec-card',
|
||||||
'卡片容器',
|
'卡片容器',
|
||||||
'card 组件:标题 + 任意子内容,样式全部来自设计令牌。',
|
'card 组件:标题行(可带右侧操作区)+ 任意子内容 + 可选底部操作区;bordered / hoverable 控制外观,样式全部来自设计令牌。',
|
||||||
card({ title: '令牌驱动' }, h('p', { class: 'desc' }, '这里没有硬编码颜色 —— 全部消费 --ua-* CSS 变量,暗色模式自动切换。')),
|
card({ title: '令牌驱动' }, h('p', { class: 'desc' }, '这里没有硬编码颜色 —— 全部消费 --ua-* CSS 变量,暗色模式自动切换。')),
|
||||||
|
card(
|
||||||
|
{
|
||||||
|
title: '带操作区的卡片',
|
||||||
|
extra: h('span', { class: 'desc' }, '共 3 项'),
|
||||||
|
hoverable: true,
|
||||||
|
footer: h('div', { class: 'row' }, button({ label: '取消', variant: 'ghost', onClick: () => toast.info('已取消') }), button({ label: '保存', onClick: () => toast.success('已保存') })),
|
||||||
|
},
|
||||||
|
h('p', { class: 'desc' }, '标题行右侧放操作或统计,底部 footer 放主次按钮;hoverable 时整卡悬停浮起。'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
+8
-1
@@ -1,6 +1,6 @@
|
|||||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||||
|
|
||||||
> 自动生成于 2026-10-26T04:26:29.979Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
> 自动生成于 2026-10-26T04:28:43.453Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
@@ -388,6 +388,10 @@ breadcrumb({ items: [{ label: '首页', href: '/' }, { label: '设置' }, { labe
|
|||||||
|
|
||||||
- title:string —— 标题;缺省不渲染标题行
|
- title:string —— 标题;缺省不渲染标题行
|
||||||
- ...children:unknown[] —— 任意子节点:字符串 / DOM / 响应式函数
|
- ...children:unknown[] —— 任意子节点:字符串 / DOM / 响应式函数
|
||||||
|
- extra:unknown —— 标题行右侧操作区(按钮 / 统计文案);缺省不渲染
|
||||||
|
- footer:unknown —— 底部操作区;缺省不渲染
|
||||||
|
- bordered:boolean —— 是否显示边框;缺省 true
|
||||||
|
- hoverable:boolean —— 悬停浮起(可点击卡片);缺省 false
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
@@ -400,6 +404,9 @@ card({ title: '用户表' }, dataTable({ data, columns }))
|
|||||||
1. card 是容器组件:第一个参数是 props,其余参数是任意子节点
|
1. card 是容器组件:第一个参数是 props,其余参数是任意子节点
|
||||||
2. 传 title 显示标题行,否则只有内容区
|
2. 传 title 显示标题行,否则只有内容区
|
||||||
3. 子节点可以混合字符串、DOM 节点与响应式函数
|
3. 子节点可以混合字符串、DOM 节点与响应式函数
|
||||||
|
4. title 与 extra 同处标题行:有 extra 时右侧对齐,没标题也能只放操作区
|
||||||
|
5. footer 渲染在内容之后的底部区,右对齐并带上分隔线,适合放主次按钮
|
||||||
|
6. bordered:false 去边框(嵌在已有边界的容器里),hoverable 让整卡悬停浮起(自带 reduced-motion 兜底)
|
||||||
|
|
||||||
现场演示:apps/docs/demo.html#sec-card
|
现场演示:apps/docs/demo.html#sec-card
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,22 @@
|
|||||||
import { h } from '@ui-arch/frame';
|
import { h } from '@ui-arch/frame';
|
||||||
import type { CardProps } from './types.js';
|
import type { CardProps } from './types.js';
|
||||||
|
|
||||||
/*** 卡片容器:可选标题 + 任意子内容 ***/
|
/*** 卡片容器:可选标题行(含右侧操作区)+ 任意子内容 + 可选底部操作区,bordered / hoverable 控制外观 ***/
|
||||||
export function card(props: CardProps, ...children: unknown[]): HTMLElement {
|
export function card(props: CardProps, ...children: unknown[]): HTMLElement {
|
||||||
|
const cls = `ua-card${props.bordered === false ? ' ua-card-borderless' : ''}${props.hoverable ? ' ua-card-hoverable' : ''}`;
|
||||||
|
const head = props.title || props.extra
|
||||||
|
? h(
|
||||||
|
'div',
|
||||||
|
{ class: 'ua-card-head' },
|
||||||
|
props.title ? h('h3', { class: 'ua-card-title' }, props.title) : h('span', {}), // 无标题时占位,保证操作区仍靠右
|
||||||
|
props.extra ? h('span', { class: 'ua-card-extra' }, props.extra) : null,
|
||||||
|
)
|
||||||
|
: null;
|
||||||
return h(
|
return h(
|
||||||
'div',
|
'div',
|
||||||
{ class: 'ua-card' },
|
{ class: cls },
|
||||||
props.title ? h('h3', { class: 'ua-card-title' }, props.title) : null,
|
head,
|
||||||
...children,
|
...children,
|
||||||
);
|
props.footer ? h('div', { class: 'ua-card-foot' }, props.footer) : null, // 底部操作区:右对齐,仅传了才渲染
|
||||||
|
) as HTMLElement;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ export interface TextFieldProps {
|
|||||||
/*** 卡片入参 ***/
|
/*** 卡片入参 ***/
|
||||||
export interface CardProps {
|
export interface CardProps {
|
||||||
title?: string; // 标题;缺省不渲染标题行
|
title?: string; // 标题;缺省不渲染标题行
|
||||||
|
extra?: unknown; // 标题行右侧操作区(按钮 / 链接);缺省不渲染
|
||||||
|
footer?: unknown; // 底部操作区;缺省不渲染
|
||||||
|
bordered?: boolean; // 是否显示边框;缺省 true
|
||||||
|
hoverable?: boolean; // 悬停浮起(可点击卡片);缺省 false
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** 皮肤层列定义:内核列定义 + 纯渲染字段(对齐 / 文案格式化),不参与排序筛选语义 ***/
|
/*** 皮肤层列定义:内核列定义 + 纯渲染字段(对齐 / 文案格式化),不参与排序筛选语义 ***/
|
||||||
|
|||||||
@@ -698,13 +698,36 @@
|
|||||||
"n": "...children",
|
"n": "...children",
|
||||||
"t": "unknown[]",
|
"t": "unknown[]",
|
||||||
"d": "任意子节点:字符串 / DOM / 响应式函数"
|
"d": "任意子节点:字符串 / DOM / 响应式函数"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "extra",
|
||||||
|
"t": "unknown",
|
||||||
|
"d": "标题行右侧操作区(按钮 / 统计文案);缺省不渲染"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "footer",
|
||||||
|
"t": "unknown",
|
||||||
|
"d": "底部操作区;缺省不渲染"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "bordered",
|
||||||
|
"t": "boolean",
|
||||||
|
"d": "是否显示边框;缺省 true"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "hoverable",
|
||||||
|
"t": "boolean",
|
||||||
|
"d": "悬停浮起(可点击卡片);缺省 false"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "card({ title: '用户表' }, dataTable({ data, columns }))",
|
"example": "card({ title: '用户表' }, dataTable({ data, columns }))",
|
||||||
"steps": [
|
"steps": [
|
||||||
"card 是容器组件:第一个参数是 props,其余参数是任意子节点",
|
"card 是容器组件:第一个参数是 props,其余参数是任意子节点",
|
||||||
"传 title 显示标题行,否则只有内容区",
|
"传 title 显示标题行,否则只有内容区",
|
||||||
"子节点可以混合字符串、DOM 节点与响应式函数"
|
"子节点可以混合字符串、DOM 节点与响应式函数",
|
||||||
|
"title 与 extra 同处标题行:有 extra 时右侧对齐,没标题也能只放操作区",
|
||||||
|
"footer 渲染在内容之后的底部区,右对齐并带上分隔线,适合放主次按钮",
|
||||||
|
"bordered:false 去边框(嵌在已有边界的容器里),hoverable 让整卡悬停浮起(自带 reduced-motion 兜底)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+39
-1
@@ -58,11 +58,41 @@
|
|||||||
border-radius: var(--ua-radius-lg);
|
border-radius: var(--ua-radius-lg);
|
||||||
background: var(--ua-color-surface);
|
background: var(--ua-color-surface);
|
||||||
}
|
}
|
||||||
|
.ua-card-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--ua-space-2);
|
||||||
|
margin-bottom: var(--ua-space-3);
|
||||||
|
}
|
||||||
.ua-card-title {
|
.ua-card-title {
|
||||||
margin: 0 0 var(--ua-space-3);
|
margin: 0;
|
||||||
font-size: var(--ua-font-size-lg);
|
font-size: var(--ua-font-size-lg);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
.ua-card-extra {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--ua-space-1);
|
||||||
|
}
|
||||||
|
.ua-card-foot {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: var(--ua-space-1);
|
||||||
|
margin-top: var(--ua-space-3);
|
||||||
|
padding-top: var(--ua-space-2);
|
||||||
|
border-top: 1px solid var(--ua-color-border);
|
||||||
|
}
|
||||||
|
.ua-card-borderless {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.ua-card-hoverable {
|
||||||
|
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||||
|
}
|
||||||
|
.ua-card-hoverable:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 24px color-mix(in srgb, var(--ua-color-text) 14%, transparent);
|
||||||
|
}
|
||||||
.ua-table table {
|
.ua-table table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
@@ -3394,3 +3424,11 @@ button.ua-step-dot:hover {
|
|||||||
.ua-tree-row .ua-tree-box {
|
.ua-tree-row .ua-tree-box {
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.ua-card-hoverable {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
.ua-card-hoverable:hover {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user