@manohub/ui 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +141 -0
- package/dist/components/avatar/index.d.ts +283 -0
- package/dist/components/avatar/index.js +167 -0
- package/dist/components/badge/index.d.ts +132 -0
- package/dist/components/badge/index.js +99 -0
- package/dist/components/button/index.d.ts +578 -0
- package/dist/components/button/index.js +191 -0
- package/dist/components/capsule/index.d.ts +161 -0
- package/dist/components/capsule/index.js +101 -0
- package/dist/components/card/index.d.ts +456 -0
- package/dist/components/card/index.js +231 -0
- package/dist/components/checkbox/index.d.ts +331 -0
- package/dist/components/checkbox/index.js +165 -0
- package/dist/components/code/index.d.ts +134 -0
- package/dist/components/code/index.js +117 -0
- package/dist/components/code-editor/index.d.ts +163 -0
- package/dist/components/code-editor/index.js +98 -0
- package/dist/components/collapse/index.d.ts +125 -0
- package/dist/components/collapse/index.js +95 -0
- package/dist/components/control-size.d.ts +8 -0
- package/dist/components/dialog/index.d.ts +224 -0
- package/dist/components/dialog/index.js +194 -0
- package/dist/components/divider/index.d.ts +64 -0
- package/dist/components/divider/index.js +62 -0
- package/dist/components/drawer/index.d.ts +226 -0
- package/dist/components/drawer/index.js +190 -0
- package/dist/components/filter/index.d.ts +391 -0
- package/dist/components/filter/index.js +203 -0
- package/dist/components/form/index.d.ts +704 -0
- package/dist/components/form/index.js +329 -0
- package/dist/components/input/index.d.ts +980 -0
- package/dist/components/input/index.js +334 -0
- package/dist/components/layout/index.d.ts +162 -0
- package/dist/components/layout/index.js +100 -0
- package/dist/components/list-view/index.d.ts +563 -0
- package/dist/components/list-view/index.js +221 -0
- package/dist/components/loading/index.d.ts +77 -0
- package/dist/components/loading/index.js +40 -0
- package/dist/components/nav/index.d.ts +293 -0
- package/dist/components/nav/index.js +164 -0
- package/dist/components/notice/index.d.ts +137 -0
- package/dist/components/notice/index.js +101 -0
- package/dist/components/notification/index.d.ts +118 -0
- package/dist/components/notification/index.js +99 -0
- package/dist/components/page/index.d.ts +276 -0
- package/dist/components/page/index.js +351 -0
- package/dist/components/page/split-resize.d.ts +42 -0
- package/dist/components/page/split-resize.js +36 -0
- package/dist/components/pagination/index.d.ts +171 -0
- package/dist/components/pagination/index.js +226 -0
- package/dist/components/panel/index.d.ts +401 -0
- package/dist/components/panel/index.js +193 -0
- package/dist/components/part-padding.d.ts +27 -0
- package/dist/components/part-padding.js +23 -0
- package/dist/components/query-state/index.d.ts +207 -0
- package/dist/components/query-state/index.js +181 -0
- package/dist/components/radio/index.d.ts +473 -0
- package/dist/components/radio/index.js +250 -0
- package/dist/components/search/index.d.ts +167 -0
- package/dist/components/search/index.js +105 -0
- package/dist/components/select/index.d.ts +252 -0
- package/dist/components/select/index.js +281 -0
- package/dist/components/skeleton/index.d.ts +88 -0
- package/dist/components/skeleton/index.js +56 -0
- package/dist/components/steps/index.d.ts +199 -0
- package/dist/components/steps/index.js +117 -0
- package/dist/components/switch/index.d.ts +150 -0
- package/dist/components/switch/index.js +103 -0
- package/dist/components/table/index.d.ts +351 -0
- package/dist/components/table/index.js +286 -0
- package/dist/components/tabs/index.d.ts +179 -0
- package/dist/components/tabs/index.js +153 -0
- package/dist/components/tabset/index.d.ts +105 -0
- package/dist/components/tabset/index.js +68 -0
- package/dist/components/tag/index.d.ts +203 -0
- package/dist/components/tag/index.js +144 -0
- package/dist/components/toast/index.d.ts +271 -0
- package/dist/components/toast/index.js +116 -0
- package/dist/components/tooltip/index.d.ts +126 -0
- package/dist/components/tooltip/index.js +97 -0
- package/dist/components/tree/index.d.ts +317 -0
- package/dist/components/tree/index.js +251 -0
- package/dist/components/upload/index.d.ts +384 -0
- package/dist/components/upload/index.js +172 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +102 -0
- package/dist/services/base.d.ts +21 -0
- package/dist/services/base.js +42 -0
- package/dist/services/dialog.d.ts +41 -0
- package/dist/services/dialog.js +83 -0
- package/dist/services/index.d.ts +12 -0
- package/dist/services/loading.d.ts +21 -0
- package/dist/services/loading.js +41 -0
- package/dist/services/toast.d.ts +40 -0
- package/dist/services/toast.js +74 -0
- package/dist/styles/components/avatar.css +128 -0
- package/dist/styles/components/avatar.tokens.css +24 -0
- package/dist/styles/components/badge.css +237 -0
- package/dist/styles/components/button.css +454 -0
- package/dist/styles/components/button.tokens.css +56 -0
- package/dist/styles/components/capsule.css +120 -0
- package/dist/styles/components/capsule.tokens.css +27 -0
- package/dist/styles/components/card.css +192 -0
- package/dist/styles/components/checkbox.css +145 -0
- package/dist/styles/components/checkbox.tokens.css +26 -0
- package/dist/styles/components/code-editor.css +91 -0
- package/dist/styles/components/code-editor.tokens.css +24 -0
- package/dist/styles/components/code.css +153 -0
- package/dist/styles/components/code.tokens.css +29 -0
- package/dist/styles/components/collapse.css +62 -0
- package/dist/styles/components/collapse.tokens.css +20 -0
- package/dist/styles/components/dialog.css +106 -0
- package/dist/styles/components/dialog.tokens.css +24 -0
- package/dist/styles/components/divider.css +71 -0
- package/dist/styles/components/divider.tokens.css +21 -0
- package/dist/styles/components/drawer.css +143 -0
- package/dist/styles/components/drawer.tokens.css +22 -0
- package/dist/styles/components/filter.css +41 -0
- package/dist/styles/components/filter.tokens.css +14 -0
- package/dist/styles/components/form.css +220 -0
- package/dist/styles/components/form.tokens.css +15 -0
- package/dist/styles/components/input.css +445 -0
- package/dist/styles/components/input.tokens.css +41 -0
- package/dist/styles/components/layout.css +198 -0
- package/dist/styles/components/list-view.css +244 -0
- package/dist/styles/components/loading.css +76 -0
- package/dist/styles/components/loading.tokens.css +23 -0
- package/dist/styles/components/nav.css +48 -0
- package/dist/styles/components/notice.css +153 -0
- package/dist/styles/components/notice.tokens.css +17 -0
- package/dist/styles/components/notification.css +159 -0
- package/dist/styles/components/notification.tokens.css +37 -0
- package/dist/styles/components/page.css +473 -0
- package/dist/styles/components/pagination.css +297 -0
- package/dist/styles/components/pagination.tokens.css +32 -0
- package/dist/styles/components/panel.css +77 -0
- package/dist/styles/components/query-state.css +88 -0
- package/dist/styles/components/radio.css +271 -0
- package/dist/styles/components/radio.tokens.css +26 -0
- package/dist/styles/components/search.css +30 -0
- package/dist/styles/components/select.css +242 -0
- package/dist/styles/components/select.tokens.css +33 -0
- package/dist/styles/components/skeleton.css +60 -0
- package/dist/styles/components/skeleton.tokens.css +27 -0
- package/dist/styles/components/steps.css +157 -0
- package/dist/styles/components/steps.tokens.css +42 -0
- package/dist/styles/components/switch.css +165 -0
- package/dist/styles/components/switch.tokens.css +26 -0
- package/dist/styles/components/table.css +439 -0
- package/dist/styles/components/table.tokens.css +41 -0
- package/dist/styles/components/tabs.css +234 -0
- package/dist/styles/components/tabs.tokens.css +30 -0
- package/dist/styles/components/tag.css +200 -0
- package/dist/styles/components/tag.tokens.css +30 -0
- package/dist/styles/components/toast.css +150 -0
- package/dist/styles/components/toast.tokens.css +26 -0
- package/dist/styles/components/tooltip.css +89 -0
- package/dist/styles/components/tooltip.tokens.css +25 -0
- package/dist/styles/components/tree.css +139 -0
- package/dist/styles/components/tree.tokens.css +20 -0
- package/dist/styles/components/upload.css +150 -0
- package/dist/styles/components/upload.tokens.css +27 -0
- package/dist/styles/index.css +149 -0
- package/dist/styles/operations/collapse.css +84 -0
- package/dist/styles/operations/popup.css +189 -0
- package/dist/styles/operations/tabset.css +57 -0
- package/dist/utils/state-illustration.d.ts +31 -0
- package/dist/utils/state-illustration.js +156 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# @manohub/ui
|
|
2
|
+
|
|
3
|
+
AIHub **自绘组件层**:不依赖 farris 的手写 Vue TSX 组件 + 自带令牌层。
|
|
4
|
+
|
|
5
|
+
本包分三层,**可分别引用**:
|
|
6
|
+
|
|
7
|
+
| 层 | 入口 | 内容 |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| ① 主题 | `@manohub/theme/default.css`(兜底)/ `@manohub/theme/farris.css`(Farris 规范) | **全局**令牌(值)—— 随主题包独立发布,换主题只换这一行 |
|
|
10
|
+
| ② 组件面 | `@manohub/ui/styles.css` | 类与状态协议 + 组件令牌基础值,**无需 JS** 即可使用 |
|
|
11
|
+
| ③ Vue 层 | `@manohub/ui` | Vue 组件:渲染 ② 的类 + 行为增强 |
|
|
12
|
+
| ④ 服务层 | `@manohub/ui` | 命令式反馈:`toast()` / `confirm()` / `alert()` / `showLoading()`(宿主 + 队列 + 生命周期,共用 ③ 的组件) |
|
|
13
|
+
|
|
14
|
+
- **视觉与几何**:《Farris Web 基础视觉规范》(`@aihub/theme` 的令牌与组件实测值)
|
|
15
|
+
- **令牌命名**:daisyUI 式语义槽位(`--ui-primary` / `--ui-base-content` / `--ui-info*` / `--ui-warning*` …,`default ≠ primary`)
|
|
16
|
+
- **组件 API**:shadcn 通行词汇(变体维度 `tone` × `shape` × `size`、compound 点号子件、受控 prop + 语义事件)
|
|
17
|
+
- **实现**:零依赖手写 CSS,类名 `.mh-*`(bem-lite)
|
|
18
|
+
|
|
19
|
+
> **0.5.0(破坏性变更)**:`Section` 组件已退役 —— 它的「标题行 + 右端操作位(`extra`)」能力并入
|
|
20
|
+
> **`Form.Header`**(支持 `title` / `description` / `extra`,且成员归位到表单最前)。
|
|
21
|
+
> 旧写法 `Section` / `Form.Section` 统一改为 `Form.Header`;需要「区块分组」时用 `Panel` 或嵌套容器。
|
|
22
|
+
|
|
23
|
+
## 安装
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add @manohub/ui @manohub/theme
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 接入
|
|
30
|
+
|
|
31
|
+
样式两行,顺序固定(先主题后组件面;若应用的样式链已级联本包,则不必单独引):
|
|
32
|
+
|
|
33
|
+
```css
|
|
34
|
+
@import "@manohub/theme/default.css"; /* ① 全局令牌(值) */
|
|
35
|
+
@import "@manohub/ui/styles.css"; /* ② 组件面(类 + 组件令牌值) */
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
只写静态页(不装 Vue)时引这两行即可 —— 类名与 `data-*` 状态协议直接可用。
|
|
39
|
+
|
|
40
|
+
本包**可独立发布**:组件与类型一律从 `@manohub/ui` 直接引;下游(如 `@manohub/kit`)若转出口,是下游自己的选择。
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { Card, Form, Tree, Checkbox } from '@manohub/ui'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 组件清单
|
|
47
|
+
|
|
48
|
+
| 分组 | 组件 |
|
|
49
|
+
|---|---|
|
|
50
|
+
| 容器与分区 | `Page`(页面级骨架:`.Header` / `.Body` / `.Footer` / `.Filter` / `.Toolbar` / `.Split`)、`Panel`(+ `.Header` / `.Body` / `.Footer`)、`Card`(+ 三件)、`Nav`、`Layout`(`Layout.Row` 排「行」/ `Layout.Column` 排「列」:轴向 × 间距档 × 对齐) |
|
|
51
|
+
| 数据展示 | `Tree`、`ListView`(+ `.CardItem`)、`Badge`、`Tag`(常规 / 状态 / 可选 / 新增 / 筛选片) |
|
|
52
|
+
| 展示 | `Avatar`(+ `.Group`)、`Skeleton`(行 / 列表 / 块状) |
|
|
53
|
+
| 表单基础(**自绘**) | `Button`(+ `.Group` / `.Divider`;五型 / 小号 / 加载 / 菜单按钮 / 连体切换)、`Input`(+ `.Group` / `.Chip` / `.Display` / `.Textarea`;清除 / 可见性切换 / 校验)、`Search`(搜索框:值 / 搜索 / 清空三条回调)、`Select`(单选 / 多选 / 搜索 / 加载;面板用原生 popover)、`Switch`(表格档 / 表单档) |
|
|
54
|
+
| 表单与选择 | `Form`(+ `.Header` / `.Item`;标签体系、成员归位、`v-model` 值收集与命令式 `clear()`)、`Filter`(+ `.Item`;`Form` 的壳:条件字段写成子件、控件自放、查询/重置与快照归一)、`Checkbox`、`Radio`(+ `.Group` / `.Card`;卡片式单选)、`Capsule`(胶囊选择器;胶囊 / 方形两档外形,与 `Tabset` 组合即页签) |
|
|
55
|
+
| 反馈与三态 | `Notice`(条状提示)、`Notification`(通知卡:标题 + 头像 / 标签 / 操作 / 关闭位)、`QueryState`(三态容器;内建空 / 错误插画取自设计交付的四张 SVG,原样内联在 `src/utils/state-illustration.ts`)、`Loading`(转圈 + 遮罩:行内 / 区域 / 全屏三形态) |
|
|
56
|
+
| 反馈与引导(**自绘**) | `Toast`(消息提示:语义四档 / loading + 可选动作 + 自动消失计时 + `.Stack` 堆叠)、`Steps`(步骤条:节点式 / 燕尾段式 / `.Progress` 进度条) |
|
|
57
|
+
| 数据展示(**自绘**) | `Table`(列 × 数据 + 选择列 + 排序事件 + 固定表头 / 固定列 + 批量栏 / 合计行;排序与列宽拖拽算法归调用方)、`Pagination`(总数 / 每页条数 / 页码窗口 / 简单档 / 外框档)、`Tabs`(下划线页签 + 「更多」收纳) |
|
|
58
|
+
| 内容与文件 | `Code`(行内 chip / 代码块 + 语言角标 + 图标复制位,**只读展示**)、`CodeEditor`(等宽多行可编辑代码区:按行自适应 / 限高内滚 / 只读、禁用)、`Upload`(+ `.Thumbs`;拖拽 / 点击上传区 + 已传文件列表) |
|
|
59
|
+
| 浮层(**自绘**) | `Dialog`(原生 `<dialog>` + 模态皮肤;关闭受控)、`Tooltip`(**零 JS**:显隐与定位全走操作层 hover 档) |
|
|
60
|
+
| 操作层(零 JS 原语) | `Collapse`(+ `.Stack`)、`Tabset`;浮层只提供样式层(`styles/operations/popup.css`,零 JS 可直接用),Vue 浮层仍用 farris 适配件 |
|
|
61
|
+
|
|
62
|
+
## 三态的操作按钮
|
|
63
|
+
|
|
64
|
+
`QueryState` / `Tree` / `ListView` 的空态·错误态操作按钮就是**本包的 `Button`**(`variant="primary"`),
|
|
65
|
+
与全站按钮同族 —— 应用**不需要**注入任何东西,只要把文案与回调给上:
|
|
66
|
+
|
|
67
|
+
```vue
|
|
68
|
+
<QueryState
|
|
69
|
+
:empty="list.length === 0"
|
|
70
|
+
empty-action-text="新建"
|
|
71
|
+
:on-empty-action="openCreate"
|
|
72
|
+
>
|
|
73
|
+
<MyList />
|
|
74
|
+
</QueryState>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- 要换外观:改主题令牌(`Button` 的外观档位是 `variant`);
|
|
78
|
+
- 要换结构(多个按钮 / 自定义动作区):用 `actions` 插槽自绘。
|
|
79
|
+
|
|
80
|
+
## 服务层(组件树之外调反馈)
|
|
81
|
+
|
|
82
|
+
组件是**受控**的(可见性归调用方);组件树之外(请求回调、路由守卫、工具函数里)要弹提示时用服务层:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { toast, confirm, alert, showLoading, configureHost } from '@manohub/ui'
|
|
86
|
+
|
|
87
|
+
toast('success', '保存成功') // 3 秒自动消失
|
|
88
|
+
toast('error', '导出失败', { action: { text: '重试', onClick: retry } })
|
|
89
|
+
const pending = toast('loading', '正在导入…', { duration: 0 }) // 常驻,完成后自己收
|
|
90
|
+
pending.close()
|
|
91
|
+
|
|
92
|
+
if (await confirm({ title: '删除确认', detail: '删除后不可恢复', okDanger: true })) await remove()
|
|
93
|
+
await alert({ title: '导入完成', detail: '共 128 条' })
|
|
94
|
+
|
|
95
|
+
const done = showLoading('保存中…') // 引用计数:并发调用要各关各的
|
|
96
|
+
try { await save() } finally { done() }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**四件必须知道的事**:
|
|
100
|
+
|
|
101
|
+
1. **服务层不新造浮层**:`toast()` 渲染的是本包的 `Toast`、`confirm()` 渲染的是本包的 `Dialog`、
|
|
102
|
+
`showLoading()` 渲染的是本包的 `Loading` —— 外观改一处两处一起变。
|
|
103
|
+
2. **宿主即渲染位置**:解析顺序是「**显式 `configureHost(el)`** → 带 `[data-manohub-ui]` 作用域锚的容器
|
|
104
|
+
→ `document.body`」。微前端下**必须落回本应用自己的容器**:本包全部令牌(全局值 + 组件值)都锚在
|
|
105
|
+
`[data-manohub-ui]` 上,落在容器外的浮层会「样式全丢」(变量解析为空 +
|
|
106
|
+
应用侧样式被 `scopecss` 前缀化后匹配不到)。骨架层只需在包裹节点上补一个
|
|
107
|
+
`data-manohub-ui` 属性,服务层不必反向依赖它。
|
|
108
|
+
注:`.app-container` 是 `@manohub/kit` 的输出类名(内部命名),**不是**跨包契约,服务层不认它。
|
|
109
|
+
3. **按需挂载、自动收尾**:首次调用才建宿主与 Vue 实例(模块顶层零 DOM 操作);
|
|
110
|
+
`toast` 队列空了、`loading` 计数归零即收掉宿主 —— 反复重挂不留悬挂实例。
|
|
111
|
+
4. **`loading` 是引用计数语义**:并发的两次「显示」只关一次时遮罩仍在(先结束的那个不会把
|
|
112
|
+
别人的遮罩一起收掉)。推荐用返回的句柄在 `finally` 里关掉自己那一次。
|
|
113
|
+
|
|
114
|
+
## 主题层
|
|
115
|
+
|
|
116
|
+
全局令牌(`--ui-primary` / `--ui-space-*` / `--ui-font-*` 这类跨件共用的值)**不在本包**,已拆成独立包
|
|
117
|
+
**`@manohub/theme`**(值层与面层分开:值随主题变、面不随主题变)。每个主题分五片:
|
|
118
|
+
`colors` / `typography` / `spacing` / `radius` / `shadows`,一律锚定 **`[data-manohub-ui]`**
|
|
119
|
+
(库品牌作用域锚;micro-app 的 scopecss 不作用域化 `:root`,定义在 `:root` 会泄漏到宿主)。
|
|
120
|
+
|
|
121
|
+
只服务单一组件的令牌分两半落在本包:
|
|
122
|
+
|
|
123
|
+
| 位置 | 内容 | 锚 |
|
|
124
|
+
|---|---|---|
|
|
125
|
+
| `styles/components/<件>.tokens.css` | **基础值**(唯一的、两套主题同值的那一条) | `[data-manohub-ui]` |
|
|
126
|
+
| `styles/components/<件>.css` | **档位改写**(`.mh-<件>--<档>` 覆盖同一令牌)+ `--mh-*` 内部状态变量 | 元件根类 / 档位类 |
|
|
127
|
+
|
|
128
|
+
后一半必须在面里:它们靠「同一元素上两个选择器改写同一个令牌」生效,搬走就断链。
|
|
129
|
+
|
|
130
|
+
随包两套:
|
|
131
|
+
|
|
132
|
+
| 主题 | 入口 | 锚点 | 取值来源 |
|
|
133
|
+
|---|---|---|---|
|
|
134
|
+
| `default` | `@manohub/theme/default.css` | `[data-manohub-ui]`(兜底套) | AIHub 品牌蓝(primary `#2660FF`)|
|
|
135
|
+
| `farris` | `@manohub/theme/farris.css` | `[data-manohub-ui][data-theme='farris']` | 《Farris Web 基础视觉规范》(primary `#2a87ff`,色板编号 Th/Au/Ne/Te 逐条标注)|
|
|
136
|
+
|
|
137
|
+
换皮只需给容器加属性(`createSubApp({ theme: 'farris' })` 会写 `data-theme`),不动类名、不动组件代码、不重打包。
|
|
138
|
+
新增主题 = `packages/theme/src/<name>/` 新目录,令牌锚 `[data-manohub-ui][data-theme='<name>']`,
|
|
139
|
+
且必须**全量覆盖** default 的令牌。
|
|
140
|
+
|
|
141
|
+
局部换主题:在更深的容器上重设变量即可,例如 `.role-tree-pane { --ui-primary: #7C3AED; }`。
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { type PropType, type SlotsType, type VNodeChild } from 'vue';
|
|
2
|
+
/** 尺寸:四档预设,或**直接给像素数**(`<Avatar :size="48" />`) */
|
|
3
|
+
export type AvatarSize = 'sm' | 'md' | 'lg' | 'xl' | number;
|
|
4
|
+
export interface AvatarProps {
|
|
5
|
+
/** 尺寸四档(24 / 36 / 60 / 80)或像素数,缺省 `md` */
|
|
6
|
+
size?: AvatarSize;
|
|
7
|
+
/** 外形:`circle`(缺省)圆 / `square` 圆角矩形 */
|
|
8
|
+
shape?: 'circle' | 'square';
|
|
9
|
+
/** 图片地址(给了就渲染图片,图片自裁圆/方角) */
|
|
10
|
+
src?: string;
|
|
11
|
+
/** 响应式图片候选(与 `src` 搭配给 1x/2x) */
|
|
12
|
+
srcSet?: string;
|
|
13
|
+
/** 图片替代文本 */
|
|
14
|
+
alt?: string;
|
|
15
|
+
/** 文字头像:显示 1~2 个字(底色取主色) */
|
|
16
|
+
letter?: string;
|
|
17
|
+
/** 图标头像:`true` 用默认人像图标,或传图标名(`@manohub/icon` 的 `IconName`) */
|
|
18
|
+
icon?: boolean | string;
|
|
19
|
+
/** 占位档:虚线框(内容走默认插槽;不传插槽就只是一个占位框) */
|
|
20
|
+
placeholder?: boolean;
|
|
21
|
+
/** 方角(= `shape="square"`,保留的既有写法) */
|
|
22
|
+
square?: boolean;
|
|
23
|
+
/** 带框:底部 2px 亮环 + 环境光(与文字/卡片并排时用) */
|
|
24
|
+
framed?: boolean;
|
|
25
|
+
/** 计数档:头像组末位的「+N」 */
|
|
26
|
+
count?: string | number;
|
|
27
|
+
/** 右上角标(数字或短文字) */
|
|
28
|
+
badge?: string | number;
|
|
29
|
+
/** 图片加载失败(本件会自动回退到 `letter` / `icon` / 插槽) */
|
|
30
|
+
onError?: (event: Event) => void;
|
|
31
|
+
}
|
|
32
|
+
export declare const Avatar: {
|
|
33
|
+
new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<Readonly<import("vue").ExtractPropTypes<{
|
|
34
|
+
size: {
|
|
35
|
+
type: PropType<AvatarSize>;
|
|
36
|
+
default: string;
|
|
37
|
+
};
|
|
38
|
+
shape: {
|
|
39
|
+
type: PropType<"circle" | "square">;
|
|
40
|
+
default: undefined;
|
|
41
|
+
};
|
|
42
|
+
src: {
|
|
43
|
+
type: StringConstructor;
|
|
44
|
+
default: undefined;
|
|
45
|
+
};
|
|
46
|
+
srcSet: {
|
|
47
|
+
type: StringConstructor;
|
|
48
|
+
default: undefined;
|
|
49
|
+
};
|
|
50
|
+
alt: {
|
|
51
|
+
type: StringConstructor;
|
|
52
|
+
default: undefined;
|
|
53
|
+
};
|
|
54
|
+
letter: {
|
|
55
|
+
type: StringConstructor;
|
|
56
|
+
default: undefined;
|
|
57
|
+
};
|
|
58
|
+
icon: {
|
|
59
|
+
type: PropType<boolean | string>;
|
|
60
|
+
default: boolean;
|
|
61
|
+
};
|
|
62
|
+
placeholder: {
|
|
63
|
+
type: BooleanConstructor;
|
|
64
|
+
default: boolean;
|
|
65
|
+
};
|
|
66
|
+
square: {
|
|
67
|
+
type: BooleanConstructor;
|
|
68
|
+
default: boolean;
|
|
69
|
+
};
|
|
70
|
+
framed: {
|
|
71
|
+
type: BooleanConstructor;
|
|
72
|
+
default: boolean;
|
|
73
|
+
};
|
|
74
|
+
count: {
|
|
75
|
+
type: PropType<string | number>;
|
|
76
|
+
default: undefined;
|
|
77
|
+
};
|
|
78
|
+
badge: {
|
|
79
|
+
type: PropType<string | number>;
|
|
80
|
+
default: undefined;
|
|
81
|
+
};
|
|
82
|
+
onError: {
|
|
83
|
+
type: PropType<(event: Event) => void>;
|
|
84
|
+
default: undefined;
|
|
85
|
+
};
|
|
86
|
+
}>> & Readonly<{}>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, import("vue").PublicProps, {
|
|
87
|
+
size: AvatarSize;
|
|
88
|
+
square: boolean;
|
|
89
|
+
icon: string | boolean;
|
|
90
|
+
onError: (event: Event) => void;
|
|
91
|
+
shape: "square" | "circle";
|
|
92
|
+
src: string;
|
|
93
|
+
srcSet: string;
|
|
94
|
+
alt: string;
|
|
95
|
+
letter: string;
|
|
96
|
+
placeholder: boolean;
|
|
97
|
+
framed: boolean;
|
|
98
|
+
count: string | number;
|
|
99
|
+
badge: string | number;
|
|
100
|
+
}, true, {}, SlotsType<{
|
|
101
|
+
default?: () => VNodeChild;
|
|
102
|
+
badge?: () => VNodeChild;
|
|
103
|
+
chip?: () => VNodeChild;
|
|
104
|
+
}>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
|
|
105
|
+
P: {};
|
|
106
|
+
B: {};
|
|
107
|
+
D: {};
|
|
108
|
+
C: {};
|
|
109
|
+
M: {};
|
|
110
|
+
Defaults: {};
|
|
111
|
+
}, Readonly<import("vue").ExtractPropTypes<{
|
|
112
|
+
size: {
|
|
113
|
+
type: PropType<AvatarSize>;
|
|
114
|
+
default: string;
|
|
115
|
+
};
|
|
116
|
+
shape: {
|
|
117
|
+
type: PropType<"circle" | "square">;
|
|
118
|
+
default: undefined;
|
|
119
|
+
};
|
|
120
|
+
src: {
|
|
121
|
+
type: StringConstructor;
|
|
122
|
+
default: undefined;
|
|
123
|
+
};
|
|
124
|
+
srcSet: {
|
|
125
|
+
type: StringConstructor;
|
|
126
|
+
default: undefined;
|
|
127
|
+
};
|
|
128
|
+
alt: {
|
|
129
|
+
type: StringConstructor;
|
|
130
|
+
default: undefined;
|
|
131
|
+
};
|
|
132
|
+
letter: {
|
|
133
|
+
type: StringConstructor;
|
|
134
|
+
default: undefined;
|
|
135
|
+
};
|
|
136
|
+
icon: {
|
|
137
|
+
type: PropType<boolean | string>;
|
|
138
|
+
default: boolean;
|
|
139
|
+
};
|
|
140
|
+
placeholder: {
|
|
141
|
+
type: BooleanConstructor;
|
|
142
|
+
default: boolean;
|
|
143
|
+
};
|
|
144
|
+
square: {
|
|
145
|
+
type: BooleanConstructor;
|
|
146
|
+
default: boolean;
|
|
147
|
+
};
|
|
148
|
+
framed: {
|
|
149
|
+
type: BooleanConstructor;
|
|
150
|
+
default: boolean;
|
|
151
|
+
};
|
|
152
|
+
count: {
|
|
153
|
+
type: PropType<string | number>;
|
|
154
|
+
default: undefined;
|
|
155
|
+
};
|
|
156
|
+
badge: {
|
|
157
|
+
type: PropType<string | number>;
|
|
158
|
+
default: undefined;
|
|
159
|
+
};
|
|
160
|
+
onError: {
|
|
161
|
+
type: PropType<(event: Event) => void>;
|
|
162
|
+
default: undefined;
|
|
163
|
+
};
|
|
164
|
+
}>> & Readonly<{}>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, {
|
|
165
|
+
size: AvatarSize;
|
|
166
|
+
square: boolean;
|
|
167
|
+
icon: string | boolean;
|
|
168
|
+
onError: (event: Event) => void;
|
|
169
|
+
shape: "square" | "circle";
|
|
170
|
+
src: string;
|
|
171
|
+
srcSet: string;
|
|
172
|
+
alt: string;
|
|
173
|
+
letter: string;
|
|
174
|
+
placeholder: boolean;
|
|
175
|
+
framed: boolean;
|
|
176
|
+
count: string | number;
|
|
177
|
+
badge: string | number;
|
|
178
|
+
}>;
|
|
179
|
+
__isFragment?: never;
|
|
180
|
+
__isTeleport?: never;
|
|
181
|
+
__isSuspense?: never;
|
|
182
|
+
} & import("vue").ComponentOptionsBase<Readonly<import("vue").ExtractPropTypes<{
|
|
183
|
+
size: {
|
|
184
|
+
type: PropType<AvatarSize>;
|
|
185
|
+
default: string;
|
|
186
|
+
};
|
|
187
|
+
shape: {
|
|
188
|
+
type: PropType<"circle" | "square">;
|
|
189
|
+
default: undefined;
|
|
190
|
+
};
|
|
191
|
+
src: {
|
|
192
|
+
type: StringConstructor;
|
|
193
|
+
default: undefined;
|
|
194
|
+
};
|
|
195
|
+
srcSet: {
|
|
196
|
+
type: StringConstructor;
|
|
197
|
+
default: undefined;
|
|
198
|
+
};
|
|
199
|
+
alt: {
|
|
200
|
+
type: StringConstructor;
|
|
201
|
+
default: undefined;
|
|
202
|
+
};
|
|
203
|
+
letter: {
|
|
204
|
+
type: StringConstructor;
|
|
205
|
+
default: undefined;
|
|
206
|
+
};
|
|
207
|
+
icon: {
|
|
208
|
+
type: PropType<boolean | string>;
|
|
209
|
+
default: boolean;
|
|
210
|
+
};
|
|
211
|
+
placeholder: {
|
|
212
|
+
type: BooleanConstructor;
|
|
213
|
+
default: boolean;
|
|
214
|
+
};
|
|
215
|
+
square: {
|
|
216
|
+
type: BooleanConstructor;
|
|
217
|
+
default: boolean;
|
|
218
|
+
};
|
|
219
|
+
framed: {
|
|
220
|
+
type: BooleanConstructor;
|
|
221
|
+
default: boolean;
|
|
222
|
+
};
|
|
223
|
+
count: {
|
|
224
|
+
type: PropType<string | number>;
|
|
225
|
+
default: undefined;
|
|
226
|
+
};
|
|
227
|
+
badge: {
|
|
228
|
+
type: PropType<string | number>;
|
|
229
|
+
default: undefined;
|
|
230
|
+
};
|
|
231
|
+
onError: {
|
|
232
|
+
type: PropType<(event: Event) => void>;
|
|
233
|
+
default: undefined;
|
|
234
|
+
};
|
|
235
|
+
}>> & Readonly<{}>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, {
|
|
236
|
+
size: AvatarSize;
|
|
237
|
+
square: boolean;
|
|
238
|
+
icon: string | boolean;
|
|
239
|
+
onError: (event: Event) => void;
|
|
240
|
+
shape: "square" | "circle";
|
|
241
|
+
src: string;
|
|
242
|
+
srcSet: string;
|
|
243
|
+
alt: string;
|
|
244
|
+
letter: string;
|
|
245
|
+
placeholder: boolean;
|
|
246
|
+
framed: boolean;
|
|
247
|
+
count: string | number;
|
|
248
|
+
badge: string | number;
|
|
249
|
+
}, {}, string, SlotsType<{
|
|
250
|
+
default?: () => VNodeChild;
|
|
251
|
+
badge?: () => VNodeChild;
|
|
252
|
+
chip?: () => VNodeChild;
|
|
253
|
+
}>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & {
|
|
254
|
+
Group: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
255
|
+
/** 最多显示几个,其余收成末位「+N」(0 / 不传 = 不限) */
|
|
256
|
+
max: {
|
|
257
|
+
type: NumberConstructor;
|
|
258
|
+
default: number;
|
|
259
|
+
};
|
|
260
|
+
/** 堆叠重叠量(像素,缺省 10) */
|
|
261
|
+
gap: {
|
|
262
|
+
type: NumberConstructor;
|
|
263
|
+
default: number;
|
|
264
|
+
};
|
|
265
|
+
}>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
266
|
+
/** 最多显示几个,其余收成末位「+N」(0 / 不传 = 不限) */
|
|
267
|
+
max: {
|
|
268
|
+
type: NumberConstructor;
|
|
269
|
+
default: number;
|
|
270
|
+
};
|
|
271
|
+
/** 堆叠重叠量(像素,缺省 10) */
|
|
272
|
+
gap: {
|
|
273
|
+
type: NumberConstructor;
|
|
274
|
+
default: number;
|
|
275
|
+
};
|
|
276
|
+
}>> & Readonly<{}>, {
|
|
277
|
+
max: number;
|
|
278
|
+
gap: number;
|
|
279
|
+
}, SlotsType<{
|
|
280
|
+
default?: () => VNodeChild;
|
|
281
|
+
}>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
282
|
+
};
|
|
283
|
+
export type AvatarCompound = typeof Avatar;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { defineComponent, ref, computed, createVNode } from "vue";
|
|
2
|
+
import { Icon } from "@manohub/icon";
|
|
3
|
+
const AvatarRoot = /* @__PURE__ */ defineComponent({
|
|
4
|
+
name: "Avatar",
|
|
5
|
+
inheritAttrs: false,
|
|
6
|
+
props: {
|
|
7
|
+
size: {
|
|
8
|
+
type: [String, Number],
|
|
9
|
+
default: "md"
|
|
10
|
+
},
|
|
11
|
+
shape: {
|
|
12
|
+
type: String,
|
|
13
|
+
default: void 0
|
|
14
|
+
},
|
|
15
|
+
src: {
|
|
16
|
+
type: String,
|
|
17
|
+
default: void 0
|
|
18
|
+
},
|
|
19
|
+
srcSet: {
|
|
20
|
+
type: String,
|
|
21
|
+
default: void 0
|
|
22
|
+
},
|
|
23
|
+
alt: {
|
|
24
|
+
type: String,
|
|
25
|
+
default: void 0
|
|
26
|
+
},
|
|
27
|
+
letter: {
|
|
28
|
+
type: String,
|
|
29
|
+
default: void 0
|
|
30
|
+
},
|
|
31
|
+
icon: {
|
|
32
|
+
type: [Boolean, String],
|
|
33
|
+
default: false
|
|
34
|
+
},
|
|
35
|
+
placeholder: {
|
|
36
|
+
type: Boolean,
|
|
37
|
+
default: false
|
|
38
|
+
},
|
|
39
|
+
square: {
|
|
40
|
+
type: Boolean,
|
|
41
|
+
default: false
|
|
42
|
+
},
|
|
43
|
+
framed: {
|
|
44
|
+
type: Boolean,
|
|
45
|
+
default: false
|
|
46
|
+
},
|
|
47
|
+
count: {
|
|
48
|
+
type: [String, Number],
|
|
49
|
+
default: void 0
|
|
50
|
+
},
|
|
51
|
+
badge: {
|
|
52
|
+
type: [String, Number],
|
|
53
|
+
default: void 0
|
|
54
|
+
},
|
|
55
|
+
onError: {
|
|
56
|
+
type: Function,
|
|
57
|
+
default: void 0
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
slots: Object,
|
|
61
|
+
setup(props, {
|
|
62
|
+
slots,
|
|
63
|
+
attrs
|
|
64
|
+
}) {
|
|
65
|
+
const failed = ref(false);
|
|
66
|
+
const showImage = computed(() => !!props.src && !failed.value);
|
|
67
|
+
const isSquare = computed(() => props.shape === "square" || props.square);
|
|
68
|
+
const presetSize = computed(() => typeof props.size === "number" ? "md" : props.size);
|
|
69
|
+
const customStyle = computed(() => typeof props.size === "number" ? {
|
|
70
|
+
width: `${props.size}px`,
|
|
71
|
+
height: `${props.size}px`,
|
|
72
|
+
fontSize: `${Math.max(12, Math.round(props.size * 0.38))}px`
|
|
73
|
+
} : void 0);
|
|
74
|
+
const iconSize = computed(() => {
|
|
75
|
+
const size = typeof props.size === "number" ? props.size : {
|
|
76
|
+
sm: 14,
|
|
77
|
+
md: 20,
|
|
78
|
+
lg: 30,
|
|
79
|
+
xl: 40
|
|
80
|
+
}[props.size];
|
|
81
|
+
return Math.round(size * 0.56);
|
|
82
|
+
});
|
|
83
|
+
const content = () => {
|
|
84
|
+
if (slots.default) return slots.default();
|
|
85
|
+
if (showImage.value) {
|
|
86
|
+
return createVNode("img", {
|
|
87
|
+
"src": props.src,
|
|
88
|
+
"srcset": props.srcSet,
|
|
89
|
+
"alt": props.alt ?? "",
|
|
90
|
+
"onError": (event) => {
|
|
91
|
+
failed.value = true;
|
|
92
|
+
props.onError?.(event);
|
|
93
|
+
}
|
|
94
|
+
}, null);
|
|
95
|
+
}
|
|
96
|
+
if (props.letter) return createVNode("span", null, [props.letter]);
|
|
97
|
+
if (props.icon) {
|
|
98
|
+
const name = typeof props.icon === "string" ? props.icon : "user";
|
|
99
|
+
return createVNode(Icon, {
|
|
100
|
+
"name": name,
|
|
101
|
+
"size": iconSize.value
|
|
102
|
+
}, null);
|
|
103
|
+
}
|
|
104
|
+
if (props.count !== void 0) return createVNode("span", null, [props.count]);
|
|
105
|
+
return null;
|
|
106
|
+
};
|
|
107
|
+
return () => createVNode("span", {
|
|
108
|
+
"class": ["mh-avatar", `mh-avatar--${presetSize.value}`, props.letter && !showImage.value ? "mh-avatar--letter" : void 0, props.icon && !showImage.value ? "mh-avatar--icon" : void 0, props.placeholder ? "mh-avatar--placeholder" : void 0, isSquare.value ? "mh-avatar--square" : void 0, props.framed ? "mh-avatar--framed" : void 0, props.count !== void 0 && !showImage.value ? "mh-avatar--count" : void 0, attrs.class].filter(Boolean).join(" "),
|
|
109
|
+
"style": {
|
|
110
|
+
...customStyle.value,
|
|
111
|
+
...attrs.style
|
|
112
|
+
}
|
|
113
|
+
}, [content(), props.badge !== void 0 || slots.badge ? createVNode("span", {
|
|
114
|
+
"class": "mh-avatar__badge"
|
|
115
|
+
}, [slots.badge ? slots.badge() : props.badge]) : null, slots.chip ? createVNode("span", {
|
|
116
|
+
"class": "mh-avatar__chip"
|
|
117
|
+
}, [slots.chip()]) : null]);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
const flatten = (nodes) => nodes.flatMap((node) => {
|
|
121
|
+
if (Array.isArray(node)) return flatten(node);
|
|
122
|
+
return node ? [node] : [];
|
|
123
|
+
});
|
|
124
|
+
const AvatarGroup = /* @__PURE__ */ defineComponent({
|
|
125
|
+
name: "AvatarGroup",
|
|
126
|
+
inheritAttrs: false,
|
|
127
|
+
props: {
|
|
128
|
+
/** 最多显示几个,其余收成末位「+N」(0 / 不传 = 不限) */
|
|
129
|
+
max: {
|
|
130
|
+
type: Number,
|
|
131
|
+
default: 0
|
|
132
|
+
},
|
|
133
|
+
/** 堆叠重叠量(像素,缺省 10) */
|
|
134
|
+
gap: {
|
|
135
|
+
type: Number,
|
|
136
|
+
default: 10
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
slots: Object,
|
|
140
|
+
setup(props, {
|
|
141
|
+
slots,
|
|
142
|
+
attrs
|
|
143
|
+
}) {
|
|
144
|
+
const nodes = computed(() => flatten([slots.default?.() ?? null]));
|
|
145
|
+
const rendered = computed(() => {
|
|
146
|
+
const list = nodes.value;
|
|
147
|
+
if (!props.max || list.length <= props.max) return list;
|
|
148
|
+
return [...list.slice(0, props.max), createVNode(AvatarRoot, {
|
|
149
|
+
"key": "__mh-avatar-rest",
|
|
150
|
+
"count": `+${list.length - props.max}`
|
|
151
|
+
}, null)];
|
|
152
|
+
});
|
|
153
|
+
return () => createVNode("span", {
|
|
154
|
+
"class": ["mh-avatar-group", attrs.class].filter(Boolean).join(" "),
|
|
155
|
+
"style": {
|
|
156
|
+
"--ui-avatar-gap": `${props.gap}px`,
|
|
157
|
+
...attrs.style
|
|
158
|
+
}
|
|
159
|
+
}, [rendered.value]);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
const Avatar = Object.assign(AvatarRoot, {
|
|
163
|
+
Group: AvatarGroup
|
|
164
|
+
});
|
|
165
|
+
export {
|
|
166
|
+
Avatar
|
|
167
|
+
};
|