iter(g305): 新增瀑布流布局 masonry
This commit is contained in:
@@ -3131,6 +3131,15 @@
|
|||||||
"motivation": "sortableList 是句柄式组件(返回 { el, move, order }),与 form / virtualList 同类,但 18 条配方里没有一条演示句柄式拼装——Agent 照配方抄会把返回值直接当节点 append,页面一片空白;「顺序只存一份」「move 与拖拽同一条提交路径」这些约定也没人写下来。",
|
"motivation": "sortableList 是句柄式组件(返回 { el, move, order }),与 form / virtualList 同类,但 18 条配方里没有一条演示句柄式拼装——Agent 照配方抄会把返回值直接当节点 append,页面一片空白;「顺序只存一份」「move 与拖拽同一条提交路径」这些约定也没人写下来。",
|
||||||
"acceptance": "① recipes.md 新增配方 19(编号连续、带代码块与要点),代码块里认出 sortableList / card / button / toast;② 要点覆盖句柄要 append(.el)、顺序唯一真相、move 与拖拽同路径、边缘不自动滚动、条目文案唯一;③ ua recipes 19 能取到该条,llms-full.txt 与 llms.txt 重生成;④ npm run verify / check 全绿。",
|
"acceptance": "① recipes.md 新增配方 19(编号连续、带代码块与要点),代码块里认出 sortableList / card / button / toast;② 要点覆盖句柄要 append(.el)、顺序唯一真相、move 与拖拽同路径、边缘不自动滚动、条目文案唯一;③ ua recipes 19 能取到该条,llms-full.txt 与 llms.txt 重生成;④ npm run verify / check 全绿。",
|
||||||
"notes": "recipes.md 新增配方 19:sortableList + card + button + toast 的可排序检查单,要点写清句柄要 append(.el)、顺序唯一真相、move 与拖拽同路径、边缘不自动滚动、文案唯一;ua recipes 19 可取出,llms.txt / llms-full.txt 重生成"
|
"notes": "recipes.md 新增配方 19:sortableList + card + button + toast 的可排序检查单,要点写清句柄要 append(.el)、顺序唯一真相、move 与拖拽同路径、边缘不自动滚动、文案唯一;ua recipes 19 可取出,llms.txt / llms-full.txt 重生成"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "g305",
|
||||||
|
"domain": "new",
|
||||||
|
"status": "open",
|
||||||
|
"title": "新增瀑布流布局 masonry(89 个组件)",
|
||||||
|
"motivation": "库里有 grid(等宽等高的规则栅格)与 space(线性间距),但图片墙、评论流、看板这类「列高不等」的布局没有落点,业务侧只能自己写 column-width 与 break-inside,还要记住多列容器不能是 flex/grid 这个坑。",
|
||||||
|
"acceptance": "① masonry({ items, columns?, minColumnWidth?, gap? }) 走 CSS 多列:不传 columns 按 minColumnWidth 自适应,传了即固定列数;② 每项包 .ua-masonry-item 并 break-inside: avoid,卡片不在列边界拆断;③ gap 同时管列间距与行间距;④ 样式只消费 --ua-* 令牌,禁止新增硬编码阴影/时长/字号;⑤ manifest/docs/README/demo/llms-full 同步,组件 88→89,npm run check 全绿。",
|
||||||
|
"notes": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "ui-arch-registry-index/0.1",
|
"$schema": "ui-arch-registry-index/0.1",
|
||||||
"generatedAt": "2026-10-26T16:31:56.843Z",
|
"generatedAt": "2026-10-26T16:34:08.281Z",
|
||||||
"repoRoot": ".",
|
"repoRoot": ".",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
@@ -231,7 +231,8 @@
|
|||||||
"heatmap(props):日历热力图,按周列 × 星期行铺格子、四档浓度表示当天数量,悬停看明细",
|
"heatmap(props):日历热力图,按周列 × 星期行铺格子、四档浓度表示当天数量,悬停看明细",
|
||||||
"progressRing(props):环形进度,SVG 双圆靠 dasharray 画弧 + 中心百分比,value 可传信号自动重绘",
|
"progressRing(props):环形进度,SVG 双圆靠 dasharray 画弧 + 中心百分比,value 可传信号自动重绘",
|
||||||
"noticeBar(props):单行通知条,语义色 + 左侧符号 + 可选跑马灯与关闭,适合页顶常驻播报",
|
"noticeBar(props):单行通知条,语义色 + 左侧符号 + 可选跑马灯与关闭,适合页顶常驻播报",
|
||||||
"sortableList(props):拖拽排序列表,整行可拖 + Alt+↑↓ 键盘搬位,返回 { el, move, order } 句柄"
|
"sortableList(props):拖拽排序列表,整行可拖 + Alt+↑↓ 键盘搬位,返回 { el, move, order } 句柄",
|
||||||
|
"masonry(props):瀑布流多列布局,列宽自适应或固定列数,卡片不被拆到两列"
|
||||||
],
|
],
|
||||||
"keyTypes": [
|
"keyTypes": [
|
||||||
"ButtonProps",
|
"ButtonProps",
|
||||||
@@ -4696,6 +4697,45 @@
|
|||||||
"外部要按代码调整顺序用句柄 move(from, to),下标越界或禁用时静默忽略;order() 以 DOM 为唯一真相回读当前顺序",
|
"外部要按代码调整顺序用句柄 move(from, to),下标越界或禁用时静默忽略;order() 以 DOM 为唯一真相回读当前顺序",
|
||||||
"什么时候别用:条目超过一屏(拖到边缘不会自动滚动,改用筛选或上下按钮)、需要跨列表搬运(用两个实例并自己管状态)"
|
"什么时候别用:条目超过一屏(拖到边缘不会自动滚动,改用筛选或上下按钮)、需要跨列表搬运(用两个实例并自己管状态)"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "masonry",
|
||||||
|
"title": "瀑布流 Masonry",
|
||||||
|
"demo": "sec-masonry",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"n": "items",
|
||||||
|
"t": "unknown[]",
|
||||||
|
"r": 1,
|
||||||
|
"d": "每个卡片内容(字符串 / DOM / 响应式值);数组顺序即铺放顺序,列高不等由内容自己决定"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "columns",
|
||||||
|
"t": "number",
|
||||||
|
"d": "固定列数;传了即不随容器宽度变化,与 minColumnWidth 互斥"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "minColumnWidth",
|
||||||
|
"t": "number",
|
||||||
|
"d": "自适应列宽下限(像素);缺省 240,容器越宽列数越多",
|
||||||
|
"df": "240"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "gap",
|
||||||
|
"t": "string",
|
||||||
|
"d": "列间距与行间距(CSS 长度或令牌值);缺省 var(--ua-space-3)",
|
||||||
|
"df": "var(--ua-space-3)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "masonry({ minColumnWidth: 220, items: shots.map((s) => card({ title: s.title }, image({ src: s.url }))) })",
|
||||||
|
"steps": [
|
||||||
|
"items 里直接放 card / image / 任意 DOM:每项由组件包一层 .ua-masonry-item,只有这一层带 break-inside: avoid,卡片才不会在列边界断成两截",
|
||||||
|
"不传 columns 就按 minColumnWidth 自适应:容器变宽自动加列、窄屏自动降到一列,不需要自己监听 window.resize",
|
||||||
|
"要固定几列(比如大屏看板恒 4 列)就传 columns,此时 minColumnWidth 不再生效——两者互斥,别同时传指望它俩协商",
|
||||||
|
"间距传一次就够:gap 同时给 column-gap 与每项的 margin-bottom,列间距和行间距天然一致;改间距只改这一处",
|
||||||
|
"列高不等才是瀑布流的价值所在(图片墙、评论流、看板):等高内容请用 grid,瀑布流会把等高卡片铺成奇怪的错位",
|
||||||
|
"什么时候别用:需要严格的行对齐(表格化数据)、需要按列独立滚动(用 splitter / 多个 virtualList),以及首屏内容需要按阅读顺序严格横向(多列布局是竖向填充,读屏顺序与视觉列序一致但不等同于行序)"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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):88 个 DOM 组件(表单 / 反馈 / 浮层 / 导航 / 表格),样式只消费令牌
|
- `packages/skin/ui`(@ui-arch/ui):89 个 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
@@ -39,7 +39,7 @@ ui-arch-mvp/
|
|||||||
│ │ ├── table-core/ # 表格状态内核
|
│ │ ├── table-core/ # 表格状态内核
|
||||||
│ │ └── frame/ # 信号 + hyperscript 微框架
|
│ │ └── frame/ # 信号 + hyperscript 微框架
|
||||||
│ ├── skin/ # 皮肤层(DOM 组件,可发布)
|
│ ├── skin/ # 皮肤层(DOM 组件,可发布)
|
||||||
│ │ └── ui/ # 88 个组件 + ui.css
|
│ │ └── ui/ # 89 个组件 + ui.css
|
||||||
│ ├── design/ # 资产层(可发布)
|
│ ├── design/ # 资产层(可发布)
|
||||||
│ │ └── tokens/ # --ua-* 设计令牌
|
│ │ └── tokens/ # --ua-* 设计令牌
|
||||||
│ └── tools/ # 工程层(private,不发布)
|
│ └── tools/ # 工程层(private,不发布)
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ npm run docs # 生成人类文档站 apps/docs/index.html + Agent 全
|
|||||||
npm run changelog # 依据 git 提交史重写 CHANGELOG.md(发版前跑;changelog:check 可查漂移)
|
npm run changelog # 依据 git 提交史重写 CHANGELOG.md(发版前跑;changelog:check 可查漂移)
|
||||||
```
|
```
|
||||||
|
|
||||||
## 组件(共 88 个)
|
## 组件(共 89 个)
|
||||||
|
|
||||||
<!-- ua:components -->
|
<!-- ua:components -->
|
||||||
accordion · affix · alert · anchor · autoComplete · avatar · avatarGroup · backTop · badge · breadcrumb · button · calendar · carousel · card · cascader · checkbox · checkboxGroup · codeBlock · colorPicker · commandPalette · copyButton · countdown · dataTable · datePicker · descriptions · diffView · divider · drawer · empty · floatButton · form · grid · heatmap · highlight · image · inputNumber · inputTag · jsonViewer · kbd · layout · link · list · loading · menu · modal · mentions · multiSelect · noticeBar · notification · pagination · pinInput · popover · popconfirm · progress · progressRing · radioGroup · rate · relativeTime · result · segmented · select · signaturePad · skeleton · slider · sortableList · space · sparkline · spinner · splitter · statistic · stepper · switchButton · tabs · tag · textField · textarea · timeline · timePicker · toast · tooltip · tour · transfer · tree · treeSelect · typography · upload · virtualList · watermark
|
accordion · affix · alert · anchor · autoComplete · avatar · avatarGroup · backTop · badge · breadcrumb · button · calendar · carousel · card · cascader · checkbox · checkboxGroup · codeBlock · colorPicker · commandPalette · copyButton · countdown · dataTable · datePicker · descriptions · diffView · divider · drawer · empty · floatButton · form · grid · heatmap · highlight · image · inputNumber · inputTag · jsonViewer · kbd · layout · link · list · loading · masonry · menu · modal · mentions · multiSelect · noticeBar · notification · pagination · pinInput · popover · popconfirm · progress · progressRing · radioGroup · rate · relativeTime · result · segmented · select · signaturePad · skeleton · slider · sortableList · space · sparkline · spinner · splitter · statistic · stepper · switchButton · tabs · tag · textField · textarea · timeline · timePicker · toast · tooltip · tour · transfer · tree · treeSelect · typography · upload · virtualList · watermark
|
||||||
<!-- /ua:components -->
|
<!-- /ua:components -->
|
||||||
|
|
||||||
分组:表单(button / copyButton / signaturePad / textField / inputNumber / inputTag / textarea / checkbox / checkboxGroup / colorPicker / switchButton / select / mentions / multiSelect / radioGroup / slider / rate / autoComplete / datePicker / timePicker / pinInput / segmented / upload / form + formItem / transfer / cascader)、反馈(alert / loading / toast / notification / noticeBar / result / spinner / progress / progressRing / skeleton / badge / empty / statistic / countdown)、浮层(tooltip / popover / popconfirm / modal / drawer / menu / commandPalette)、导航(tabs / breadcrumb / pagination / accordion / stepper / tree / treeSelect / anchor)、数据展示(dataTable / jsonViewer / diffView / codeBlock / relativeTime / sparkline / heatmap / descriptions / card / list / sortableList / kbd / highlight / timeline / tag / avatar / avatarGroup / image / carousel / calendar / typography / virtualList)、其他(floatButton / grid / layout / link / space / divider / splitter / backTop / affix / tour / watermark)。
|
分组:表单(button / copyButton / signaturePad / textField / inputNumber / inputTag / textarea / checkbox / checkboxGroup / colorPicker / switchButton / select / mentions / multiSelect / radioGroup / slider / rate / autoComplete / datePicker / timePicker / pinInput / segmented / upload / form + formItem / transfer / cascader)、反馈(alert / loading / toast / notification / noticeBar / result / spinner / progress / progressRing / skeleton / badge / empty / statistic / countdown)、浮层(tooltip / popover / popconfirm / modal / drawer / menu / commandPalette)、导航(tabs / breadcrumb / pagination / accordion / stepper / tree / treeSelect / anchor)、数据展示(dataTable / jsonViewer / diffView / codeBlock / relativeTime / sparkline / heatmap / descriptions / card / list / sortableList / kbd / highlight / timeline / tag / avatar / avatarGroup / image / carousel / calendar / typography / virtualList)、其他(floatButton / grid / layout / link / space / divider / splitter / backTop / affix / tour / masonry / watermark)。
|
||||||
|
|
||||||
## 可访问性约定
|
## 可访问性约定
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ accordion · affix · alert · anchor · autoComplete · avatar · avatarGroup
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `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:88 个 DOM 组件 + ui.css(只消费 `--ua-*` 令牌) |
|
| `packages/skin/ui` | @ui-arch/ui:89 个 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 打包成单文件) |
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ import {
|
|||||||
upload,
|
upload,
|
||||||
watermark,
|
watermark,
|
||||||
menu,
|
menu,
|
||||||
|
masonry,
|
||||||
modal,
|
modal,
|
||||||
mentions,
|
mentions,
|
||||||
multiSelect,
|
multiSelect,
|
||||||
@@ -1930,6 +1931,21 @@ content.append(
|
|||||||
h('p', { class: 'desc' }, () => `当前顺序:${sortableOrder().join(' → ')}`),
|
h('p', { class: 'desc' }, () => `当前顺序:${sortableOrder().join(' → ')}`),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
section(
|
||||||
|
'sec-masonry',
|
||||||
|
'瀑布流',
|
||||||
|
'masonry:CSS 多列布局,列宽按 minColumnWidth 自适应(窄屏自动降列)、也能固定列数;每项包一层才不被列边界拆断,间距一处传值同时管列间距与行间距。',
|
||||||
|
masonry({
|
||||||
|
minColumnWidth: 200,
|
||||||
|
items: [
|
||||||
|
card({ title: '图片墙 ①' }, h('p', { class: 'desc' }, '列高不同才是瀑布流的价值:内容自己决定卡片高度,浏览器负责找最短列。')),
|
||||||
|
card({ title: '图片墙 ②' }, h('p', { class: 'desc' }, '短卡片。')),
|
||||||
|
card({ title: '图片墙 ③' }, h('p', { class: 'desc' }, '这一张内容偏长,用来制造高度差:文字越多卡片越高,多列布局才会错落起来,而不是排成整齐的行。')),
|
||||||
|
card({ title: '图片墙 ④' }, h('p', { class: 'desc' }, '中等长度的一段说明,两行左右。')),
|
||||||
|
card({ title: '图片墙 ⑤' }, h('p', { class: 'desc' }, '再一条短的。')),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 悬浮按钮:fixed 定位,挂 body 不参与页面布局
|
// 悬浮按钮:fixed 定位,挂 body 不参与页面布局
|
||||||
|
|||||||
+28
-1
@@ -1,6 +1,6 @@
|
|||||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||||
|
|
||||||
> 自动生成于 2026-10-26T16:31:56.843Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
> 自动生成于 2026-10-26T16:34:08.281Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
@@ -2524,6 +2524,32 @@ container.append(sort.el);
|
|||||||
|
|
||||||
现场演示:apps/docs/demo.html#sec-sortable
|
现场演示:apps/docs/demo.html#sec-sortable
|
||||||
|
|
||||||
|
### masonry — 瀑布流 Masonry
|
||||||
|
|
||||||
|
参数:
|
||||||
|
|
||||||
|
- items(必填):unknown[] —— 每个卡片内容(字符串 / DOM / 响应式值);数组顺序即铺放顺序,列高不等由内容自己决定
|
||||||
|
- columns:number —— 固定列数;传了即不随容器宽度变化,与 minColumnWidth 互斥
|
||||||
|
- minColumnWidth(默认 240):number —— 自适应列宽下限(像素);缺省 240,容器越宽列数越多
|
||||||
|
- gap(默认 var(--ua-space-3)):string —— 列间距与行间距(CSS 长度或令牌值);缺省 var(--ua-space-3)
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```
|
||||||
|
masonry({ minColumnWidth: 220, items: shots.map((s) => card({ title: s.title }, image({ src: s.url }))) })
|
||||||
|
```
|
||||||
|
|
||||||
|
手把手:
|
||||||
|
|
||||||
|
1. items 里直接放 card / image / 任意 DOM:每项由组件包一层 .ua-masonry-item,只有这一层带 break-inside: avoid,卡片才不会在列边界断成两截
|
||||||
|
2. 不传 columns 就按 minColumnWidth 自适应:容器变宽自动加列、窄屏自动降到一列,不需要自己监听 window.resize
|
||||||
|
3. 要固定几列(比如大屏看板恒 4 列)就传 columns,此时 minColumnWidth 不再生效——两者互斥,别同时传指望它俩协商
|
||||||
|
4. 间距传一次就够:gap 同时给 column-gap 与每项的 margin-bottom,列间距和行间距天然一致;改间距只改这一处
|
||||||
|
5. 列高不等才是瀑布流的价值所在(图片墙、评论流、看板):等高内容请用 grid,瀑布流会把等高卡片铺成奇怪的错位
|
||||||
|
6. 什么时候别用:需要严格的行对齐(表格化数据)、需要按列独立滚动(用 splitter / 多个 virtualList),以及首屏内容需要按阅读顺序严格横向(多列布局是竖向填充,读屏顺序与视觉列序一致但不等同于行序)
|
||||||
|
|
||||||
|
现场演示:apps/docs/demo.html#sec-masonry
|
||||||
|
|
||||||
API 速览:
|
API 速览:
|
||||||
|
|
||||||
- button(props):按钮,variant 三档视觉强度,disabled / loading 支持布尔或信号动态同步(loading 转圈并禁点),onClick 点击回调
|
- button(props):按钮,variant 三档视觉强度,disabled / loading 支持布尔或信号动态同步(loading 转圈并禁点),onClick 点击回调
|
||||||
@@ -2616,6 +2642,7 @@ API 速览:
|
|||||||
- progressRing(props):环形进度,SVG 双圆靠 dasharray 画弧 + 中心百分比,value 可传信号自动重绘
|
- progressRing(props):环形进度,SVG 双圆靠 dasharray 画弧 + 中心百分比,value 可传信号自动重绘
|
||||||
- noticeBar(props):单行通知条,语义色 + 左侧符号 + 可选跑马灯与关闭,适合页顶常驻播报
|
- noticeBar(props):单行通知条,语义色 + 左侧符号 + 可选跑马灯与关闭,适合页顶常驻播报
|
||||||
- sortableList(props):拖拽排序列表,整行可拖 + Alt+↑↓ 键盘搬位,返回 { el, move, order } 句柄
|
- sortableList(props):拖拽排序列表,整行可拖 + Alt+↑↓ 键盘搬位,返回 { el, move, order } 句柄
|
||||||
|
- masonry(props):瀑布流多列布局,列宽自适应或固定列数,卡片不被拆到两列
|
||||||
|
|
||||||
一行用法:
|
一行用法:
|
||||||
|
|
||||||
|
|||||||
@@ -137,6 +137,7 @@
|
|||||||
- progressRing(props):环形进度,SVG 双圆靠 dasharray 画弧 + 中心百分比,value 可传信号自动重绘
|
- progressRing(props):环形进度,SVG 双圆靠 dasharray 画弧 + 中心百分比,value 可传信号自动重绘
|
||||||
- noticeBar(props):单行通知条,语义色 + 左侧符号 + 可选跑马灯与关闭,适合页顶常驻播报
|
- noticeBar(props):单行通知条,语义色 + 左侧符号 + 可选跑马灯与关闭,适合页顶常驻播报
|
||||||
- sortableList(props):拖拽排序列表,整行可拖 + Alt+↑↓ 键盘搬位,返回 { el, move, order } 句柄
|
- sortableList(props):拖拽排序列表,整行可拖 + Alt+↑↓ 键盘搬位,返回 { el, move, order } 句柄
|
||||||
|
- masonry(props):瀑布流多列布局,列宽自适应或固定列数,卡片不被拆到两列
|
||||||
- 用法: `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 为回调
|
||||||
|
|||||||
@@ -88,3 +88,4 @@ export { heatmap } from './heatmap.js';
|
|||||||
export { progressRing } from './progressRing.js';
|
export { progressRing } from './progressRing.js';
|
||||||
export { noticeBar } from './noticeBar.js';
|
export { noticeBar } from './noticeBar.js';
|
||||||
export { sortableList } from './sortableList.js';
|
export { sortableList } from './sortableList.js';
|
||||||
|
export { masonry } from './masonry.js';
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { h } from '@ui-arch/frame';
|
||||||
|
import type { MasonryProps } from './types.js';
|
||||||
|
|
||||||
|
/*** 瀑布流:CSS 多列布局,列数按容器宽度自适应或显式指定;每项单独包一层才能 break-inside 不拆断 ***/
|
||||||
|
export function masonry(props: MasonryProps): HTMLElement {
|
||||||
|
// —— 状态集中声明 ——
|
||||||
|
const adaptive = !props.columns; // 未指定列数即自适应(column-width 让浏览器自己算列数)
|
||||||
|
const width = props.minColumnWidth ?? 240; // 自适应时的最小列宽(像素)
|
||||||
|
const gap = props.gap ?? 'var(--ua-space-3)'; // 列间距与行间距同源,避免两处各写一份
|
||||||
|
const root = h('div', { class: 'ua-masonry' }) as HTMLElement;
|
||||||
|
root.style.columnGap = gap;
|
||||||
|
if (adaptive) root.style.columnWidth = `${width}px`; // 自适应:容器越宽列数越多,窄屏自动降到一列
|
||||||
|
else root.style.columnCount = String(props.columns); // 显式列数:与 column-width 互斥,固定几列就几列
|
||||||
|
|
||||||
|
props.items.forEach((item) => {
|
||||||
|
const cell = h('div', { class: 'ua-masonry-item' }, item);
|
||||||
|
cell.style.marginBottom = gap; // 行间距与列间距同值,视觉上才是均匀网格
|
||||||
|
root.append(cell);
|
||||||
|
});
|
||||||
|
return root;
|
||||||
|
}
|
||||||
@@ -1146,3 +1146,11 @@ export interface SortableListHandle {
|
|||||||
move: (from: number, to: number) => void; // 程序化搬位:下标越界或禁用时静默忽略
|
move: (from: number, to: number) => void; // 程序化搬位:下标越界或禁用时静默忽略
|
||||||
order: () => string[]; // 读取当前顺序(以 DOM 为唯一真相)
|
order: () => string[]; // 读取当前顺序(以 DOM 为唯一真相)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** 瀑布流入参 ***/
|
||||||
|
export interface MasonryProps {
|
||||||
|
items: unknown[]; // 每个卡片内容(字符串 / DOM / 响应式值),按数组顺序铺进最短列
|
||||||
|
columns?: number; // 固定列数;传了即不随容器宽度变化,与 minColumnWidth 互斥
|
||||||
|
minColumnWidth?: number; // 自适应列宽下限(像素);缺省 240,容器越宽列数越多
|
||||||
|
gap?: string; // 列间距与行间距(CSS 长度或令牌值);缺省 var(--ua-space-3)
|
||||||
|
}
|
||||||
|
|||||||
@@ -102,7 +102,8 @@
|
|||||||
"heatmap(props):日历热力图,按周列 × 星期行铺格子、四档浓度表示当天数量,悬停看明细",
|
"heatmap(props):日历热力图,按周列 × 星期行铺格子、四档浓度表示当天数量,悬停看明细",
|
||||||
"progressRing(props):环形进度,SVG 双圆靠 dasharray 画弧 + 中心百分比,value 可传信号自动重绘",
|
"progressRing(props):环形进度,SVG 双圆靠 dasharray 画弧 + 中心百分比,value 可传信号自动重绘",
|
||||||
"noticeBar(props):单行通知条,语义色 + 左侧符号 + 可选跑马灯与关闭,适合页顶常驻播报",
|
"noticeBar(props):单行通知条,语义色 + 左侧符号 + 可选跑马灯与关闭,适合页顶常驻播报",
|
||||||
"sortableList(props):拖拽排序列表,整行可拖 + Alt+↑↓ 键盘搬位,返回 { el, move, order } 句柄"
|
"sortableList(props):拖拽排序列表,整行可拖 + Alt+↑↓ 键盘搬位,返回 { el, move, order } 句柄",
|
||||||
|
"masonry(props):瀑布流多列布局,列宽自适应或固定列数,卡片不被拆到两列"
|
||||||
],
|
],
|
||||||
"keyTypes": [
|
"keyTypes": [
|
||||||
"ButtonProps",
|
"ButtonProps",
|
||||||
@@ -4567,6 +4568,45 @@
|
|||||||
"外部要按代码调整顺序用句柄 move(from, to),下标越界或禁用时静默忽略;order() 以 DOM 为唯一真相回读当前顺序",
|
"外部要按代码调整顺序用句柄 move(from, to),下标越界或禁用时静默忽略;order() 以 DOM 为唯一真相回读当前顺序",
|
||||||
"什么时候别用:条目超过一屏(拖到边缘不会自动滚动,改用筛选或上下按钮)、需要跨列表搬运(用两个实例并自己管状态)"
|
"什么时候别用:条目超过一屏(拖到边缘不会自动滚动,改用筛选或上下按钮)、需要跨列表搬运(用两个实例并自己管状态)"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "masonry",
|
||||||
|
"title": "瀑布流 Masonry",
|
||||||
|
"demo": "sec-masonry",
|
||||||
|
"props": [
|
||||||
|
{
|
||||||
|
"n": "items",
|
||||||
|
"t": "unknown[]",
|
||||||
|
"r": 1,
|
||||||
|
"d": "每个卡片内容(字符串 / DOM / 响应式值);数组顺序即铺放顺序,列高不等由内容自己决定"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "columns",
|
||||||
|
"t": "number",
|
||||||
|
"d": "固定列数;传了即不随容器宽度变化,与 minColumnWidth 互斥"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "minColumnWidth",
|
||||||
|
"t": "number",
|
||||||
|
"d": "自适应列宽下限(像素);缺省 240,容器越宽列数越多",
|
||||||
|
"df": "240"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "gap",
|
||||||
|
"t": "string",
|
||||||
|
"d": "列间距与行间距(CSS 长度或令牌值);缺省 var(--ua-space-3)",
|
||||||
|
"df": "var(--ua-space-3)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"example": "masonry({ minColumnWidth: 220, items: shots.map((s) => card({ title: s.title }, image({ src: s.url }))) })",
|
||||||
|
"steps": [
|
||||||
|
"items 里直接放 card / image / 任意 DOM:每项由组件包一层 .ua-masonry-item,只有这一层带 break-inside: avoid,卡片才不会在列边界断成两截",
|
||||||
|
"不传 columns 就按 minColumnWidth 自适应:容器变宽自动加列、窄屏自动降到一列,不需要自己监听 window.resize",
|
||||||
|
"要固定几列(比如大屏看板恒 4 列)就传 columns,此时 minColumnWidth 不再生效——两者互斥,别同时传指望它俩协商",
|
||||||
|
"间距传一次就够:gap 同时给 column-gap 与每项的 margin-bottom,列间距和行间距天然一致;改间距只改这一处",
|
||||||
|
"列高不等才是瀑布流的价值所在(图片墙、评论流、看板):等高内容请用 grid,瀑布流会把等高卡片铺成奇怪的错位",
|
||||||
|
"什么时候别用:需要严格的行对齐(表格化数据)、需要按列独立滚动(用 splitter / 多个 virtualList),以及首屏内容需要按阅读顺序严格横向(多列布局是竖向填充,读屏顺序与视觉列序一致但不等同于行序)"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6089,3 +6089,12 @@ h5.ua-type-title { font-size: var(--ua-font-size-md); }
|
|||||||
transition-property: color, background-color, border-color, box-shadow, opacity, fill, stroke; /* 只保留不产生空间位移的属性 */
|
transition-property: color, background-color, border-color, box-shadow, opacity, fill, stroke; /* 只保留不产生空间位移的属性 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* —— 瀑布流:多列布局,列宽自适应或固定列数 —— */
|
||||||
|
.ua-masonry {
|
||||||
|
display: block; /* 多列容器不能是 flex/grid,否则 column-width 失效 */
|
||||||
|
}
|
||||||
|
.ua-masonry-item {
|
||||||
|
break-inside: avoid; /* 卡片不被拆到两列:图片墙里断半截最刺眼 */
|
||||||
|
page-break-inside: avoid; /* 打印时同样不拆断 */
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user