iter(g35): 新增 notification 通知卡片组件

This commit is contained in:
HIsheR
2026-10-26 11:46:54 +08:00
parent 8711fae1af
commit 8b5f355d7e
13 changed files with 302 additions and 9 deletions
+8
View File
@@ -636,6 +636,14 @@
"acceptance": "ua info <组件id> 输出 props 表 + 步骤 + 示例,ua info <包名> 出摘要且 --json 保留原始清单;MCP get_component 支持组件 id(返回 package + component);npm run check 全绿", "acceptance": "ua info <组件id> 输出 props 表 + 步骤 + 示例,ua info <包名> 出摘要且 --json 保留原始清单;MCP get_component 支持组件 id(返回 package + component);npm run check 全绿",
"status": "done", "status": "done",
"notes": "自补:Agent 查询体验;--json 保留原始清单" "notes": "自补:Agent 查询体验;--json 保留原始清单"
},
{
"id": "g35",
"title": "新增 notification 通知卡片",
"motivation": "toast 是右上角轻量瞬时提示,装不下「标题 + 描述 + 操作按钮 + 需读完」的通知场景,业务侧只能自己写浮层",
"acceptance": "notification({ title, description, variant, duration, closable, action }) 右下角堆叠(与 toast 分区)、超出 3 条挤掉最早、返回 { el, close }、duration 0 常驻;组件数 42→43 三处计数同步;npm run check 全绿",
"status": "open",
"notes": "自补:组件补充"
} }
] ]
} }
+49 -2
View File
@@ -1,6 +1,6 @@
{ {
"$schema": "ui-arch-registry-index/0.1", "$schema": "ui-arch-registry-index/0.1",
"generatedAt": "2026-10-26T03:45:25.126Z", "generatedAt": "2026-10-26T03:46:32.583Z",
"repoRoot": ".", "repoRoot": ".",
"packages": [ "packages": [
{ {
@@ -185,7 +185,8 @@
"list(props):列表,header/footer 外壳 + 条目 avatar/title/description/extra,onItemClick 让条目可点击,items 为空回落空状态", "list(props):列表,header/footer 外壳 + 条目 avatar/title/description/extra,onItemClick 让条目可点击,items 为空回落空状态",
"carousel(props):轮播,items 横排位移,指示点/箭头/←→ 键切换,autoplay 毫秒自动前进且悬停暂停,index 信号可外部受控", "carousel(props):轮播,items 横排位移,指示点/箭头/←→ 键切换,autoplay 毫秒自动前进且悬停暂停,index 信号可外部受控",
"tree(props):树形,递归节点 + 箭头展开/收起 + 点选节点,键盘 Enter 选中、→ 展开、← 收起,禁用节点不响应", "tree(props):树形,递归节点 + 箭头展开/收起 + 点选节点,键盘 Enter 选中、→ 展开、← 收起,禁用节点不响应",
"upload(props):文件选择,点击或拖拽入列,清单可逐个移除,onChange 回传文件数组(不内置上传请求)" "upload(props):文件选择,点击或拖拽入列,清单可逐个移除,onChange 回传文件数组(不内置上传请求)",
"notification(props):右下角通知卡片(标题 + 描述 + 操作区),返回 { el, close } 句柄,缺省常驻需手动关闭"
], ],
"keyTypes": [ "keyTypes": [
"ButtonProps", "ButtonProps",
@@ -1946,6 +1947,52 @@
"键盘:区域可 Tab 聚焦,Enter/空格 打开选择器;拖拽时区域高亮,拖出恢复", "键盘:区域可 Tab 聚焦,Enter/空格 打开选择器;拖拽时区域高亮,拖出恢复",
"disabled 时不可点选、不接受拖拽;已选清单的 × 会触发 onChange 回传剩余文件" "disabled 时不可点选、不接受拖拽;已选清单的 × 会触发 onChange 回传剩余文件"
] ]
},
{
"id": "notification",
"title": "通知卡片 Notification",
"demo": "sec-notification",
"props": [
{
"n": "title",
"t": "string",
"r": 1,
"d": "通知标题"
},
{
"n": "description",
"t": "string",
"d": "补充说明;缺省不渲染"
},
{
"n": "variant",
"t": "'info' | 'success' | 'warning' | 'danger'",
"d": "语义色(左侧条与圆点);缺省 info"
},
{
"n": "duration",
"t": "number",
"d": "自动关闭毫秒;缺省 0 = 常驻不自动关"
},
{
"n": "closable",
"t": "boolean",
"d": "是否显示关闭按钮;缺省 true"
},
{
"n": "action",
"t": "unknown",
"d": "操作区内容(如按钮);缺省不渲染"
}
],
"example": "const { close } = notification({ title: '导出完成', description: 'report.csv 已开始下载', variant: 'success', duration: 4000 }); // close() 手动关闭",
"steps": [
"调用即入列:右下角堆叠,同屏最多 3 条,超出自动挤掉最早一条",
"缺省常驻(duration 0),需要自动消失就传毫秒数,如 duration: 4000",
"返回 { el, close } 句柄:程序化关闭用 close(),需要自己挂交互就用 el",
"action 放操作区(常见为按钮);variant 决定左侧条与圆点语义色",
"与 toast 的分工:toast 右上角轻量瞬时,notification 右下角可长读、可操作"
]
} }
] ]
}, },
+1 -1
View File
@@ -21,7 +21,7 @@
- `packages/core/table-core`(@ui-arch/table-core):Headless 表格逻辑内核 - `packages/core/table-core`(@ui-arch/table-core):Headless 表格逻辑内核
- `packages/core/frame`(@ui-arch/frame):signal + hyperscript 微框架 - `packages/core/frame`(@ui-arch/frame):signal + hyperscript 微框架
- `packages/skin/ui`(@ui-arch/ui):42 个 DOM 组件(表单 / 反馈 / 浮层 / 导航 / 表格),样式只消费令牌 - `packages/skin/ui`(@ui-arch/ui):43 个 DOM 组件(表单 / 反馈 / 浮层 / 导航 / 表格),样式只消费令牌
- `packages/design/tokens`(@ui-arch/tokens):设计令牌 - `packages/design/tokens`(@ui-arch/tokens):设计令牌
- `packages/tools/cli`(@ui-arch/cli):`ua` 命令行 + MCP 注册表服务(private) - `packages/tools/cli`(@ui-arch/cli):`ua` 命令行 + MCP 注册表服务(private)
- `apps/demo`(@ui-arch/demo):交互演示,产物 apps/demo/dist/demo.html - `apps/demo`(@ui-arch/demo):交互演示,产物 apps/demo/dist/demo.html
+1 -1
View File
@@ -34,7 +34,7 @@ ui-arch-mvp/
│ │ ├── table-core/ # 表格状态内核 │ │ ├── table-core/ # 表格状态内核
│ │ └── frame/ # 信号 + hyperscript 微框架 │ │ └── frame/ # 信号 + hyperscript 微框架
│ ├── skin/ # 皮肤层(DOM 组件,可发布) │ ├── skin/ # 皮肤层(DOM 组件,可发布)
│ │ └── ui/ # 42 个组件 + ui.css │ │ └── ui/ # 43 个组件 + ui.css
│ ├── design/ # 资产层(可发布) │ ├── design/ # 资产层(可发布)
│ │ └── tokens/ # --ua-* 设计令牌 │ │ └── tokens/ # --ua-* 设计令牌
│ └── tools/ # 工程层(private,不发布) │ └── tools/ # 工程层(private,不发布)
+3 -3
View File
@@ -11,10 +11,10 @@ npm run demo # 打开演示:apps/demo/dist/demo.html
npm run docs # 生成人类文档站 apps/docs/index.html + Agent 全量文档 llms-full.txt npm run docs # 生成人类文档站 apps/docs/index.html + Agent 全量文档 llms-full.txt
``` ```
## 组件(共 42 个) ## 组件(共 43 个)
<!-- ua:components --> <!-- ua:components -->
accordion · alert · avatar · badge · breadcrumb · button · carousel · card · checkbox · dataTable · descriptions · divider · drawer · empty · form · image · inputNumber · list · menu · modal · pagination · popover · progress · radioGroup · rate · segmented · select · skeleton · slider · spinner · statistic · stepper · switchButton · tabs · tag · textField · textarea · timeline · toast · tooltip · tree · upload accordion · alert · avatar · badge · breadcrumb · button · carousel · card · checkbox · dataTable · descriptions · divider · drawer · empty · form · image · inputNumber · list · menu · modal · notification · pagination · popover · progress · radioGroup · rate · segmented · select · skeleton · slider · spinner · statistic · stepper · switchButton · tabs · tag · textField · textarea · timeline · toast · tooltip · tree · upload
<!-- /ua:components --> <!-- /ua:components -->
分组:表单(button / textField / inputNumber / textarea / checkbox / switchButton / select / radioGroup / slider / rate / segmented / form / formItem)、反馈(alert / toast / spinner / progress / skeleton / badge / empty / statistic)、浮层(tooltip / popover / modal / drawer / menu)、导航(tabs / breadcrumb / pagination / accordion / stepper)、数据(dataTable / descriptions / tag / avatar / card)、其他(divider / timeline)。 分组:表单(button / textField / inputNumber / textarea / checkbox / switchButton / select / radioGroup / slider / rate / segmented / form / formItem)、反馈(alert / toast / spinner / progress / skeleton / badge / empty / statistic)、浮层(tooltip / popover / modal / drawer / menu)、导航(tabs / breadcrumb / pagination / accordion / stepper)、数据(dataTable / descriptions / tag / avatar / card)、其他(divider / timeline)。
@@ -25,7 +25,7 @@ accordion · alert · avatar · badge · breadcrumb · button · carousel · car
|---|---| |---|---|
| `packages/core/frame` | @ui-arch/frame:signal / computed / effect + hyperscript(无虚拟 DOM) | | `packages/core/frame` | @ui-arch/frame:signal / computed / effect + hyperscript(无虚拟 DOM) |
| `packages/core/table-core` | @ui-arch/table-core:headless 表格(filter → sort → paginate 派生管线) | | `packages/core/table-core` | @ui-arch/table-core:headless 表格(filter → sort → paginate 派生管线) |
| `packages/skin/ui` | @ui-arch/ui:42 个 DOM 组件 + ui.css(只消费 `--ua-*` 令牌) | | `packages/skin/ui` | @ui-arch/ui:43 个 DOM 组件 + ui.css(只消费 `--ua-*` 令牌) |
| `packages/design/tokens` | @ui-arch/tokens:设计令牌(暗色只换值不换名) | | `packages/design/tokens` | @ui-arch/tokens:设计令牌(暗色只换值不换名) |
| `packages/tools/cli` | @ui-arch/cli:`ua` 命令行与 stdio MCP server | | `packages/tools/cli` | @ui-arch/cli:`ua` 命令行与 stdio MCP server |
| `apps/demo` | 交互演示(esbuild 打包成单文件) | | `apps/demo` | 交互演示(esbuild 打包成单文件) |
+14
View File
@@ -28,6 +28,7 @@ import {
list, list,
carousel, carousel,
tree, tree,
notification,
upload, upload,
menu, menu,
modal, modal,
@@ -657,6 +658,19 @@ const content = h(
), ),
h('div', { class: 'row' }, upload({ accept: 'image/*', hint: '仅图片(accept: image/*)' }), upload({ disabled: true, hint: '禁用态' })), h('div', { class: 'row' }, upload({ accept: 'image/*', hint: '仅图片(accept: image/*)' }), upload({ disabled: true, hint: '禁用态' })),
), ),
section(
'sec-notification',
'通知卡片',
'notification:右下角堆叠(与右上角 toast 分区),标题 + 描述 + 操作区,缺省常驻需手动关闭;duration 传毫秒可自动消失。',
h(
'div',
{ class: 'row' },
button({ label: '常驻通知', variant: 'primary', onClick: () => notification({ title: '有 3 条待审报销', description: '王小明 / 李静 / 张伟提交的报销单等待复核', action: button({ label: '去处理', onClick: () => toast.success('已跳转待办') }) }) }),
button({ label: '成功(4s 自动关)', onClick: () => notification({ title: '导出完成', description: 'report-2026-10.csv 已开始下载', variant: 'success', duration: 4000 }) }),
button({ label: '警告(常驻)', variant: 'ghost', onClick: () => notification({ title: '连接不稳定', description: '正在重试第 2 次,请检查网络', variant: 'warning' }) }),
button({ label: '错误(可关)', variant: 'danger', onClick: () => notification({ title: '保存失败', description: '服务端返回 500,请稍后重试', variant: 'danger' }) }),
),
),
section( section(
'sec-menu', 'sec-menu',
'下拉菜单', '下拉菜单',
+29 -1
View File
@@ -1,6 +1,6 @@
# @ui-arch 组件库 Agent 文档(无图形版) # @ui-arch 组件库 Agent 文档(无图形版)
> 自动生成于 2026-10-26T03:45:25.126Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 自动生成于 2026-10-26T03:46:32.583Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
## 快速上手 ## 快速上手
@@ -1115,6 +1115,33 @@ upload({ multiple: true, accept: 'image/*', hint: '拖拽图片到此处', onCha
现场演示:apps/docs/demo.html#sec-upload 现场演示:apps/docs/demo.html#sec-upload
### notification — 通知卡片 Notification
参数:
- title(必填):string —— 通知标题
- description:string —— 补充说明;缺省不渲染
- variant:'info' | 'success' | 'warning' | 'danger' —— 语义色(左侧条与圆点);缺省 info
- duration:number —— 自动关闭毫秒;缺省 0 = 常驻不自动关
- closable:boolean —— 是否显示关闭按钮;缺省 true
- action:unknown —— 操作区内容(如按钮);缺省不渲染
示例:
```
const { close } = notification({ title: '导出完成', description: 'report.csv 已开始下载', variant: 'success', duration: 4000 }); // close() 手动关闭
```
手把手:
1. 调用即入列:右下角堆叠,同屏最多 3 条,超出自动挤掉最早一条
2. 缺省常驻(duration 0),需要自动消失就传毫秒数,如 duration: 4000
3. 返回 { el, close } 句柄:程序化关闭用 close(),需要自己挂交互就用 el
4. action 放操作区(常见为按钮);variant 决定左侧条与圆点语义色
5. 与 toast 的分工:toast 右上角轻量瞬时,notification 右下角可长读、可操作
现场演示:apps/docs/demo.html#sec-notification
API 速览: API 速览:
- button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用 - button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用
@@ -1161,6 +1188,7 @@ API 速览:
- carousel(props):轮播,items 横排位移,指示点/箭头/←→ 键切换,autoplay 毫秒自动前进且悬停暂停,index 信号可外部受控 - carousel(props):轮播,items 横排位移,指示点/箭头/←→ 键切换,autoplay 毫秒自动前进且悬停暂停,index 信号可外部受控
- tree(props):树形,递归节点 + 箭头展开/收起 + 点选节点,键盘 Enter 选中、→ 展开、← 收起,禁用节点不响应 - tree(props):树形,递归节点 + 箭头展开/收起 + 点选节点,键盘 Enter 选中、→ 展开、← 收起,禁用节点不响应
- upload(props):文件选择,点击或拖拽入列,清单可逐个移除,onChange 回传文件数组(不内置上传请求) - upload(props):文件选择,点击或拖拽入列,清单可逐个移除,onChange 回传文件数组(不内置上传请求)
- notification(props):右下角通知卡片(标题 + 描述 + 操作区),返回 { el, close } 句柄,缺省常驻需手动关闭
一行用法: 一行用法:
+1
View File
@@ -91,6 +91,7 @@
- carousel(props):轮播,items 横排位移,指示点/箭头/←→ 键切换,autoplay 毫秒自动前进且悬停暂停,index 信号可外部受控 - carousel(props):轮播,items 横排位移,指示点/箭头/←→ 键切换,autoplay 毫秒自动前进且悬停暂停,index 信号可外部受控
- tree(props):树形,递归节点 + 箭头展开/收起 + 点选节点,键盘 Enter 选中、→ 展开、← 收起,禁用节点不响应 - tree(props):树形,递归节点 + 箭头展开/收起 + 点选节点,键盘 Enter 选中、→ 展开、← 收起,禁用节点不响应
- upload(props):文件选择,点击或拖拽入列,清单可逐个移除,onChange 回传文件数组(不内置上传请求) - upload(props):文件选择,点击或拖拽入列,清单可逐个移除,onChange 回传文件数组(不内置上传请求)
- notification(props):右下角通知卡片(标题 + 描述 + 操作区),返回 { el, close } 句柄,缺省常驻需手动关闭
- 用法: `const checked = signal(false); checkbox({ label: '同意', checked }); const open = signal(false); modal({ title: '确认', open }, '内容')` - 用法: `const checked = signal(false); checkbox({ label: '同意', checked }); const open = signal(false); modal({ title: '确认', open }, '内容')`
- 约定: 皮肤不持有业务状态,一律委托 headless 内核或调用方信号 - 约定: 皮肤不持有业务状态,一律委托 headless 内核或调用方信号
- 约定: 表单类组件统一约定:*Props.value/checked/active 为 Signal 双向绑定,onChange/onInput 为回调 - 约定: 表单类组件统一约定:*Props.value/checked/active 为 Signal 双向绑定,onChange/onInput 为回调
+1
View File
@@ -18,6 +18,7 @@ export { avatar } from './avatar.js';
export { image } from './image.js'; export { image } from './image.js';
export { alert } from './alert.js'; export { alert } from './alert.js';
export { toast, showToast } from './toast.js'; export { toast, showToast } from './toast.js';
export { notification } from './notification.js';
export { tooltip } from './tooltip.js'; export { tooltip } from './tooltip.js';
export { popover } from './popover.js'; export { popover } from './popover.js';
export { flip } from './position.js'; export { flip } from './position.js';
+56
View File
@@ -0,0 +1,56 @@
import { h } from '@ui-arch/frame';
import type { NotificationProps } from './types.js';
let host: HTMLElement | null = null; // 右下角堆叠容器:首条通知出现时懒创建
const MAX_STACK = 3; // 同屏最多条数,超出挤掉最早一条
/*** 确保堆叠容器存在:fixed 挂 body 末尾,与右上角 toast 分区不打架 ***/
function ensureHost(): HTMLElement {
if (host) return host;
host = h('div', { class: 'ua-notice-host' });
document.body.appendChild(host);
return host;
}
/*** 退场并移除:摘 ua-open 触发过渡,transitionend 与 320ms 超时二选一兜底 ***/
function dismiss(item: HTMLElement): void {
item.classList.remove('ua-open');
item.addEventListener('transitionend', () => item.remove(), { once: true });
window.setTimeout(() => item.isConnected && item.remove(), 320); // reduced-motion 下不触发 transition
}
/*** 通知卡片:标题 + 描述 + 可选操作区,返回 { el, close } 句柄;duration 为 0 表示常驻 ***/
export function notification(props: NotificationProps): { el: HTMLElement; close: () => void } {
// —— 状态集中声明 ——
const duration = props.duration ?? 0; // 缺省常驻:通知通常需要用户读完
const closable = props.closable ?? true; // 缺省带关闭按钮
let timer = 0; // 自动关闭计时器句柄
const closeBtn = h('button', { type: 'button', class: 'ua-notice-close', 'aria-label': '关闭通知' }, '×') as HTMLButtonElement; // 关闭按钮,回调在下方绑定
const el = h(
'div',
{ class: `ua-notice ua-notice-${props.variant ?? 'info'}`, role: 'alert' },
h(
'div',
{ class: 'ua-notice-head' },
h('span', { class: 'ua-notice-dot', 'aria-hidden': 'true' }),
h('span', { class: 'ua-notice-title' }, props.title),
closable ? closeBtn : null,
),
props.description ? h('div', { class: 'ua-notice-desc' }, props.description) : null,
props.action ? h('div', { class: 'ua-notice-action' }, props.action) : null,
) as HTMLElement;
/*** 关闭:清计时器后走退场动画 ***/
const close = () => {
window.clearTimeout(timer);
dismiss(el);
};
closeBtn.addEventListener('click', () => close());
const stack = ensureHost();
if (stack.childElementCount >= MAX_STACK) stack.firstElementChild?.remove(); // 超出上限挤掉最早一条
stack.appendChild(el);
requestAnimationFrame(() => el.classList.add('ua-open')); // 下一帧入场,保证过渡生效
if (duration > 0) timer = window.setTimeout(() => dismiss(el), duration); // 常驻(0)不设定时器
return { el, close };
}
+10
View File
@@ -420,6 +420,16 @@ export interface ListProps {
onItemClick?: (item: ListItem, index: number) => void; // 条目点击回调;提供后条目可点击并带 hover 反馈 onItemClick?: (item: ListItem, index: number) => void; // 条目点击回调;提供后条目可点击并带 hover 反馈
} }
/*** 通知入参 ***/
export interface NotificationProps {
title: string; // 通知标题(必填)
description?: string; // 补充说明;缺省不渲染
variant?: 'info' | 'success' | 'warning' | 'danger'; // 语义色;缺省 info
duration?: number; // 自动关闭毫秒;缺省 0 = 常驻(通知通常要读完)
closable?: boolean; // 是否显示关闭按钮;缺省 true
action?: unknown; // 操作区内容(如按钮);缺省不渲染
}
/*** 文件选择入参 ***/ /*** 文件选择入参 ***/
export interface UploadProps { export interface UploadProps {
accept?: string; // 接受的类型(同原生 accept),如 'image/*,.pdf' accept?: string; // 接受的类型(同原生 accept),如 'image/*,.pdf'
+48 -1
View File
@@ -56,7 +56,8 @@
"list(props):列表,header/footer 外壳 + 条目 avatar/title/description/extra,onItemClick 让条目可点击,items 为空回落空状态", "list(props):列表,header/footer 外壳 + 条目 avatar/title/description/extra,onItemClick 让条目可点击,items 为空回落空状态",
"carousel(props):轮播,items 横排位移,指示点/箭头/←→ 键切换,autoplay 毫秒自动前进且悬停暂停,index 信号可外部受控", "carousel(props):轮播,items 横排位移,指示点/箭头/←→ 键切换,autoplay 毫秒自动前进且悬停暂停,index 信号可外部受控",
"tree(props):树形,递归节点 + 箭头展开/收起 + 点选节点,键盘 Enter 选中、→ 展开、← 收起,禁用节点不响应", "tree(props):树形,递归节点 + 箭头展开/收起 + 点选节点,键盘 Enter 选中、→ 展开、← 收起,禁用节点不响应",
"upload(props):文件选择,点击或拖拽入列,清单可逐个移除,onChange 回传文件数组(不内置上传请求)" "upload(props):文件选择,点击或拖拽入列,清单可逐个移除,onChange 回传文件数组(不内置上传请求)",
"notification(props):右下角通知卡片(标题 + 描述 + 操作区),返回 { el, close } 句柄,缺省常驻需手动关闭"
], ],
"keyTypes": [ "keyTypes": [
"ButtonProps", "ButtonProps",
@@ -1817,6 +1818,52 @@
"键盘:区域可 Tab 聚焦,Enter/空格 打开选择器;拖拽时区域高亮,拖出恢复", "键盘:区域可 Tab 聚焦,Enter/空格 打开选择器;拖拽时区域高亮,拖出恢复",
"disabled 时不可点选、不接受拖拽;已选清单的 × 会触发 onChange 回传剩余文件" "disabled 时不可点选、不接受拖拽;已选清单的 × 会触发 onChange 回传剩余文件"
] ]
},
{
"id": "notification",
"title": "通知卡片 Notification",
"demo": "sec-notification",
"props": [
{
"n": "title",
"t": "string",
"r": 1,
"d": "通知标题"
},
{
"n": "description",
"t": "string",
"d": "补充说明;缺省不渲染"
},
{
"n": "variant",
"t": "'info' | 'success' | 'warning' | 'danger'",
"d": "语义色(左侧条与圆点);缺省 info"
},
{
"n": "duration",
"t": "number",
"d": "自动关闭毫秒;缺省 0 = 常驻不自动关"
},
{
"n": "closable",
"t": "boolean",
"d": "是否显示关闭按钮;缺省 true"
},
{
"n": "action",
"t": "unknown",
"d": "操作区内容(如按钮);缺省不渲染"
}
],
"example": "const { close } = notification({ title: '导出完成', description: 'report.csv 已开始下载', variant: 'success', duration: 4000 }); // close() 手动关闭",
"steps": [
"调用即入列:右下角堆叠,同屏最多 3 条,超出自动挤掉最早一条",
"缺省常驻(duration 0),需要自动消失就传毫秒数,如 duration: 4000",
"返回 { el, close } 句柄:程序化关闭用 close(),需要自己挂交互就用 el",
"action 放操作区(常见为按钮);variant 决定左侧条与圆点语义色",
"与 toast 的分工:toast 右上角轻量瞬时,notification 右下角可长读、可操作"
]
} }
] ]
} }
+81
View File
@@ -2282,3 +2282,84 @@ button.ua-step-dot:hover {
transition: none; transition: none;
} }
} }
/* —— 通知卡片 —— */
.ua-notice-host {
position: fixed;
right: var(--ua-space-4);
bottom: var(--ua-space-4);
display: flex;
flex-direction: column;
gap: var(--ua-space-2);
z-index: 60;
pointer-events: none; /* 容器不挡点击,卡片自身恢复 */
}
.ua-notice {
width: 300px;
padding: var(--ua-space-3);
border: 1px solid var(--ua-color-border);
border-left: 3px solid var(--ua-color-accent);
border-radius: var(--ua-radius-md);
background: var(--ua-color-bg);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
pointer-events: auto;
opacity: 0;
transform: translateY(8px);
transition: opacity 0.2s ease, transform 0.2s ease;
}
.ua-notice-open {
opacity: 1;
transform: none;
}
.ua-notice-success { border-left-color: var(--ua-color-success); }
.ua-notice-warning { border-left-color: var(--ua-color-warning); }
.ua-notice-danger { border-left-color: var(--ua-color-danger); }
.ua-notice-head {
display: flex;
align-items: center;
gap: var(--ua-space-1);
}
.ua-notice-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--ua-color-accent);
flex: none;
}
.ua-notice-success .ua-notice-dot { background: var(--ua-color-success); }
.ua-notice-warning .ua-notice-dot { background: var(--ua-color-warning); }
.ua-notice-danger .ua-notice-dot { background: var(--ua-color-danger); }
.ua-notice-title {
flex: 1;
font-size: var(--ua-font-size-md);
font-weight: 600;
color: var(--ua-color-text);
}
.ua-notice-close {
border: none;
background: none;
color: var(--ua-color-text-muted);
cursor: pointer;
line-height: 1;
padding: 0 2px;
}
.ua-notice-close:hover {
color: var(--ua-color-text);
}
.ua-notice-desc {
margin-top: var(--ua-space-1);
font-size: var(--ua-font-size-sm);
color: var(--ua-color-text-muted);
line-height: 1.5;
}
.ua-notice-action {
margin-top: var(--ua-space-2);
display: flex;
gap: var(--ua-space-1);
justify-content: flex-end;
}
@media (prefers-reduced-motion: reduce) {
.ua-notice {
transition: none;
}
}