@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.
Files changed (169) hide show
  1. package/README.md +141 -0
  2. package/dist/components/avatar/index.d.ts +283 -0
  3. package/dist/components/avatar/index.js +167 -0
  4. package/dist/components/badge/index.d.ts +132 -0
  5. package/dist/components/badge/index.js +99 -0
  6. package/dist/components/button/index.d.ts +578 -0
  7. package/dist/components/button/index.js +191 -0
  8. package/dist/components/capsule/index.d.ts +161 -0
  9. package/dist/components/capsule/index.js +101 -0
  10. package/dist/components/card/index.d.ts +456 -0
  11. package/dist/components/card/index.js +231 -0
  12. package/dist/components/checkbox/index.d.ts +331 -0
  13. package/dist/components/checkbox/index.js +165 -0
  14. package/dist/components/code/index.d.ts +134 -0
  15. package/dist/components/code/index.js +117 -0
  16. package/dist/components/code-editor/index.d.ts +163 -0
  17. package/dist/components/code-editor/index.js +98 -0
  18. package/dist/components/collapse/index.d.ts +125 -0
  19. package/dist/components/collapse/index.js +95 -0
  20. package/dist/components/control-size.d.ts +8 -0
  21. package/dist/components/dialog/index.d.ts +224 -0
  22. package/dist/components/dialog/index.js +194 -0
  23. package/dist/components/divider/index.d.ts +64 -0
  24. package/dist/components/divider/index.js +62 -0
  25. package/dist/components/drawer/index.d.ts +226 -0
  26. package/dist/components/drawer/index.js +190 -0
  27. package/dist/components/filter/index.d.ts +391 -0
  28. package/dist/components/filter/index.js +203 -0
  29. package/dist/components/form/index.d.ts +704 -0
  30. package/dist/components/form/index.js +329 -0
  31. package/dist/components/input/index.d.ts +980 -0
  32. package/dist/components/input/index.js +334 -0
  33. package/dist/components/layout/index.d.ts +162 -0
  34. package/dist/components/layout/index.js +100 -0
  35. package/dist/components/list-view/index.d.ts +563 -0
  36. package/dist/components/list-view/index.js +221 -0
  37. package/dist/components/loading/index.d.ts +77 -0
  38. package/dist/components/loading/index.js +40 -0
  39. package/dist/components/nav/index.d.ts +293 -0
  40. package/dist/components/nav/index.js +164 -0
  41. package/dist/components/notice/index.d.ts +137 -0
  42. package/dist/components/notice/index.js +101 -0
  43. package/dist/components/notification/index.d.ts +118 -0
  44. package/dist/components/notification/index.js +99 -0
  45. package/dist/components/page/index.d.ts +276 -0
  46. package/dist/components/page/index.js +351 -0
  47. package/dist/components/page/split-resize.d.ts +42 -0
  48. package/dist/components/page/split-resize.js +36 -0
  49. package/dist/components/pagination/index.d.ts +171 -0
  50. package/dist/components/pagination/index.js +226 -0
  51. package/dist/components/panel/index.d.ts +401 -0
  52. package/dist/components/panel/index.js +193 -0
  53. package/dist/components/part-padding.d.ts +27 -0
  54. package/dist/components/part-padding.js +23 -0
  55. package/dist/components/query-state/index.d.ts +207 -0
  56. package/dist/components/query-state/index.js +181 -0
  57. package/dist/components/radio/index.d.ts +473 -0
  58. package/dist/components/radio/index.js +250 -0
  59. package/dist/components/search/index.d.ts +167 -0
  60. package/dist/components/search/index.js +105 -0
  61. package/dist/components/select/index.d.ts +252 -0
  62. package/dist/components/select/index.js +281 -0
  63. package/dist/components/skeleton/index.d.ts +88 -0
  64. package/dist/components/skeleton/index.js +56 -0
  65. package/dist/components/steps/index.d.ts +199 -0
  66. package/dist/components/steps/index.js +117 -0
  67. package/dist/components/switch/index.d.ts +150 -0
  68. package/dist/components/switch/index.js +103 -0
  69. package/dist/components/table/index.d.ts +351 -0
  70. package/dist/components/table/index.js +286 -0
  71. package/dist/components/tabs/index.d.ts +179 -0
  72. package/dist/components/tabs/index.js +153 -0
  73. package/dist/components/tabset/index.d.ts +105 -0
  74. package/dist/components/tabset/index.js +68 -0
  75. package/dist/components/tag/index.d.ts +203 -0
  76. package/dist/components/tag/index.js +144 -0
  77. package/dist/components/toast/index.d.ts +271 -0
  78. package/dist/components/toast/index.js +116 -0
  79. package/dist/components/tooltip/index.d.ts +126 -0
  80. package/dist/components/tooltip/index.js +97 -0
  81. package/dist/components/tree/index.d.ts +317 -0
  82. package/dist/components/tree/index.js +251 -0
  83. package/dist/components/upload/index.d.ts +384 -0
  84. package/dist/components/upload/index.js +172 -0
  85. package/dist/index.d.ts +63 -0
  86. package/dist/index.js +102 -0
  87. package/dist/services/base.d.ts +21 -0
  88. package/dist/services/base.js +42 -0
  89. package/dist/services/dialog.d.ts +41 -0
  90. package/dist/services/dialog.js +83 -0
  91. package/dist/services/index.d.ts +12 -0
  92. package/dist/services/loading.d.ts +21 -0
  93. package/dist/services/loading.js +41 -0
  94. package/dist/services/toast.d.ts +40 -0
  95. package/dist/services/toast.js +74 -0
  96. package/dist/styles/components/avatar.css +128 -0
  97. package/dist/styles/components/avatar.tokens.css +24 -0
  98. package/dist/styles/components/badge.css +237 -0
  99. package/dist/styles/components/button.css +454 -0
  100. package/dist/styles/components/button.tokens.css +56 -0
  101. package/dist/styles/components/capsule.css +120 -0
  102. package/dist/styles/components/capsule.tokens.css +27 -0
  103. package/dist/styles/components/card.css +192 -0
  104. package/dist/styles/components/checkbox.css +145 -0
  105. package/dist/styles/components/checkbox.tokens.css +26 -0
  106. package/dist/styles/components/code-editor.css +91 -0
  107. package/dist/styles/components/code-editor.tokens.css +24 -0
  108. package/dist/styles/components/code.css +153 -0
  109. package/dist/styles/components/code.tokens.css +29 -0
  110. package/dist/styles/components/collapse.css +62 -0
  111. package/dist/styles/components/collapse.tokens.css +20 -0
  112. package/dist/styles/components/dialog.css +106 -0
  113. package/dist/styles/components/dialog.tokens.css +24 -0
  114. package/dist/styles/components/divider.css +71 -0
  115. package/dist/styles/components/divider.tokens.css +21 -0
  116. package/dist/styles/components/drawer.css +143 -0
  117. package/dist/styles/components/drawer.tokens.css +22 -0
  118. package/dist/styles/components/filter.css +41 -0
  119. package/dist/styles/components/filter.tokens.css +14 -0
  120. package/dist/styles/components/form.css +220 -0
  121. package/dist/styles/components/form.tokens.css +15 -0
  122. package/dist/styles/components/input.css +445 -0
  123. package/dist/styles/components/input.tokens.css +41 -0
  124. package/dist/styles/components/layout.css +198 -0
  125. package/dist/styles/components/list-view.css +244 -0
  126. package/dist/styles/components/loading.css +76 -0
  127. package/dist/styles/components/loading.tokens.css +23 -0
  128. package/dist/styles/components/nav.css +48 -0
  129. package/dist/styles/components/notice.css +153 -0
  130. package/dist/styles/components/notice.tokens.css +17 -0
  131. package/dist/styles/components/notification.css +159 -0
  132. package/dist/styles/components/notification.tokens.css +37 -0
  133. package/dist/styles/components/page.css +473 -0
  134. package/dist/styles/components/pagination.css +297 -0
  135. package/dist/styles/components/pagination.tokens.css +32 -0
  136. package/dist/styles/components/panel.css +77 -0
  137. package/dist/styles/components/query-state.css +88 -0
  138. package/dist/styles/components/radio.css +271 -0
  139. package/dist/styles/components/radio.tokens.css +26 -0
  140. package/dist/styles/components/search.css +30 -0
  141. package/dist/styles/components/select.css +242 -0
  142. package/dist/styles/components/select.tokens.css +33 -0
  143. package/dist/styles/components/skeleton.css +60 -0
  144. package/dist/styles/components/skeleton.tokens.css +27 -0
  145. package/dist/styles/components/steps.css +157 -0
  146. package/dist/styles/components/steps.tokens.css +42 -0
  147. package/dist/styles/components/switch.css +165 -0
  148. package/dist/styles/components/switch.tokens.css +26 -0
  149. package/dist/styles/components/table.css +439 -0
  150. package/dist/styles/components/table.tokens.css +41 -0
  151. package/dist/styles/components/tabs.css +234 -0
  152. package/dist/styles/components/tabs.tokens.css +30 -0
  153. package/dist/styles/components/tag.css +200 -0
  154. package/dist/styles/components/tag.tokens.css +30 -0
  155. package/dist/styles/components/toast.css +150 -0
  156. package/dist/styles/components/toast.tokens.css +26 -0
  157. package/dist/styles/components/tooltip.css +89 -0
  158. package/dist/styles/components/tooltip.tokens.css +25 -0
  159. package/dist/styles/components/tree.css +139 -0
  160. package/dist/styles/components/tree.tokens.css +20 -0
  161. package/dist/styles/components/upload.css +150 -0
  162. package/dist/styles/components/upload.tokens.css +27 -0
  163. package/dist/styles/index.css +149 -0
  164. package/dist/styles/operations/collapse.css +84 -0
  165. package/dist/styles/operations/popup.css +189 -0
  166. package/dist/styles/operations/tabset.css +57 -0
  167. package/dist/utils/state-illustration.d.ts +31 -0
  168. package/dist/utils/state-illustration.js +156 -0
  169. 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
+ };