iter: b04 新增独立 pagination 分页器并与 dataTable 页码联动
This commit is contained in:
+2
-2
@@ -30,8 +30,8 @@
|
|||||||
"title": "新增独立 pagination 分页器组件并与 dataTable 演示联动",
|
"title": "新增独立 pagination 分页器组件并与 dataTable 演示联动",
|
||||||
"motivation": "table-core 已有 pagination 插件,但皮肤包没有独立分页器;dataTable 演示也未展示分页联动",
|
"motivation": "table-core 已有 pagination 插件,但皮肤包没有独立分页器;dataTable 演示也未展示分页联动",
|
||||||
"acceptance": "新增 pagination.ts:受控 API {page, pageSize, total, onChange},页码过多时折叠省略号,键盘可达,禁用态完整;demo 页 dataTable 一节增加分页联动演示;导出、manifest、文档、llms-full.txt 同步;npm run check 全绿",
|
"acceptance": "新增 pagination.ts:受控 API {page, pageSize, total, onChange},页码过多时折叠省略号,键盘可达,禁用态完整;demo 页 dataTable 一节增加分页联动演示;导出、manifest、文档、llms-full.txt 同步;npm run check 全绿",
|
||||||
"status": "open",
|
"status": "done",
|
||||||
"notes": ""
|
"notes": "pagination(props):page 信号双向绑定(受控),≤7 页全展开、更多时首尾 + 当前页 ±1 折叠省略号(renderList 重建),原生 button 键盘可达,aria-current / aria-label 完整,首末页禁用 ‹ ›;dataTable 新增可选 page 信号桥接(双向同步 + 越界钳制,防无效派发);demo sec-table 新增 pageSize 3 × 24 行的联动卡片"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "b05",
|
"id": "b05",
|
||||||
|
|||||||
+51
-4
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "ui-arch-registry-index/0.1",
|
"$schema": "ui-arch-registry-index/0.1",
|
||||||
"generatedAt": "2026-10-23T07:52:40.603Z",
|
"generatedAt": "2026-10-23T07:59:47.390Z",
|
||||||
"repoRoot": ".",
|
"repoRoot": ".",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@ui-arch/ui",
|
"name": "@ui-arch/ui",
|
||||||
"type": "skin-dom",
|
"type": "skin-dom",
|
||||||
"description": "DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / radioGroup / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。",
|
"description": "DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / radioGroup / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / pagination / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。",
|
||||||
"tags": [
|
"tags": [
|
||||||
"components",
|
"components",
|
||||||
"dom",
|
"dom",
|
||||||
@@ -155,6 +155,7 @@
|
|||||||
"toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失,出入场微动画;showToast(props) 为完整入参版本",
|
"toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失,出入场微动画;showToast(props) 为完整入参版本",
|
||||||
"tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起",
|
"tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起",
|
||||||
"skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画",
|
"skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画",
|
||||||
|
"pagination(props):独立分页器,page 信号双向绑定,页码过多折叠省略号,首末页禁用翻页,键盘可达",
|
||||||
"spinner(props):加载指示器,size 控制直径",
|
"spinner(props):加载指示器,size 控制直径",
|
||||||
"progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色",
|
"progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色",
|
||||||
"tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签",
|
"tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签",
|
||||||
@@ -178,6 +179,7 @@
|
|||||||
"ToastProps",
|
"ToastProps",
|
||||||
"TooltipProps",
|
"TooltipProps",
|
||||||
"SkeletonProps",
|
"SkeletonProps",
|
||||||
|
"PaginationProps",
|
||||||
"SpinnerProps",
|
"SpinnerProps",
|
||||||
"ProgressProps",
|
"ProgressProps",
|
||||||
"TabsProps",
|
"TabsProps",
|
||||||
@@ -196,7 +198,7 @@
|
|||||||
"语义色 variant 四档:default|info / success / warning / danger,一律来自令牌,禁止硬编码颜色"
|
"语义色 variant 四档:default|info / success / warning / danger,一律来自令牌,禁止硬编码颜色"
|
||||||
],
|
],
|
||||||
"docs": {
|
"docs": {
|
||||||
"intro": "19 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。",
|
"intro": "20 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。",
|
||||||
"steps": [
|
"steps": [
|
||||||
"安装依赖:pnpm add @ui-arch/ui @ui-arch/frame(workspace 内自动链接)",
|
"安装依赖:pnpm add @ui-arch/ui @ui-arch/frame(workspace 内自动链接)",
|
||||||
"引入样式:在应用入口 import '@ui-arch/ui/ui.css',暗色主题在根元素设 data-theme='dark'",
|
"引入样式:在应用入口 import '@ui-arch/ui/ui.css',暗色主题在根元素设 data-theme='dark'",
|
||||||
@@ -656,6 +658,45 @@
|
|||||||
"键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动"
|
"键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "pagination",
|
||||||
|
"title": "分页器 Pagination",
|
||||||
|
"demo": "sec-table",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"n": "page",
|
||||||
|
"t": "Signal<number>",
|
||||||
|
"r": 1,
|
||||||
|
"d": "当前页码信号,双向绑定:外部赋值自动翻页,点击页码写回信号"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "pageSize",
|
||||||
|
"t": "number",
|
||||||
|
"r": 1,
|
||||||
|
"d": "每页条数"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "total",
|
||||||
|
"t": "number",
|
||||||
|
"r": 1,
|
||||||
|
"d": "总条数"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onChange",
|
||||||
|
"t": "(page: number) => void",
|
||||||
|
"k": 1,
|
||||||
|
"d": "翻页回调"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "const page = signal(1); pagination({ page, pageSize: 3, total: 24, onChange: (p) => console.log(p) })",
|
||||||
|
"steps": [
|
||||||
|
"创建页码信号 page = signal(1),与 pageSize / total 一起传入",
|
||||||
|
"页码 ≤7 全部展开;更多时只保留首尾与当前页 ±1,缺口折叠为省略号",
|
||||||
|
"首末页自动禁用 ‹ › 翻页按钮,当前页禁点并标记 aria-current",
|
||||||
|
"与 dataTable 联动:把同一个 page 信号传给 dataTable 的 page 参数,两处翻页自动同步",
|
||||||
|
"键盘可达:全部是原生 button,Tab 聚焦后 Enter / Space 触发"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "modal",
|
"id": "modal",
|
||||||
"title": "弹窗 Modal",
|
"title": "弹窗 Modal",
|
||||||
@@ -788,6 +829,11 @@
|
|||||||
"n": "selectable",
|
"n": "selectable",
|
||||||
"t": "boolean",
|
"t": "boolean",
|
||||||
"d": "启用行选择列:全选三态(含半选)、行 checkbox、已选计数;缺省 false"
|
"d": "启用行选择列:全选三态(含半选)、行 checkbox、已选计数;缺省 false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "page",
|
||||||
|
"t": "Signal<number>",
|
||||||
|
"d": "受控页码信号:与内核分页双向同步,传同一个信号给独立 pagination 组件即可联动"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
||||||
@@ -797,7 +843,8 @@
|
|||||||
"selectable: true 开启行选择:表头全选框带 indeterminate 半选态,工具栏显示已选计数",
|
"selectable: true 开启行选择:表头全选框带 indeterminate 半选态,工具栏显示已选计数",
|
||||||
"选中行用低视觉比重样式(淡色底 + 弱化文字),选择状态存 rowKey,跨页不丢失",
|
"选中行用低视觉比重样式(淡色底 + 弱化文字),选择状态存 rowKey,跨页不丢失",
|
||||||
"工具栏输入框即筛选,输入即时过滤并回第 1 页;无匹配时显示空状态行",
|
"工具栏输入框即筛选,输入即时过滤并回第 1 页;无匹配时显示空状态行",
|
||||||
"表头粘性固定(滚动容器 max-height 420px),斑马纹 + 悬停高亮由 CSS 提供"
|
"表头粘性固定(滚动容器 max-height 420px),斑马纹 + 悬停高亮由 CSS 提供",
|
||||||
|
"需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
{"time":"2026-10-23T15:47:00+08:00","id":"b01","result":"done","summary":"新增 tooltip 悬浮提示组件(包裹式 API / 150ms 延迟 / Esc 收起 / reduced-motion 兜底),同步 manifest + llms-full.txt + demo 区块,并修复 ua style 对 URL 中 // 的误判;npm run check 全绿"}
|
{"time":"2026-10-23T15:47:00+08:00","id":"b01","result":"done","summary":"新增 tooltip 悬浮提示组件(包裹式 API / 150ms 延迟 / Esc 收起 / reduced-motion 兜底),同步 manifest + llms-full.txt + demo 区块,并修复 ua style 对 URL 中 // 的误判;npm run check 全绿"}
|
||||||
{"time":"2026-10-23T15:50:03+08:00","id":"b02","result":"done","summary":"新增 toast 消息提示组件:四语义工厂 + showToast,右上角堆叠,默认 3s 自动消失,微动画 + reduced-motion 兜底;manifest/docs/llms 同步,check 全绿"}
|
{"time":"2026-10-23T15:50:03+08:00","id":"b02","result":"done","summary":"新增 toast 消息提示组件:四语义工厂 + showToast,右上角堆叠,默认 3s 自动消失,微动画 + reduced-motion 兜底;manifest/docs/llms 同步,check 全绿"}
|
||||||
{"time":"2026-10-23T15:53:33+08:00","id":"b03","result":"done","summary":"新增 skeleton 骨架屏组件:text/rect/circle 三形状 + 扫光微动画 + reduced-motion 静态化;manifest/docs/llms/demo 同步,check 全绿"}
|
{"time":"2026-10-23T15:53:33+08:00","id":"b03","result":"done","summary":"新增 skeleton 骨架屏组件:text/rect/circle 三形状 + 扫光微动画 + reduced-motion 静态化;manifest/docs/llms/demo 同步,check 全绿"}
|
||||||
|
{"time":"2026-10-23T16:00:11+08:00","id":"b04","result":"done","summary":"新增独立 pagination 分页器(page 信号双向绑定 + 省略号折叠 + 键盘可达),dataTable 新增 page 受控桥接实现联动;demo 联动卡片,check 全绿"}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
form,
|
form,
|
||||||
formItem,
|
formItem,
|
||||||
modal,
|
modal,
|
||||||
|
pagination,
|
||||||
progress,
|
progress,
|
||||||
radioGroup,
|
radioGroup,
|
||||||
select,
|
select,
|
||||||
@@ -67,6 +68,9 @@ const username = signal('');
|
|||||||
const email = signal('');
|
const email = signal('');
|
||||||
const formResult = signal('');
|
const formResult = signal('');
|
||||||
|
|
||||||
|
// —— 分页联动状态:dataTable 与独立 pagination 共用同一页码信号 ——
|
||||||
|
const tablePage = signal(1);
|
||||||
|
|
||||||
const app = h(
|
const app = h(
|
||||||
'div',
|
'div',
|
||||||
{ class: 'page' },
|
{ class: 'page' },
|
||||||
@@ -190,6 +194,19 @@ const app = h(
|
|||||||
],
|
],
|
||||||
pageSize: 5,
|
pageSize: 5,
|
||||||
})),
|
})),
|
||||||
|
card(
|
||||||
|
{ title: '独立分页器联动(pageSize 3,共用页码信号)' },
|
||||||
|
dataTable({
|
||||||
|
data: users,
|
||||||
|
columns: [
|
||||||
|
{ key: 'name', header: '姓名' },
|
||||||
|
{ key: 'city', header: '城市' },
|
||||||
|
],
|
||||||
|
pageSize: 3,
|
||||||
|
page: tablePage,
|
||||||
|
}),
|
||||||
|
pagination({ page: tablePage, pageSize: 3, total: users.length, onChange: (p) => tablePage(p) }),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
section(
|
section(
|
||||||
'sec-card',
|
'sec-card',
|
||||||
|
|||||||
+30
-2
@@ -1,6 +1,6 @@
|
|||||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||||
|
|
||||||
> 自动生成于 2026-10-23T07:52:40.603Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
> 自动生成于 2026-10-23T07:59:47.390Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
## @ui-arch/ui(skin-dom)v0.1.0
|
## @ui-arch/ui(skin-dom)v0.1.0
|
||||||
|
|
||||||
19 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。
|
20 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。
|
||||||
|
|
||||||
### button — 按钮 Button
|
### button — 按钮 Button
|
||||||
|
|
||||||
@@ -395,6 +395,31 @@ const tab = signal('a'); tabs({ tabs: [{ value: 'a', label: '概览' }, { value:
|
|||||||
|
|
||||||
现场演示:apps/docs/demo.html#sec-tabs-modal
|
现场演示:apps/docs/demo.html#sec-tabs-modal
|
||||||
|
|
||||||
|
### pagination — 分页器 Pagination
|
||||||
|
|
||||||
|
参数:
|
||||||
|
|
||||||
|
- page(必填):Signal<number> —— 当前页码信号,双向绑定:外部赋值自动翻页,点击页码写回信号
|
||||||
|
- pageSize(必填):number —— 每页条数
|
||||||
|
- total(必填):number —— 总条数
|
||||||
|
- onChange(回调):(page: number) => void —— 翻页回调
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```
|
||||||
|
const page = signal(1); pagination({ page, pageSize: 3, total: 24, onChange: (p) => console.log(p) })
|
||||||
|
```
|
||||||
|
|
||||||
|
手把手:
|
||||||
|
|
||||||
|
1. 创建页码信号 page = signal(1),与 pageSize / total 一起传入
|
||||||
|
2. 页码 ≤7 全部展开;更多时只保留首尾与当前页 ±1,缺口折叠为省略号
|
||||||
|
3. 首末页自动禁用 ‹ › 翻页按钮,当前页禁点并标记 aria-current
|
||||||
|
4. 与 dataTable 联动:把同一个 page 信号传给 dataTable 的 page 参数,两处翻页自动同步
|
||||||
|
5. 键盘可达:全部是原生 button,Tab 聚焦后 Enter / Space 触发
|
||||||
|
|
||||||
|
现场演示:apps/docs/demo.html#sec-table
|
||||||
|
|
||||||
### modal — 弹窗 Modal
|
### modal — 弹窗 Modal
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
@@ -459,6 +484,7 @@ const name = signal(''); const f = form({ onSubmit: submit }, [formItem({ label:
|
|||||||
- filterKey:string —— 工具栏筛选的列;缺省取第一列
|
- filterKey:string —— 工具栏筛选的列;缺省取第一列
|
||||||
- pageSize:number —— 每页行数;缺省 5
|
- pageSize:number —— 每页行数;缺省 5
|
||||||
- selectable:boolean —— 启用行选择列:全选三态(含半选)、行 checkbox、已选计数;缺省 false
|
- selectable:boolean —— 启用行选择列:全选三态(含半选)、行 checkbox、已选计数;缺省 false
|
||||||
|
- page:Signal<number> —— 受控页码信号:与内核分页双向同步,传同一个信号给独立 pagination 组件即可联动
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
@@ -474,6 +500,7 @@ dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓
|
|||||||
4. 选中行用低视觉比重样式(淡色底 + 弱化文字),选择状态存 rowKey,跨页不丢失
|
4. 选中行用低视觉比重样式(淡色底 + 弱化文字),选择状态存 rowKey,跨页不丢失
|
||||||
5. 工具栏输入框即筛选,输入即时过滤并回第 1 页;无匹配时显示空状态行
|
5. 工具栏输入框即筛选,输入即时过滤并回第 1 页;无匹配时显示空状态行
|
||||||
6. 表头粘性固定(滚动容器 max-height 420px),斑马纹 + 悬停高亮由 CSS 提供
|
6. 表头粘性固定(滚动容器 max-height 420px),斑马纹 + 悬停高亮由 CSS 提供
|
||||||
|
7. 需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用
|
||||||
|
|
||||||
现场演示:apps/docs/demo.html#sec-table
|
现场演示:apps/docs/demo.html#sec-table
|
||||||
|
|
||||||
@@ -492,6 +519,7 @@ API 速览:
|
|||||||
- toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失,出入场微动画;showToast(props) 为完整入参版本
|
- toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失,出入场微动画;showToast(props) 为完整入参版本
|
||||||
- tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起
|
- tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起
|
||||||
- skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画
|
- skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画
|
||||||
|
- pagination(props):独立分页器,page 信号双向绑定,页码过多折叠省略号,首末页禁用翻页,键盘可达
|
||||||
- spinner(props):加载指示器,size 控制直径
|
- spinner(props):加载指示器,size 控制直径
|
||||||
- progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色
|
- progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色
|
||||||
- tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签
|
- tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
## @ui-arch/ui (v0.1.0)
|
## @ui-arch/ui (v0.1.0)
|
||||||
|
|
||||||
- 类型: skin-dom · 目录: packages/skin/ui
|
- 类型: skin-dom · 目录: packages/skin/ui
|
||||||
- 描述: DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / radioGroup / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。
|
- 描述: DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / radioGroup / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / pagination / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。
|
||||||
- API:
|
- API:
|
||||||
- button(props):按钮,variant = primary | ghost | danger
|
- button(props):按钮,variant = primary | ghost | danger
|
||||||
- textField(props):文本输入,value 信号双向绑定
|
- textField(props):文本输入,value 信号双向绑定
|
||||||
@@ -60,6 +60,7 @@
|
|||||||
- toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失,出入场微动画;showToast(props) 为完整入参版本
|
- toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失,出入场微动画;showToast(props) 为完整入参版本
|
||||||
- tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起
|
- tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起
|
||||||
- skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画
|
- skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画
|
||||||
|
- pagination(props):独立分页器,page 信号双向绑定,页码过多折叠省略号,首末页禁用翻页,键盘可达
|
||||||
- spinner(props):加载指示器,size 控制直径
|
- spinner(props):加载指示器,size 控制直径
|
||||||
- progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色
|
- progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色
|
||||||
- tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签
|
- tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签
|
||||||
|
|||||||
@@ -44,6 +44,19 @@ export function dataTable<T extends { id: RowKey }>(props: DataTableProps<T>): H
|
|||||||
});
|
});
|
||||||
const selectedCount = computed(() => state().selection.length);
|
const selectedCount = computed(() => state().selection.length);
|
||||||
|
|
||||||
|
// —— 受控页码桥接:外部信号 ↔ 内核分页;外部越界先钳制,signal 等值写回不触发,双向同步收敛安全 ——
|
||||||
|
if (props.page) {
|
||||||
|
const pageSig = props.page;
|
||||||
|
effect(() => {
|
||||||
|
const external = Math.min(api.getPageInfo().totalPages, Math.max(1, pageSig())); // 越界钳制,防无效派发
|
||||||
|
if (external !== info().page) table.dispatch({ type: 'setPage', page: external }); // 外部翻页 → 内核
|
||||||
|
});
|
||||||
|
effect(() => {
|
||||||
|
const internal = info().page;
|
||||||
|
if (internal !== pageSig()) pageSig(internal); // 内部翻页(筛选/内置翻页器)→ 写回外部
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const filterKey = props.filterKey ?? props.columns[0].key;
|
const filterKey = props.filterKey ?? props.columns[0].key;
|
||||||
const colspan = props.columns.length + (selectable ? 1 : 0);
|
const colspan = props.columns.length + (selectable ? 1 : 0);
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export { alert } from './alert.js';
|
|||||||
export { toast, showToast } from './toast.js';
|
export { toast, showToast } from './toast.js';
|
||||||
export { tooltip } from './tooltip.js';
|
export { tooltip } from './tooltip.js';
|
||||||
export { skeleton } from './skeleton.js';
|
export { skeleton } from './skeleton.js';
|
||||||
|
export { pagination } from './pagination.js';
|
||||||
export { spinner } from './spinner.js';
|
export { spinner } from './spinner.js';
|
||||||
export { progress } from './progress.js';
|
export { progress } from './progress.js';
|
||||||
export { tabs } from './tabs.js';
|
export { tabs } from './tabs.js';
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { h, renderList } from '@ui-arch/frame';
|
||||||
|
import type { PaginationProps } from './types.js';
|
||||||
|
|
||||||
|
/*** 页码序列:≤7 页全展开;否则保留首尾与当前页 ±1 窗口,缺口折叠为省略号(null 占位) ***/
|
||||||
|
function pageSequence(page: number, totalPages: number): (number | null)[] {
|
||||||
|
if (totalPages <= 7) return Array.from({ length: totalPages }, (_, i) => i + 1);
|
||||||
|
const marks = [1, 2, page - 1, page, page + 1, totalPages - 1, totalPages].filter((p) => p >= 1 && p <= totalPages);
|
||||||
|
const seq: (number | null)[] = [];
|
||||||
|
let prev = 0;
|
||||||
|
for (const p of [...new Set(marks)].sort((a, b) => a - b)) {
|
||||||
|
if (p - prev > 1) seq.push(null); // 缺口 → 省略号
|
||||||
|
seq.push(p);
|
||||||
|
prev = p;
|
||||||
|
}
|
||||||
|
return seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** 独立分页器:受控组件,page 为双向绑定信号;页码过多折叠省略号,首末页禁用翻页,原生 button 保证键盘可达 ***/
|
||||||
|
export function pagination(props: PaginationProps): HTMLElement {
|
||||||
|
const totalPages = Math.max(1, Math.ceil(props.total / props.pageSize)); // 总页数
|
||||||
|
const go = (p: number) => {
|
||||||
|
const next = Math.min(totalPages, Math.max(1, p)); // 钳制到有效页码范围
|
||||||
|
props.page(next);
|
||||||
|
props.onChange?.(next);
|
||||||
|
};
|
||||||
|
|
||||||
|
const nav = h('nav', { class: 'ua-pagination', 'aria-label': '分页' });
|
||||||
|
renderList(nav, () => {
|
||||||
|
const page = Math.min(totalPages, Math.max(1, props.page()));
|
||||||
|
const items: HTMLElement[] = [h('button', { class: 'ua-page-btn', disabled: page <= 1, 'aria-label': '上一页', onClick: () => go(page - 1) }, '‹')];
|
||||||
|
for (const it of pageSequence(page, totalPages)) {
|
||||||
|
items.push(
|
||||||
|
it == null
|
||||||
|
? h('span', { class: 'ua-page-gap', 'aria-hidden': 'true' }, '…')
|
||||||
|
: h('button', { class: `ua-page-btn${it === page ? ' ua-active' : ''}`, disabled: it === page, 'aria-current': it === page ? 'page' : null, 'aria-label': `第 ${it} 页`, onClick: () => go(it) }, String(it)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
items.push(h('button', { class: 'ua-page-btn', disabled: page >= totalPages, 'aria-label': '下一页', onClick: () => go(page + 1) }, '›'));
|
||||||
|
return items;
|
||||||
|
});
|
||||||
|
return nav;
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ export interface DataTableProps<T> {
|
|||||||
filterKey?: string; // 工具栏筛选的列;缺省取第一列
|
filterKey?: string; // 工具栏筛选的列;缺省取第一列
|
||||||
pageSize?: number; // 每页行数;缺省 5
|
pageSize?: number; // 每页行数;缺省 5
|
||||||
selectable?: boolean; // 是否启用行选择列(含全选三态与已选计数);缺省 false
|
selectable?: boolean; // 是否启用行选择列(含全选三态与已选计数);缺省 false
|
||||||
|
page?: Signal<number>; // 受控页码信号:与内核分页双向同步,可接独立 pagination 组件联动
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** 选项条目:select / radioGroup / tabs 共用的选项形状 ***/
|
/*** 选项条目:select / radioGroup / tabs 共用的选项形状 ***/
|
||||||
@@ -98,6 +99,14 @@ export interface SkeletonProps {
|
|||||||
rows?: number; // text 形状的行数;缺省 3
|
rows?: number; // text 形状的行数;缺省 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** 分页器入参 ***/
|
||||||
|
export interface PaginationProps {
|
||||||
|
page: Signal<number>; // 当前页码信号,双向绑定(外部赋值自动翻页)
|
||||||
|
pageSize: number; // 每页条数
|
||||||
|
total: number; // 总条数
|
||||||
|
onChange?: (page: number) => void; // 翻页回调
|
||||||
|
}
|
||||||
|
|
||||||
/*** toast 消息提示入参 ***/
|
/*** toast 消息提示入参 ***/
|
||||||
export interface ToastProps {
|
export interface ToastProps {
|
||||||
message: string; // 提示文案
|
message: string; // 提示文案
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@ui-arch/ui",
|
"name": "@ui-arch/ui",
|
||||||
"type": "skin-dom",
|
"type": "skin-dom",
|
||||||
"description": "DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / radioGroup / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。",
|
"description": "DOM 皮肤组件库:button / textField / textarea / checkbox / switch / select / radioGroup / card / badge / alert / toast / tooltip / skeleton / spinner / progress / tabs / pagination / modal / form / formItem / dataTable,构建在 @ui-arch/frame 与 @ui-arch/table-core 之上,样式只消费 --ua-* 令牌。",
|
||||||
"tags": [
|
"tags": [
|
||||||
"components",
|
"components",
|
||||||
"dom",
|
"dom",
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
"toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失,出入场微动画;showToast(props) 为完整入参版本",
|
"toast.info/success/warning/error(message, duration?):消息提示工厂,右上角堆叠,默认 3s 自动消失,出入场微动画;showToast(props) 为完整入参版本",
|
||||||
"tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起",
|
"tooltip(props, trigger):悬浮提示,包裹触发器,hover / focus 延迟 150ms 出场,移出 / 失焦 / Esc 收起",
|
||||||
"skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画",
|
"skeleton(props):骨架屏占位,variant = text | rect | circle,text 支持 rows 多行,扫光微动画",
|
||||||
|
"pagination(props):独立分页器,page 信号双向绑定,页码过多折叠省略号,首末页禁用翻页,键盘可达",
|
||||||
"spinner(props):加载指示器,size 控制直径",
|
"spinner(props):加载指示器,size 控制直径",
|
||||||
"progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色",
|
"progress(props):进度条,轨道 + 渐变流动填充,value 信号驱动 0-100,满格切 success 色",
|
||||||
"tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签",
|
"tabs(props):标签页,active 信号双向绑定,滑动指示条 + 方向键环形导航,支持禁用页签",
|
||||||
@@ -49,6 +50,7 @@
|
|||||||
"ToastProps",
|
"ToastProps",
|
||||||
"TooltipProps",
|
"TooltipProps",
|
||||||
"SkeletonProps",
|
"SkeletonProps",
|
||||||
|
"PaginationProps",
|
||||||
"SpinnerProps",
|
"SpinnerProps",
|
||||||
"ProgressProps",
|
"ProgressProps",
|
||||||
"TabsProps",
|
"TabsProps",
|
||||||
@@ -67,7 +69,7 @@
|
|||||||
"语义色 variant 四档:default|info / success / warning / danger,一律来自令牌,禁止硬编码颜色"
|
"语义色 variant 四档:default|info / success / warning / danger,一律来自令牌,禁止硬编码颜色"
|
||||||
],
|
],
|
||||||
"docs": {
|
"docs": {
|
||||||
"intro": "19 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。",
|
"intro": "20 个 DOM 组件,统一用法:组件是普通函数,传入 props 返回 DOM 节点;需要状态时先创建 signal 再双向绑定。引入 CSS:import '@ui-arch/ui/ui.css'(内部已消费 tokens)。",
|
||||||
"steps": [
|
"steps": [
|
||||||
"安装依赖:pnpm add @ui-arch/ui @ui-arch/frame(workspace 内自动链接)",
|
"安装依赖:pnpm add @ui-arch/ui @ui-arch/frame(workspace 内自动链接)",
|
||||||
"引入样式:在应用入口 import '@ui-arch/ui/ui.css',暗色主题在根元素设 data-theme='dark'",
|
"引入样式:在应用入口 import '@ui-arch/ui/ui.css',暗色主题在根元素设 data-theme='dark'",
|
||||||
@@ -527,6 +529,45 @@
|
|||||||
"键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动"
|
"键盘可访问:Tab 聚焦激活页签,←→ 在启用页签间环形移动"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "pagination",
|
||||||
|
"title": "分页器 Pagination",
|
||||||
|
"demo": "sec-table",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"n": "page",
|
||||||
|
"t": "Signal<number>",
|
||||||
|
"r": 1,
|
||||||
|
"d": "当前页码信号,双向绑定:外部赋值自动翻页,点击页码写回信号"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "pageSize",
|
||||||
|
"t": "number",
|
||||||
|
"r": 1,
|
||||||
|
"d": "每页条数"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "total",
|
||||||
|
"t": "number",
|
||||||
|
"r": 1,
|
||||||
|
"d": "总条数"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onChange",
|
||||||
|
"t": "(page: number) => void",
|
||||||
|
"k": 1,
|
||||||
|
"d": "翻页回调"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "const page = signal(1); pagination({ page, pageSize: 3, total: 24, onChange: (p) => console.log(p) })",
|
||||||
|
"steps": [
|
||||||
|
"创建页码信号 page = signal(1),与 pageSize / total 一起传入",
|
||||||
|
"页码 ≤7 全部展开;更多时只保留首尾与当前页 ±1,缺口折叠为省略号",
|
||||||
|
"首末页自动禁用 ‹ › 翻页按钮,当前页禁点并标记 aria-current",
|
||||||
|
"与 dataTable 联动:把同一个 page 信号传给 dataTable 的 page 参数,两处翻页自动同步",
|
||||||
|
"键盘可达:全部是原生 button,Tab 聚焦后 Enter / Space 触发"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "modal",
|
"id": "modal",
|
||||||
"title": "弹窗 Modal",
|
"title": "弹窗 Modal",
|
||||||
@@ -659,6 +700,11 @@
|
|||||||
"n": "selectable",
|
"n": "selectable",
|
||||||
"t": "boolean",
|
"t": "boolean",
|
||||||
"d": "启用行选择列:全选三态(含半选)、行 checkbox、已选计数;缺省 false"
|
"d": "启用行选择列:全选三态(含半选)、行 checkbox、已选计数;缺省 false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "page",
|
||||||
|
"t": "Signal<number>",
|
||||||
|
"d": "受控页码信号:与内核分页双向同步,传同一个信号给独立 pagination 组件即可联动"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
||||||
@@ -668,7 +714,8 @@
|
|||||||
"selectable: true 开启行选择:表头全选框带 indeterminate 半选态,工具栏显示已选计数",
|
"selectable: true 开启行选择:表头全选框带 indeterminate 半选态,工具栏显示已选计数",
|
||||||
"选中行用低视觉比重样式(淡色底 + 弱化文字),选择状态存 rowKey,跨页不丢失",
|
"选中行用低视觉比重样式(淡色底 + 弱化文字),选择状态存 rowKey,跨页不丢失",
|
||||||
"工具栏输入框即筛选,输入即时过滤并回第 1 页;无匹配时显示空状态行",
|
"工具栏输入框即筛选,输入即时过滤并回第 1 页;无匹配时显示空状态行",
|
||||||
"表头粘性固定(滚动容器 max-height 420px),斑马纹 + 悬停高亮由 CSS 提供"
|
"表头粘性固定(滚动容器 max-height 420px),斑马纹 + 悬停高亮由 CSS 提供",
|
||||||
|
"需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -379,6 +379,48 @@
|
|||||||
background: color-mix(in srgb, var(--ua-color-danger) 12%, transparent);
|
background: color-mix(in srgb, var(--ua-color-danger) 12%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* —— 独立分页器 —— */
|
||||||
|
.ua-pagination {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--ua-space-1);
|
||||||
|
}
|
||||||
|
.ua-page-btn {
|
||||||
|
min-width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 var(--ua-space-2);
|
||||||
|
border: 1px solid var(--ua-color-border);
|
||||||
|
border-radius: var(--ua-radius-sm);
|
||||||
|
background: var(--ua-color-bg);
|
||||||
|
color: var(--ua-color-text);
|
||||||
|
font-size: var(--ua-font-size-md);
|
||||||
|
cursor: pointer;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
|
||||||
|
}
|
||||||
|
.ua-page-btn:hover:not(:disabled) {
|
||||||
|
border-color: var(--ua-color-accent);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.ua-page-btn:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--ua-color-accent);
|
||||||
|
box-shadow: 0 0 0 3px color-mix(in srgb, var(--ua-color-accent) 18%, transparent);
|
||||||
|
}
|
||||||
|
.ua-page-btn:disabled {
|
||||||
|
opacity: 0.45;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.ua-page-btn.ua-active {
|
||||||
|
border-color: var(--ua-color-accent);
|
||||||
|
background: color-mix(in srgb, var(--ua-color-accent) 10%, transparent); /* 选中态低视觉比重 */
|
||||||
|
color: var(--ua-color-text-muted);
|
||||||
|
}
|
||||||
|
.ua-page-gap {
|
||||||
|
color: var(--ua-color-text-muted);
|
||||||
|
padding: 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
/* —— 骨架屏:占位灰块 + 扫光 —— */
|
/* —— 骨架屏:占位灰块 + 扫光 —— */
|
||||||
.ua-skeleton {
|
.ua-skeleton {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -744,6 +786,7 @@
|
|||||||
.ua-switch-thumb,
|
.ua-switch-thumb,
|
||||||
.ua-tab,
|
.ua-tab,
|
||||||
.ua-tabs-indicator,
|
.ua-tabs-indicator,
|
||||||
|
.ua-page-btn,
|
||||||
.ua-table tbody tr,
|
.ua-table tbody tr,
|
||||||
.ua-progress-fill,
|
.ua-progress-fill,
|
||||||
.ua-progress-fill::after,
|
.ua-progress-fill::after,
|
||||||
|
|||||||
Reference in New Issue
Block a user