iter(g55): 新增 checkboxGroup 多选组组件
This commit is contained in:
+58
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "ui-arch-registry-index/0.1",
|
"$schema": "ui-arch-registry-index/0.1",
|
||||||
"generatedAt": "2026-10-26T04:18:05.467Z",
|
"generatedAt": "2026-10-26T04:19:05.352Z",
|
||||||
"repoRoot": ".",
|
"repoRoot": ".",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
@@ -196,7 +196,8 @@
|
|||||||
"backTop(props):返回顶部悬浮按钮,越过阈值才浮现,外环 conic-gradient 显示已滚比例,点击平滑回顶",
|
"backTop(props):返回顶部悬浮按钮,越过阈值才浮现,外环 conic-gradient 显示已滚比例,点击平滑回顶",
|
||||||
"colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号",
|
"colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号",
|
||||||
"tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose",
|
"tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose",
|
||||||
"watermark(props, ...children):内容容器 + 平铺水印覆盖层,canvas 生成单元、颜色取自令牌,覆盖层不挡交互"
|
"watermark(props, ...children):内容容器 + 平铺水印覆盖层,canvas 生成单元、颜色取自令牌,覆盖层不挡交互",
|
||||||
|
"checkboxGroup(props):多选组,value 为数组信号,min/max 用禁用边界项实现(不抛错),checkAll 支持半选且只统计未禁用项"
|
||||||
],
|
],
|
||||||
"keyTypes": [
|
"keyTypes": [
|
||||||
"ButtonProps",
|
"ButtonProps",
|
||||||
@@ -2461,6 +2462,61 @@
|
|||||||
"color 传 var(--ua-*) 时会先解析成计算值(canvas 不认 CSS 变量),所以暗色主题下颜色会自动跟随",
|
"color 传 var(--ua-*) 时会先解析成计算值(canvas 不认 CSS 变量),所以暗色主题下颜色会自动跟随",
|
||||||
"覆盖层为 pointer-events:none,下方按钮/输入框照常可点;watermark 根节点自带圆角与裁剪"
|
"覆盖层为 pointer-events:none,下方按钮/输入框照常可点;watermark 根节点自带圆角与裁剪"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "checkboxGroup",
|
||||||
|
"title": "多选组 CheckboxGroup",
|
||||||
|
"demo": "sec-checkgroup",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"n": "options",
|
||||||
|
"t": "CheckOption[]",
|
||||||
|
"r": 1,
|
||||||
|
"d": "选项集合:value / label / 可选 disabled"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "value",
|
||||||
|
"t": "Signal<string[]>",
|
||||||
|
"d": "选中值数组信号;不传则内部维护"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "min",
|
||||||
|
"t": "number",
|
||||||
|
"d": "最少选几项:到下限后已选项禁用,防止取消"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "max",
|
||||||
|
"t": "number",
|
||||||
|
"d": "最多选几项:到上限后未选项禁用"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "checkAll",
|
||||||
|
"t": "boolean",
|
||||||
|
"d": "是否显示全选行(含半选);缺省 false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "direction",
|
||||||
|
"t": "'horizontal' | 'vertical'",
|
||||||
|
"d": "排列方向;缺省 horizontal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "disabled",
|
||||||
|
"t": "boolean | Signal<boolean>",
|
||||||
|
"d": "整组禁用"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onChange",
|
||||||
|
"t": "(values: string[]) => void",
|
||||||
|
"d": "变更回调(传副本)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "checkboxGroup({ value: permPick, min: 1, max: 3, checkAll: true, options: [{ value: 'read', label: '读取' }, { value: 'write', label: '写入' }] })",
|
||||||
|
"steps": [
|
||||||
|
"options 顺序即渲染顺序;勾选后把整份数组写回 value 信号并触发 onChange",
|
||||||
|
"max 生效时未选项自动禁用,min 生效时已选项自动禁用——用禁用表达边界,不抛错也不静默回滚",
|
||||||
|
"checkAll 行的选中/半选只统计未禁用项,禁用项不计入基数",
|
||||||
|
"direction 传 vertical 改为纵向排列;disabled 支持信号,整组响应式禁用"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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):53 个 DOM 组件(表单 / 反馈 / 浮层 / 导航 / 表格),样式只消费令牌
|
- `packages/skin/ui`(@ui-arch/ui):54 个 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
@@ -34,7 +34,7 @@ ui-arch-mvp/
|
|||||||
│ │ ├── table-core/ # 表格状态内核
|
│ │ ├── table-core/ # 表格状态内核
|
||||||
│ │ └── frame/ # 信号 + hyperscript 微框架
|
│ │ └── frame/ # 信号 + hyperscript 微框架
|
||||||
│ ├── skin/ # 皮肤层(DOM 组件,可发布)
|
│ ├── skin/ # 皮肤层(DOM 组件,可发布)
|
||||||
│ │ └── ui/ # 53 个组件 + ui.css
|
│ │ └── ui/ # 54 个组件 + ui.css
|
||||||
│ ├── design/ # 资产层(可发布)
|
│ ├── design/ # 资产层(可发布)
|
||||||
│ │ └── tokens/ # --ua-* 设计令牌
|
│ │ └── tokens/ # --ua-* 设计令牌
|
||||||
│ └── tools/ # 工程层(private,不发布)
|
│ └── tools/ # 工程层(private,不发布)
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ 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
|
||||||
```
|
```
|
||||||
|
|
||||||
## 组件(共 53 个)
|
## 组件(共 54 个)
|
||||||
|
|
||||||
<!-- ua:components -->
|
<!-- ua:components -->
|
||||||
accordion · alert · autoComplete · avatar · backTop · badge · breadcrumb · button · carousel · card · cascader · checkbox · colorPicker · dataTable · datePicker · descriptions · divider · drawer · empty · form · image · inputNumber · list · menu · modal · notification · pagination · popover · progress · radioGroup · rate · result · segmented · select · skeleton · slider · spinner · splitter · statistic · stepper · switchButton · tabs · tag · textField · textarea · timeline · toast · tooltip · tour · transfer · tree · upload · watermark
|
accordion · alert · autoComplete · avatar · backTop · badge · breadcrumb · button · carousel · card · cascader · checkbox · checkboxGroup · colorPicker · dataTable · datePicker · descriptions · divider · drawer · empty · form · image · inputNumber · list · menu · modal · notification · pagination · popover · progress · radioGroup · rate · result · segmented · select · skeleton · slider · spinner · splitter · statistic · stepper · switchButton · tabs · tag · textField · textarea · timeline · toast · tooltip · tour · transfer · tree · upload · watermark
|
||||||
<!-- /ua:components -->
|
<!-- /ua:components -->
|
||||||
|
|
||||||
分组:表单(button / textField / inputNumber / textarea / checkbox / colorPicker / switchButton / select / radioGroup / slider / rate / autoComplete / datePicker / segmented / upload / form + formItem / transfer / cascader)、反馈(alert / toast / notification / result / spinner / progress / skeleton / badge / empty / statistic)、浮层(tooltip / popover / modal / drawer / menu)、导航(tabs / breadcrumb / pagination / accordion / stepper / tree)、数据展示(dataTable / descriptions / card / list / timeline / tag / avatar / image / carousel)、其他(divider / splitter / backTop / tour / watermark)。
|
分组:表单(button / textField / inputNumber / textarea / checkbox / checkboxGroup / colorPicker / switchButton / select / radioGroup / slider / rate / autoComplete / datePicker / segmented / upload / form + formItem / transfer / cascader)、反馈(alert / toast / notification / result / spinner / progress / skeleton / badge / empty / statistic)、浮层(tooltip / popover / modal / drawer / menu)、导航(tabs / breadcrumb / pagination / accordion / stepper / tree)、数据展示(dataTable / descriptions / card / list / timeline / tag / avatar / image / carousel)、其他(divider / splitter / backTop / tour / watermark)。
|
||||||
|
|
||||||
## 可访问性约定
|
## 可访问性约定
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ accordion · alert · autoComplete · avatar · backTop · badge · breadcrumb
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `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:53 个 DOM 组件 + ui.css(只消费 `--ua-*` 令牌) |
|
| `packages/skin/ui` | @ui-arch/ui:54 个 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 打包成单文件) |
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
card,
|
card,
|
||||||
cascader,
|
cascader,
|
||||||
checkbox,
|
checkbox,
|
||||||
|
checkboxGroup,
|
||||||
colorPicker,
|
colorPicker,
|
||||||
dataTable,
|
dataTable,
|
||||||
datePicker,
|
datePicker,
|
||||||
@@ -191,6 +192,7 @@ const listPick = signal(''); // 列表点击选中的条目标题
|
|||||||
const slideIndex = signal(0); // 轮播当前下标(外部受控演示)
|
const slideIndex = signal(0); // 轮播当前下标(外部受控演示)
|
||||||
const cascaderPath = signal<string[]>(['east', 'sh']); // 级联选择路径(外部受控演示)
|
const cascaderPath = signal<string[]>(['east', 'sh']); // 级联选择路径(外部受控演示)
|
||||||
const splitSizes = signal<number[]>([35, 65]); // 分栏比例(外部受控演示)
|
const splitSizes = signal<number[]>([35, 65]); // 分栏比例(外部受控演示)
|
||||||
|
const permPick = signal<string[]>(['read']); // 多选组选中值(外部受控演示)
|
||||||
const colorVal = signal('var(--ua-color-accent)'); // 颜色选择值(外部受控演示)
|
const colorVal = signal('var(--ua-color-accent)'); // 颜色选择值(外部受控演示)
|
||||||
const dateVal = signal('2026-10-26'); // 日期选择值(外部受控演示)
|
const dateVal = signal('2026-10-26'); // 日期选择值(外部受控演示)
|
||||||
const acValue = signal(''); // 自动补全输入值(外部受控演示)
|
const acValue = signal(''); // 自动补全输入值(外部受控演示)
|
||||||
@@ -690,6 +692,31 @@ const content = h(
|
|||||||
button({ label: '错误(可关)', variant: 'danger', onClick: () => notification({ title: '保存失败', description: '服务端返回 500,请稍后重试', variant: 'danger' }) }),
|
button({ label: '错误(可关)', variant: 'danger', onClick: () => notification({ title: '保存失败', description: '服务端返回 500,请稍后重试', variant: 'danger' }) }),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
section(
|
||||||
|
'sec-checkgroup',
|
||||||
|
'多选组',
|
||||||
|
'checkboxGroup:数组信号双向绑定,min/max 用「禁用边界项」代替报错,checkAll 带半选,只统计未禁用项。',
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{ class: 'col' },
|
||||||
|
checkboxGroup({
|
||||||
|
value: permPick,
|
||||||
|
min: 1,
|
||||||
|
max: 3,
|
||||||
|
checkAll: true,
|
||||||
|
onChange: (v) => toast.info(`已选 ${v.length} 项`),
|
||||||
|
options: [
|
||||||
|
{ value: 'read', label: '读取' },
|
||||||
|
{ value: 'write', label: '写入' },
|
||||||
|
{ value: 'delete', label: '删除' },
|
||||||
|
{ value: 'share', label: '分享' },
|
||||||
|
{ value: 'audit', label: '审计日志(不可授予)', disabled: true },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
checkboxGroup({ direction: 'vertical', options: [{ value: 'a', label: '纵向 A' }, { value: 'b', label: '纵向 B' }] }),
|
||||||
|
h('span', { class: 'out' }, () => `已选:${permPick().join(' / ') || '无'}(下限 1、上限 3)`),
|
||||||
|
),
|
||||||
|
),
|
||||||
section(
|
section(
|
||||||
'sec-watermark',
|
'sec-watermark',
|
||||||
'水印',
|
'水印',
|
||||||
|
|||||||
+30
-1
@@ -1,6 +1,6 @@
|
|||||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||||
|
|
||||||
> 自动生成于 2026-10-26T04:18:05.467Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
> 自动生成于 2026-10-26T04:19:05.352Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
@@ -1412,6 +1412,34 @@ watermark({ text: '内部资料\n仅限内网', gap: 160 }, card({ title: '受
|
|||||||
|
|
||||||
现场演示:apps/docs/demo.html#sec-watermark
|
现场演示:apps/docs/demo.html#sec-watermark
|
||||||
|
|
||||||
|
### checkboxGroup — 多选组 CheckboxGroup
|
||||||
|
|
||||||
|
参数:
|
||||||
|
|
||||||
|
- options(必填):CheckOption[] —— 选项集合:value / label / 可选 disabled
|
||||||
|
- value:Signal<string[]> —— 选中值数组信号;不传则内部维护
|
||||||
|
- min:number —— 最少选几项:到下限后已选项禁用,防止取消
|
||||||
|
- max:number —— 最多选几项:到上限后未选项禁用
|
||||||
|
- checkAll:boolean —— 是否显示全选行(含半选);缺省 false
|
||||||
|
- direction:'horizontal' | 'vertical' —— 排列方向;缺省 horizontal
|
||||||
|
- disabled:boolean | Signal<boolean> —— 整组禁用
|
||||||
|
- onChange:(values: string[]) => void —— 变更回调(传副本)
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```
|
||||||
|
checkboxGroup({ value: permPick, min: 1, max: 3, checkAll: true, options: [{ value: 'read', label: '读取' }, { value: 'write', label: '写入' }] })
|
||||||
|
```
|
||||||
|
|
||||||
|
手把手:
|
||||||
|
|
||||||
|
1. options 顺序即渲染顺序;勾选后把整份数组写回 value 信号并触发 onChange
|
||||||
|
2. max 生效时未选项自动禁用,min 生效时已选项自动禁用——用禁用表达边界,不抛错也不静默回滚
|
||||||
|
3. checkAll 行的选中/半选只统计未禁用项,禁用项不计入基数
|
||||||
|
4. direction 传 vertical 改为纵向排列;disabled 支持信号,整组响应式禁用
|
||||||
|
|
||||||
|
现场演示:apps/docs/demo.html#sec-checkgroup
|
||||||
|
|
||||||
API 速览:
|
API 速览:
|
||||||
|
|
||||||
- button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用;type 缺省 button(表单内不隐式提交)
|
- button(props):按钮,variant = primary | ghost | danger,disabled 支持布尔或信号禁用;type 缺省 button(表单内不隐式提交)
|
||||||
@@ -1469,6 +1497,7 @@ API 速览:
|
|||||||
- colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号
|
- colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号
|
||||||
- tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose
|
- tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose
|
||||||
- watermark(props, ...children):内容容器 + 平铺水印覆盖层,canvas 生成单元、颜色取自令牌,覆盖层不挡交互
|
- watermark(props, ...children):内容容器 + 平铺水印覆盖层,canvas 生成单元、颜色取自令牌,覆盖层不挡交互
|
||||||
|
- checkboxGroup(props):多选组,value 为数组信号,min/max 用禁用边界项实现(不抛错),checkAll 支持半选且只统计未禁用项
|
||||||
|
|
||||||
一行用法:
|
一行用法:
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,7 @@
|
|||||||
- colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号
|
- colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号
|
||||||
- tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose
|
- tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose
|
||||||
- watermark(props, ...children):内容容器 + 平铺水印覆盖层,canvas 生成单元、颜色取自令牌,覆盖层不挡交互
|
- watermark(props, ...children):内容容器 + 平铺水印覆盖层,canvas 生成单元、颜色取自令牌,覆盖层不挡交互
|
||||||
|
- checkboxGroup(props):多选组,value 为数组信号,min/max 用禁用边界项实现(不抛错),checkAll 支持半选且只统计未禁用项
|
||||||
- 用法: `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 为回调
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import { effect, h, signal } from '@ui-arch/frame';
|
||||||
|
import type { Signal } from '@ui-arch/frame';
|
||||||
|
import type { CheckboxGroupProps } from './types.js';
|
||||||
|
|
||||||
|
/*** 多选组:value 数组信号双向绑定,min/max 用「禁用边界」而非报错,可选全选行(含半选) ***/
|
||||||
|
export function checkboxGroup(props: CheckboxGroupProps): HTMLElement {
|
||||||
|
// —— 状态集中声明 ——
|
||||||
|
const value: Signal<string[]> = props.value ?? signal<string[]>([]); // 选中值
|
||||||
|
const inputs: HTMLInputElement[] = []; // 各选项勾选框
|
||||||
|
const dir = props.direction ?? 'horizontal'; // 排列方向
|
||||||
|
const usable = props.options.filter((o) => !o.disabled); // 可勾选项(全选基数)
|
||||||
|
|
||||||
|
/*** 整组禁用:布尔直接取,信号在此读取以纳入 effect 依赖 ***/
|
||||||
|
const groupOff = (): boolean => (typeof props.disabled === 'function' ? props.disabled() : props.disabled === true);
|
||||||
|
|
||||||
|
/*** 变更落库:写信号 + 回调,边界不在此处拒绝——由 sync 提前禁用越界选项 ***/
|
||||||
|
const update = (next: string[]): void => {
|
||||||
|
value(next);
|
||||||
|
props.onChange?.([...next]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const all = h('input', { type: 'checkbox', class: 'ua-checkbox', 'aria-label': '全选' }) as HTMLInputElement;
|
||||||
|
all.addEventListener('change', () => {
|
||||||
|
update(all.checked ? usable.map((o) => o.value) : []); // 全选只覆盖未禁用项
|
||||||
|
});
|
||||||
|
|
||||||
|
const rows = props.options.map((opt) => {
|
||||||
|
const input = h('input', { type: 'checkbox', class: 'ua-checkbox' }) as HTMLInputElement;
|
||||||
|
input.value = opt.value;
|
||||||
|
input.addEventListener('change', () => {
|
||||||
|
const cur = new Set(value());
|
||||||
|
input.checked ? cur.add(opt.value) : cur.delete(opt.value); // 用户操作写回
|
||||||
|
update([...cur]);
|
||||||
|
});
|
||||||
|
inputs.push(input);
|
||||||
|
return h('label', { class: 'ua-check-row' }, input, opt.label);
|
||||||
|
});
|
||||||
|
|
||||||
|
/*** 同步勾选与可见可用性:到上限禁用未选项、到下限禁用已选项(不改值只锁操作) ***/
|
||||||
|
const sync = (): void => {
|
||||||
|
const cur = value();
|
||||||
|
const off = groupOff();
|
||||||
|
const atMax = props.max != null && cur.length >= props.max;
|
||||||
|
const atMin = props.min != null && cur.length <= props.min;
|
||||||
|
props.options.forEach((opt, i) => {
|
||||||
|
const input = inputs[i];
|
||||||
|
const on = cur.includes(opt.value);
|
||||||
|
input.checked = on;
|
||||||
|
input.disabled = off || opt.disabled === true || (on ? atMin : atMax); // 边界项锁死,避免越界
|
||||||
|
});
|
||||||
|
const picked = usable.filter((o) => cur.includes(o.value)).length;
|
||||||
|
all.checked = picked > 0 && picked === usable.length;
|
||||||
|
all.indeterminate = picked > 0 && picked < usable.length; // 半选:部分已选
|
||||||
|
all.disabled = off || (picked === usable.length && atMin); // 全选行同样受边界约束
|
||||||
|
};
|
||||||
|
|
||||||
|
effect(sync);
|
||||||
|
const body = h('div', { class: `ua-check-group ua-check-group-${dir}` }, ...(props.checkAll ? [h('label', { class: 'ua-check-row' }, all, '全选')] : []), ...rows);
|
||||||
|
return h('div', { class: 'ua-checkbox-group', role: 'group' }, body) as HTMLElement;
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ export { inputNumber } from './inputNumber.js';
|
|||||||
export { textarea } from './textarea.js';
|
export { textarea } from './textarea.js';
|
||||||
export { slider } from './slider.js';
|
export { slider } from './slider.js';
|
||||||
export { checkbox } from './checkbox.js';
|
export { checkbox } from './checkbox.js';
|
||||||
|
export { checkboxGroup } from './checkboxGroup.js';
|
||||||
export { switchButton } from './switch.js';
|
export { switchButton } from './switch.js';
|
||||||
export { select } from './select.js';
|
export { select } from './select.js';
|
||||||
export { menu } from './menu.js';
|
export { menu } from './menu.js';
|
||||||
|
|||||||
@@ -600,4 +600,23 @@ export interface WatermarkProps {
|
|||||||
color?: string; // 颜色(CSS 串或令牌名);缺省取 --ua-color-text-muted 的实色
|
color?: string; // 颜色(CSS 串或令牌名);缺省取 --ua-color-text-muted 的实色
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** 多选组选项 ***/
|
||||||
|
export interface CheckOption {
|
||||||
|
value: string; // 选项值
|
||||||
|
label: string; // 显示文案
|
||||||
|
disabled?: boolean; // 单项禁用:不可勾选,也不计入全选
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** 多选组入参 ***/
|
||||||
|
export interface CheckboxGroupProps {
|
||||||
|
options: CheckOption[]; // 选项集合
|
||||||
|
value?: Signal<string[]>; // 选中值数组信号;不传则内部维护
|
||||||
|
min?: number; // 最少选几项:到下限后已选项禁用,防止取消
|
||||||
|
max?: number; // 最多选几项:到上限后未选项禁用
|
||||||
|
checkAll?: boolean; // 是否显示全选行(含半选);缺省 false
|
||||||
|
direction?: 'horizontal' | 'vertical'; // 排列方向;缺省 horizontal
|
||||||
|
disabled?: boolean | Signal<boolean>; // 整组禁用
|
||||||
|
onChange?: (values: string[]) => void; // 变更回调(传副本)
|
||||||
|
}
|
||||||
|
|
||||||
export type { ColumnDef, RowKey }; // 再导出常用内核类型,方便调用方
|
export type { ColumnDef, RowKey }; // 再导出常用内核类型,方便调用方
|
||||||
|
|||||||
@@ -67,7 +67,8 @@
|
|||||||
"backTop(props):返回顶部悬浮按钮,越过阈值才浮现,外环 conic-gradient 显示已滚比例,点击平滑回顶",
|
"backTop(props):返回顶部悬浮按钮,越过阈值才浮现,外环 conic-gradient 显示已滚比例,点击平滑回顶",
|
||||||
"colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号",
|
"colorPicker(props):颜色选择,预设色板(缺省语义令牌色)+ 原生取色器 + 手输 CSS 颜色串,value 为颜色串信号",
|
||||||
"tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose",
|
"tour(props):引导漫游,返回 { el, start, close } 句柄;聚光框按 target 选择器定位,卡片自动选方位,Esc / 跳过走 onClose",
|
||||||
"watermark(props, ...children):内容容器 + 平铺水印覆盖层,canvas 生成单元、颜色取自令牌,覆盖层不挡交互"
|
"watermark(props, ...children):内容容器 + 平铺水印覆盖层,canvas 生成单元、颜色取自令牌,覆盖层不挡交互",
|
||||||
|
"checkboxGroup(props):多选组,value 为数组信号,min/max 用禁用边界项实现(不抛错),checkAll 支持半选且只统计未禁用项"
|
||||||
],
|
],
|
||||||
"keyTypes": [
|
"keyTypes": [
|
||||||
"ButtonProps",
|
"ButtonProps",
|
||||||
@@ -2332,6 +2333,61 @@
|
|||||||
"color 传 var(--ua-*) 时会先解析成计算值(canvas 不认 CSS 变量),所以暗色主题下颜色会自动跟随",
|
"color 传 var(--ua-*) 时会先解析成计算值(canvas 不认 CSS 变量),所以暗色主题下颜色会自动跟随",
|
||||||
"覆盖层为 pointer-events:none,下方按钮/输入框照常可点;watermark 根节点自带圆角与裁剪"
|
"覆盖层为 pointer-events:none,下方按钮/输入框照常可点;watermark 根节点自带圆角与裁剪"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "checkboxGroup",
|
||||||
|
"title": "多选组 CheckboxGroup",
|
||||||
|
"demo": "sec-checkgroup",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"n": "options",
|
||||||
|
"t": "CheckOption[]",
|
||||||
|
"r": 1,
|
||||||
|
"d": "选项集合:value / label / 可选 disabled"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "value",
|
||||||
|
"t": "Signal<string[]>",
|
||||||
|
"d": "选中值数组信号;不传则内部维护"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "min",
|
||||||
|
"t": "number",
|
||||||
|
"d": "最少选几项:到下限后已选项禁用,防止取消"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "max",
|
||||||
|
"t": "number",
|
||||||
|
"d": "最多选几项:到上限后未选项禁用"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "checkAll",
|
||||||
|
"t": "boolean",
|
||||||
|
"d": "是否显示全选行(含半选);缺省 false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "direction",
|
||||||
|
"t": "'horizontal' | 'vertical'",
|
||||||
|
"d": "排列方向;缺省 horizontal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "disabled",
|
||||||
|
"t": "boolean | Signal<boolean>",
|
||||||
|
"d": "整组禁用"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onChange",
|
||||||
|
"t": "(values: string[]) => void",
|
||||||
|
"d": "变更回调(传副本)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "checkboxGroup({ value: permPick, min: 1, max: 3, checkAll: true, options: [{ value: 'read', label: '读取' }, { value: 'write', label: '写入' }] })",
|
||||||
|
"steps": [
|
||||||
|
"options 顺序即渲染顺序;勾选后把整份数组写回 value 信号并触发 onChange",
|
||||||
|
"max 生效时未选项自动禁用,min 生效时已选项自动禁用——用禁用表达边界,不抛错也不静默回滚",
|
||||||
|
"checkAll 行的选中/半选只统计未禁用项,禁用项不计入基数",
|
||||||
|
"direction 传 vertical 改为纵向排列;disabled 支持信号,整组响应式禁用"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3239,3 +3239,14 @@ button.ua-step-dot:hover {
|
|||||||
background-repeat: repeat;
|
background-repeat: repeat;
|
||||||
background-position: 0 0;
|
background-position: 0 0;
|
||||||
}
|
}
|
||||||
|
.ua-check-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--ua-space-2);
|
||||||
|
}
|
||||||
|
.ua-check-group-vertical {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--ua-space-1);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user