@manohub/ui 0.10.1 → 0.10.3

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 (51) hide show
  1. package/README.md +1 -1
  2. package/dist/components/badge/index.d.ts +46 -1
  3. package/dist/components/badge/index.js +42 -9
  4. package/dist/components/card/index.d.ts +1 -1
  5. package/dist/components/drawer/index.d.ts +1 -1
  6. package/dist/components/form/index.d.ts +50 -1
  7. package/dist/components/form/index.js +20 -2
  8. package/dist/components/input/index.js +1 -0
  9. package/dist/components/nav/index.d.ts +27 -0
  10. package/dist/components/nav/index.js +12 -0
  11. package/dist/components/notice/index.d.ts +1 -1
  12. package/dist/components/notification/index.d.ts +2 -2
  13. package/dist/components/select/index.d.ts +9 -0
  14. package/dist/components/select/index.js +57 -12
  15. package/dist/components/select/select-list.d.ts +42 -8
  16. package/dist/components/select/select-list.js +36 -11
  17. package/dist/components/select/select-panel.d.ts +36 -8
  18. package/dist/components/select/select-panel.js +18 -4
  19. package/dist/components/select/select-shared.d.ts +51 -10
  20. package/dist/components/select/select-shared.js +12 -2
  21. package/dist/components/select/select-tree.d.ts +25 -3
  22. package/dist/components/select/select-tree.js +17 -6
  23. package/dist/components/select/select-trigger.d.ts +21 -0
  24. package/dist/components/select/select-trigger.js +15 -1
  25. package/dist/components/tag/index.d.ts +32 -153
  26. package/dist/components/tag/index.js +102 -101
  27. package/dist/components/text/index.d.ts +1 -1
  28. package/dist/components/tooltip/index.d.ts +1 -1
  29. package/dist/components/tree/index.d.ts +65 -0
  30. package/dist/components/tree/index.js +29 -2
  31. package/dist/index.d.ts +1 -1
  32. package/dist/styles/components/avatar.css +2 -2
  33. package/dist/styles/components/badge.css +31 -4
  34. package/dist/styles/components/badge.tokens.css +24 -0
  35. package/dist/styles/components/button.css +7 -6
  36. package/dist/styles/components/button.tokens.css +5 -2
  37. package/dist/styles/components/dialog.css +13 -0
  38. package/dist/styles/components/divider.css +8 -4
  39. package/dist/styles/components/divider.tokens.css +2 -1
  40. package/dist/styles/components/form.css +51 -0
  41. package/dist/styles/components/notification.css +1 -1
  42. package/dist/styles/components/select.css +62 -39
  43. package/dist/styles/components/select.tokens.css +16 -9
  44. package/dist/styles/components/tag.css +77 -154
  45. package/dist/styles/components/tag.tokens.css +13 -19
  46. package/dist/styles/components/tree.css +37 -3
  47. package/dist/styles/components/tree.tokens.css +4 -3
  48. package/dist/styles/components/upload.tokens.css +1 -1
  49. package/dist/styles/index.css +2 -1
  50. package/dist/styles/operations/popup.css +5 -1
  51. package/package.json +2 -2
@@ -1,203 +1,82 @@
1
- import { type IconName } from '@manohub/icon';
2
1
  import { type PropType, type SlotsType, type VNodeChild } from 'vue';
3
- import type { ControlSize } from '../control-size';
2
+ import { type BadgeShape, type BadgeSize, type BadgeTone } from '../badge';
3
+ /** 本件的形态:`Badge` 的三档 + `add`(新增入口,本件扩展档) */
4
+ export type TagShape = BadgeShape | 'add';
4
5
  export interface TagProps {
5
6
  /**
6
7
  * 形态:
7
- * - `regular`(缺省)常规内容标签
8
- * - `status` 状态标签(配 `tone` 用四色)
9
- * - `select` 可选标签(渲染隐身原生 radio,选中态实底主色)
10
- * - `add` 新增标签(虚线框 + 加号图标,点击回调)
11
- * - `chip` 轻量筛选片
8
+ * - `soft`(缺省)/ `outline` / `plain` —— 转发给 `Badge` 的同名档;
9
+ * - `add` 新增入口:虚线入口点击后就地变窄输入框,`Enter` 提交回调 `onAdd`。
12
10
  */
13
- shape?: 'regular' | 'status' | 'select' | 'add' | 'chip';
14
- /** 状态档语义(只用 `shape="status"`) */
15
- tone?: 'info' | 'success' | 'warning' | 'error';
16
- /** 尺寸档(20 / 24 / 28) */
17
- size?: ControlSize;
18
- /** 大号档(= `size="lg"`,保留的既有写法) */
19
- lg?: boolean;
20
- /** 前置图标(图标包的闭合枚举) */
21
- icon?: IconName;
22
- /** 描边档:浅主色底 + 浅主色边(设计稿的标签配方,配 `size="sm"` 即「表格内标签」) */
23
- outline?: boolean;
24
- /** 可关闭(右端出现关闭位) */
11
+ shape?: TagShape;
12
+ /** 语义色档(转发 `Badge`;缺省 `default` 是中性面) */
13
+ tone?: BadgeTone;
14
+ /** 尺寸档(转发 `Badge`:`sm` 20 / `md` 24);缺省 `md`(与 `Badge` 的缺省 `sm` 不同,见下) */
15
+ size?: BadgeSize;
16
+ /** 可关闭:右端出现关闭位(**受控**:只回调 `onClose`,本件不删自己) */
25
17
  closable?: boolean;
26
- /** 自定义色(hex):底按该色 14% 与白混出,字用该色本身 */
27
- color?: string;
28
- /** `shape="select"` 的选中态 */
29
- selected?: boolean;
30
- /** 禁用(可选档不可点、转弱) */
31
- disabled?: boolean;
32
- /** `shape="select"` 的原生分组名与值(组内同名互斥) */
33
- name?: string;
34
- value?: string | number;
35
- /** `shape="add"` / `chip` 的点击回调(`select` 档用 `onChange`) */
36
- onClick?: () => void;
37
- /** `shape="select"` 的选中变化回调 */
38
- onChange?: (selected: boolean) => void;
39
- /** 关闭回调(`closable` 的关闭位点击) */
18
+ /** 关闭位回调(`closable`) */
40
19
  onClose?: () => void;
20
+ /** 新增档提交回调(`shape="add"`):载荷是用户敲定的标签名(已 trim、非空) */
21
+ onAdd?: (name: string) => void;
41
22
  }
42
- /**
43
- * 标签(常规 / 状态 / 可选 / 新增 / 轻量筛选片)。
44
- *
45
- * **与 `Badge` 的判据**:`Badge` 是状态徽标(固定短、状态色、可带圆点/数字);
46
- * 本件是内容标签(描述性文字,长度随内容,可能可选/可加/可关闭)。
47
- *
48
- * 可选档(`shape="select"`)是**原生 radio**:组内同名互斥、键盘方向键换项都由浏览器接管,
49
- * 样式层靠 `:has(input:checked)` / `[data-state='selected']` / `.is-selected` 三源等价选态。
50
- *
51
- * 关闭是**受控**的:本件只回调 `onClose`,是否移除由业务决定(与 `Notification` 同一口径)——
52
- * 组件自己删自己会让「关闭后又能从别处恢复」这类需求无解。
53
- *
54
- * @example 状态标签
55
- * <Tag shape="status" tone="success">已通过</Tag>
56
- *
57
- * @example 可关闭的筛选条件
58
- * <Tag closable icon="filter" onClose={() => remove('status')}>状态:已通过</Tag>
59
- *
60
- * @example 可选标签(同组互斥)
61
- * <Tag shape="select" name="scope" value="all" :selected="v === 'all'" @change="select('all')">全部</Tag>
62
- */
63
23
  export declare const Tag: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
64
24
  shape: {
65
- type: PropType<TagProps["shape"]>;
25
+ type: PropType<TagShape>;
66
26
  default: string;
67
27
  };
68
28
  tone: {
69
- type: PropType<TagProps["tone"]>;
70
- default: undefined;
29
+ type: PropType<BadgeTone>;
30
+ default: string;
71
31
  };
72
32
  size: {
73
- type: PropType<ControlSize>;
33
+ type: PropType<BadgeSize>;
74
34
  default: string;
75
35
  };
76
- lg: {
77
- type: BooleanConstructor;
78
- default: boolean;
79
- };
80
- icon: {
81
- type: PropType<IconName>;
82
- default: undefined;
83
- };
84
- outline: {
85
- type: BooleanConstructor;
86
- default: boolean;
87
- };
88
36
  closable: {
89
37
  type: BooleanConstructor;
90
38
  default: boolean;
91
39
  };
92
- color: {
93
- type: StringConstructor;
94
- default: undefined;
95
- };
96
- selected: {
97
- type: BooleanConstructor;
98
- default: boolean;
99
- };
100
- disabled: {
101
- type: BooleanConstructor;
102
- default: boolean;
103
- };
104
- name: {
105
- type: StringConstructor;
106
- default: undefined;
107
- };
108
- value: {
109
- type: PropType<string | number>;
110
- default: undefined;
111
- };
112
- onClick: {
40
+ onClose: {
113
41
  type: PropType<() => void>;
114
42
  default: undefined;
115
43
  };
116
- onChange: {
117
- type: PropType<(selected: boolean) => void>;
118
- default: undefined;
119
- };
120
- onClose: {
121
- type: PropType<() => void>;
44
+ onAdd: {
45
+ type: PropType<(name: string) => void>;
122
46
  default: undefined;
123
47
  };
124
48
  }>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
125
49
  shape: {
126
- type: PropType<TagProps["shape"]>;
50
+ type: PropType<TagShape>;
127
51
  default: string;
128
52
  };
129
53
  tone: {
130
- type: PropType<TagProps["tone"]>;
131
- default: undefined;
54
+ type: PropType<BadgeTone>;
55
+ default: string;
132
56
  };
133
57
  size: {
134
- type: PropType<ControlSize>;
58
+ type: PropType<BadgeSize>;
135
59
  default: string;
136
60
  };
137
- lg: {
138
- type: BooleanConstructor;
139
- default: boolean;
140
- };
141
- icon: {
142
- type: PropType<IconName>;
143
- default: undefined;
144
- };
145
- outline: {
146
- type: BooleanConstructor;
147
- default: boolean;
148
- };
149
61
  closable: {
150
62
  type: BooleanConstructor;
151
63
  default: boolean;
152
64
  };
153
- color: {
154
- type: StringConstructor;
155
- default: undefined;
156
- };
157
- selected: {
158
- type: BooleanConstructor;
159
- default: boolean;
160
- };
161
- disabled: {
162
- type: BooleanConstructor;
163
- default: boolean;
164
- };
165
- name: {
166
- type: StringConstructor;
167
- default: undefined;
168
- };
169
- value: {
170
- type: PropType<string | number>;
171
- default: undefined;
172
- };
173
- onClick: {
65
+ onClose: {
174
66
  type: PropType<() => void>;
175
67
  default: undefined;
176
68
  };
177
- onChange: {
178
- type: PropType<(selected: boolean) => void>;
179
- default: undefined;
180
- };
181
- onClose: {
182
- type: PropType<() => void>;
69
+ onAdd: {
70
+ type: PropType<(name: string) => void>;
183
71
  default: undefined;
184
72
  };
185
73
  }>> & Readonly<{}>, {
186
- color: string;
187
- size: ControlSize;
188
- icon: "search" | "link" | "filter" | "copy" | "help" | "table" | "menu" | "delete" | "image" | "close" | "key" | "page-title" | "loading" | "info" | "alert" | "chevron-left" | "chevron-right" | "chevron-down" | "chevron-up" | "check" | "user" | "download" | "back" | "folder" | "folder-open" | "file" | "file-code" | "file-json" | "file-text" | "terminal" | "shield" | "tool" | "plus" | "notification" | "settings" | "logout" | "home" | "workbench" | "apps" | "favorite" | "favorite-filled" | "drag-handle" | "edit" | "refresh" | "upload" | "save" | "more" | "preview" | "external-link" | "minus" | "plus-circle" | "close-circle" | "info-circle" | "check-circle" | "alert-circle" | "list" | "calendar" | "clock" | "lock" | "unlock" | "users" | "archive" | "package" | "plug" | "branch" | "tag" | "ai" | "shield-check" | "shield-alert" | "history";
189
- disabled: boolean;
190
- name: string;
191
- value: string | number;
192
- onChange: (selected: boolean) => void;
193
- onClick: () => void;
74
+ size: BadgeSize;
194
75
  onClose: () => void;
195
- outline: boolean;
196
- selected: boolean;
197
- lg: boolean;
198
- shape: "add" | "select" | "chip" | "regular" | "status" | undefined;
76
+ shape: TagShape;
199
77
  closable: boolean;
200
- tone: "error" | "info" | "success" | "warning" | undefined;
78
+ onAdd: (name: string) => void;
79
+ tone: BadgeTone;
201
80
  }, SlotsType<{
202
81
  default?: () => VNodeChild;
203
82
  }>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -1,69 +1,34 @@
1
- import { defineComponent, computed, createVNode, mergeProps, Fragment, isVNode } from "vue";
1
+ import { defineComponent, computed, ref, watch, createVNode, mergeProps, Fragment } from "vue";
2
2
  import { Icon } from "@manohub/icon";
3
- function _isSlot(s) {
4
- return typeof s === "function" || Object.prototype.toString.call(s) === "[object Object]" && !isVNode(s);
5
- }
3
+ import { Badge } from "../badge/index.js";
4
+ import { Input } from "../input/index.js";
6
5
  const Tag = /* @__PURE__ */ defineComponent({
7
6
  name: "Tag",
8
7
  inheritAttrs: false,
9
8
  props: {
10
9
  shape: {
11
10
  type: String,
12
- default: "regular"
11
+ default: "soft"
13
12
  },
14
13
  tone: {
15
14
  type: String,
16
- default: void 0
15
+ default: "default"
17
16
  },
17
+ /* 缺省 `md`(24)而不是跟随 `Badge` 的 `sm`(20):标签比徽标常用大一号 —— 它是列表项 / 卡片头
18
+ 里承载文字的那一枚,20 档留给「表格内标签」那种密集场景(显式写 `size="sm"`)。 */
18
19
  size: {
19
20
  type: String,
20
21
  default: "md"
21
22
  },
22
- lg: {
23
- type: Boolean,
24
- default: false
25
- },
26
- icon: {
27
- type: String,
28
- default: void 0
29
- },
30
- outline: {
31
- type: Boolean,
32
- default: false
33
- },
34
23
  closable: {
35
24
  type: Boolean,
36
25
  default: false
37
26
  },
38
- color: {
39
- type: String,
40
- default: void 0
41
- },
42
- selected: {
43
- type: Boolean,
44
- default: false
45
- },
46
- disabled: {
47
- type: Boolean,
48
- default: false
49
- },
50
- name: {
51
- type: String,
52
- default: void 0
53
- },
54
- value: {
55
- type: [String, Number],
56
- default: void 0
57
- },
58
- onClick: {
59
- type: Function,
60
- default: void 0
61
- },
62
- onChange: {
27
+ onClose: {
63
28
  type: Function,
64
29
  default: void 0
65
30
  },
66
- onClose: {
31
+ onAdd: {
67
32
  type: Function,
68
33
  default: void 0
69
34
  }
@@ -73,68 +38,104 @@ const Tag = /* @__PURE__ */ defineComponent({
73
38
  slots,
74
39
  attrs
75
40
  }) {
76
- const resolvedSize = computed(() => props.lg ? "lg" : props.size);
77
- const customColor = computed(() => props.color ? {
78
- "--ui-tag-bg": `color-mix(in srgb, ${props.color} 14%, white)`,
79
- color: props.color
80
- } : void 0);
81
- const classes = () => ["mh-tag", resolvedSize.value === "md" ? void 0 : `mh-tag--${resolvedSize.value}`, props.shape !== "regular" ? `mh-tag--${props.shape}` : void 0, props.shape === "status" && props.tone ? `mh-tag--${props.tone}` : void 0, props.outline ? "mh-tag--outline" : void 0, props.shape === "select" && props.selected ? "is-selected" : void 0, props.disabled ? "is-disabled" : void 0, attrs.class].filter(Boolean).join(" ");
82
- const content = () => createVNode(Fragment, null, [props.icon ? createVNode(Icon, {
83
- "name": props.icon,
84
- "class": "mh-tag__icon"
85
- }, null) : null, slots.default?.(), props.closable ? createVNode("button", {
86
- "type": "button",
87
- "class": "mh-tag__close",
88
- "aria-label": "关闭",
89
- "tabindex": -1,
90
- "onClick": (event) => {
91
- event.stopPropagation();
92
- event.preventDefault();
93
- props.onClose?.();
41
+ const isAdd = computed(() => props.shape === "add");
42
+ const baseShape = computed(() => props.shape === "add" ? "plain" : props.shape);
43
+ const editing = ref(false);
44
+ const draft = ref("");
45
+ const editorRef = ref(null);
46
+ const startEdit = () => {
47
+ draft.value = "";
48
+ editing.value = true;
49
+ };
50
+ const stopEdit = () => {
51
+ editing.value = false;
52
+ draft.value = "";
53
+ };
54
+ const submit = () => {
55
+ const name = draft.value.trim();
56
+ if (!name) return;
57
+ props.onAdd?.(name);
58
+ stopEdit();
59
+ };
60
+ watch(editing, (on) => {
61
+ if (!on) return;
62
+ editorRef.value?.querySelector("input")?.focus();
63
+ }, {
64
+ flush: "post"
65
+ });
66
+ const onEditorKeydown = (event) => {
67
+ if (event.key !== "Escape") return;
68
+ event.stopPropagation();
69
+ stopEdit();
70
+ };
71
+ const onEditorFocusout = (event) => {
72
+ const next = event.relatedTarget;
73
+ if (next && editorRef.value?.contains(next)) return;
74
+ stopEdit();
75
+ };
76
+ const forwardAttrs = computed(() => {
77
+ const rest = {};
78
+ for (const [key, value] of Object.entries(attrs)) {
79
+ if (key !== "class" && key !== "style") rest[key] = value;
94
80
  }
95
- }, [createVNode(Icon, {
96
- "name": "close"
97
- }, null)]) : null]);
81
+ if (isAdd.value) {
82
+ const consumer = rest.onClick;
83
+ rest.onClick = (event) => {
84
+ consumer?.(event);
85
+ startEdit();
86
+ };
87
+ }
88
+ return rest;
89
+ });
98
90
  return () => {
99
- let _slot;
100
- const style = {
101
- ...customColor.value,
102
- ...attrs.style
103
- };
104
- if (props.shape === "add") {
105
- return createVNode("button", {
91
+ if (isAdd.value && editing.value) {
92
+ return createVNode("span", {
93
+ "ref": editorRef,
94
+ "class": "mh-tag mh-tag--editing",
95
+ "onKeydown": onEditorKeydown,
96
+ "onFocusout": onEditorFocusout
97
+ }, [createVNode(Input, {
98
+ "class": "mh-tag__input",
99
+ "size": "sm",
100
+ "modelValue": draft.value,
101
+ "placeholder": "输入标签名",
102
+ "aria-label": "标签名",
103
+ "onChange": (value) => draft.value = value,
104
+ "onEnter": () => submit()
105
+ }, null), createVNode("button", {
106
106
  "type": "button",
107
- "class": classes(),
108
- "style": style,
109
- "onClick": props.onClick,
110
- "disabled": props.disabled
107
+ "class": "mh-tag__confirm",
108
+ "aria-label": "添加",
109
+ "tabindex": -1,
110
+ "onMousedown": (event) => event.preventDefault(),
111
+ "onClick": () => submit()
111
112
  }, [createVNode(Icon, {
112
- "name": "plus",
113
- "size": 14
114
- }, null), slots.default?.()]);
115
- }
116
- if (props.shape === "select") {
117
- return createVNode("label", {
118
- "class": classes(),
119
- "style": style
120
- }, [createVNode("input", {
121
- "type": "radio",
122
- "name": props.name,
123
- "value": props.value,
124
- "checked": props.selected,
125
- "disabled": props.disabled,
126
- "onChange": (event) => props.onChange?.(event.target.checked)
127
- }, null), content()]);
113
+ "name": "plus"
114
+ }, null)])]);
128
115
  }
129
- const Clickable = props.shape === "chip" ? "button" : "span";
130
- return createVNode(Clickable, mergeProps({
131
- "class": classes(),
132
- "style": style
133
- }, props.shape === "chip" ? {
134
- type: "button",
135
- onClick: props.onClick
136
- } : {}), _isSlot(_slot = content()) ? _slot : {
137
- default: () => [_slot]
116
+ const extraClass = attrs.class;
117
+ return createVNode(Badge, mergeProps(forwardAttrs.value, {
118
+ "as": isAdd.value ? "button" : "span",
119
+ "tone": props.tone,
120
+ "shape": baseShape.value,
121
+ "size": props.size,
122
+ "class": ["mh-tag", isAdd.value ? "mh-tag--add" : void 0, extraClass].filter(Boolean).join(" "),
123
+ "style": attrs.style
124
+ }), {
125
+ default: () => createVNode(Fragment, null, [isAdd.value ? createVNode(Icon, {
126
+ "name": "plus"
127
+ }, null) : null, slots.default?.(), props.closable && !isAdd.value ? createVNode("button", {
128
+ "type": "button",
129
+ "class": "mh-tag__close",
130
+ "aria-label": "关闭",
131
+ "tabindex": -1,
132
+ "onClick": (event) => {
133
+ event.stopPropagation();
134
+ props.onClose?.();
135
+ }
136
+ }, [createVNode(Icon, {
137
+ "name": "close"
138
+ }, null)]) : null])
138
139
  });
139
140
  };
140
141
  }
@@ -118,9 +118,9 @@ export declare const Text: import("vue").DefineComponent<import("vue").ExtractPr
118
118
  size: TextSize;
119
119
  weight: TextWeight;
120
120
  tone: TextTone;
121
+ as: TextTag;
121
122
  mono: boolean;
122
123
  truncate: number | boolean;
123
- as: TextTag;
124
124
  }, SlotsType<{
125
125
  default?: () => VNodeChild;
126
126
  }>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -115,10 +115,10 @@ export declare const Tooltip: import("vue").DefineComponent<import("vue").Extrac
115
115
  title: string;
116
116
  align: TooltipAlign;
117
117
  action: string;
118
- onAction: () => void;
119
118
  side: TooltipSide;
120
119
  trigger: TooltipTrigger;
121
120
  guide: boolean;
121
+ onAction: () => void;
122
122
  }, SlotsType<{
123
123
  default?: () => VNodeChild;
124
124
  content?: () => VNodeChild;
@@ -43,12 +43,25 @@ export interface TreeNode {
43
43
  * 应用侧只给节点;操作容器自己做 `stopPropagation`,所以点「删除」不会顺带把这一行选中。
44
44
  * 导航件(`Nav`)因此不必重写一份行渲染,把数据与 `renderActions` 透传进来即可。
45
45
  *
46
+ * ## 图标出口(两处,都是「位」而不是「样式」)
47
+ *
48
+ * 行内的图标位只有两处,各自独立,缺省都不改变现有几何:
49
+ * - `renderCaret(node, expanded)` 替掉**箭头字形**(槽位 / 点击 / `data-expanded` 仍归组件);
50
+ * - `renderIcon(node)` 是标签**前**的节点图标槽(文件夹 / 文件一类),父子节点共用一位。
51
+ *
52
+ * 不在其中之列的(标题里的图标 + 文字)走 `renderLabel`:那属于标签**本体**。
53
+ *
46
54
  * @example 受控展开 + 计数徽标 + 行尾操作
47
55
  * <Tree nodes={treeNodes.value} rowKey="id" labelKey="name"
48
56
  * selected={selectedId.value} onSelect={(n) => select(n.id)}
49
57
  * expandedKeys={expandedKeys.value} onExpandChange={(k) => (expandedKeys.value = k)}
50
58
  * renderNode={(n) => <Badge>{countOfDomain(n.id)}</Badge>}
51
59
  * renderActions={(n) => <Button title="删除" onClick={() => removeDomain(n.id)} />} />
60
+ *
61
+ * @example 文件夹 / 文件图标 + 方向字形自表达(不靠旋转)
62
+ * <Tree nodes={tree.value} rowKey="id"
63
+ * renderIcon={(n) => <Icon name={hasChild(n) ? 'folder' : 'file-text'} />}
64
+ * renderCaret={(_n, expanded) => <Icon name={expanded ? 'chevron-down' : 'chevron-right'} />} />
52
65
  */
53
66
  export declare const Tree: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
54
67
  nodes: {
@@ -92,6 +105,31 @@ export declare const Tree: import("vue").DefineComponent<import("vue").ExtractPr
92
105
  type: PropType<(node: TreeNode) => unknown>;
93
106
  default: undefined;
94
107
  };
108
+ /**
109
+ * 展开 / 收起箭头的自定义渲染(拿到节点与**当前展开态**)。
110
+ *
111
+ * 缺省仍是 `chevron-down` 字形 + 面层收起态转 −90°(默认路径零变化)。
112
+ * 自定义时**朝向由消费方按 `expanded` 自选字形**(`chevron-right` / `chevron-down`)——
113
+ * 与包内「方位一律用字形、不靠旋转别的图标」的口径同源;**不要再给内容加
114
+ * `mh-tree__caret-icon` 类**(那是缺省字形的转向钩子,加上会被转成 −90°)。
115
+ * 尺寸不必自管:槽位与槽内字形都取全局图标档 `--ui-font-icon`。
116
+ */
117
+ renderCaret: {
118
+ type: PropType<(node: TreeNode, expanded: boolean) => unknown>;
119
+ default: undefined;
120
+ };
121
+ /**
122
+ * 标签**前**的节点图标槽(文件夹 / 文件一类)。
123
+ *
124
+ * 父节点与叶子节点**都由消费方按 `node` 判定**(组件不替它猜「是不是叶子」:同一个图标槽
125
+ * 在两种节点上都成立,判定依据是数据而不是渲染期形态)。缺省不渲染该槽,几何零变化。
126
+ * 叶子行的箭头占位仍保留,故图标列跨层级对齐。颜色不写死:跟随行色,
127
+ * 选中行的主色字与滑过态自动同步。
128
+ */
129
+ renderIcon: {
130
+ type: PropType<(node: TreeNode) => unknown>;
131
+ default: undefined;
132
+ };
95
133
  /** 节点附加内容(计数徽标等),渲染在标签之后 */
96
134
  renderNode: {
97
135
  type: PropType<(node: TreeNode) => unknown>;
@@ -225,6 +263,31 @@ export declare const Tree: import("vue").DefineComponent<import("vue").ExtractPr
225
263
  type: PropType<(node: TreeNode) => unknown>;
226
264
  default: undefined;
227
265
  };
266
+ /**
267
+ * 展开 / 收起箭头的自定义渲染(拿到节点与**当前展开态**)。
268
+ *
269
+ * 缺省仍是 `chevron-down` 字形 + 面层收起态转 −90°(默认路径零变化)。
270
+ * 自定义时**朝向由消费方按 `expanded` 自选字形**(`chevron-right` / `chevron-down`)——
271
+ * 与包内「方位一律用字形、不靠旋转别的图标」的口径同源;**不要再给内容加
272
+ * `mh-tree__caret-icon` 类**(那是缺省字形的转向钩子,加上会被转成 −90°)。
273
+ * 尺寸不必自管:槽位与槽内字形都取全局图标档 `--ui-font-icon`。
274
+ */
275
+ renderCaret: {
276
+ type: PropType<(node: TreeNode, expanded: boolean) => unknown>;
277
+ default: undefined;
278
+ };
279
+ /**
280
+ * 标签**前**的节点图标槽(文件夹 / 文件一类)。
281
+ *
282
+ * 父节点与叶子节点**都由消费方按 `node` 判定**(组件不替它猜「是不是叶子」:同一个图标槽
283
+ * 在两种节点上都成立,判定依据是数据而不是渲染期形态)。缺省不渲染该槽,几何零变化。
284
+ * 叶子行的箭头占位仍保留,故图标列跨层级对齐。颜色不写死:跟随行色,
285
+ * 选中行的主色字与滑过态自动同步。
286
+ */
287
+ renderIcon: {
288
+ type: PropType<(node: TreeNode) => unknown>;
289
+ default: undefined;
290
+ };
228
291
  /** 节点附加内容(计数徽标等),渲染在标签之后 */
229
292
  renderNode: {
230
293
  type: PropType<(node: TreeNode) => unknown>;
@@ -331,6 +394,8 @@ export declare const Tree: import("vue").DefineComponent<import("vue").ExtractPr
331
394
  labelKey: string;
332
395
  childrenKey: string;
333
396
  renderLabel: (node: TreeNode) => unknown;
397
+ renderCaret: (node: TreeNode, expanded: boolean) => unknown;
398
+ renderIcon: (node: TreeNode) => unknown;
334
399
  renderNode: (node: TreeNode) => unknown;
335
400
  renderActions: (node: TreeNode) => unknown;
336
401
  expandedKeys: string[];
@@ -46,6 +46,31 @@ const Tree = /* @__PURE__ */ defineComponent({
46
46
  type: Function,
47
47
  default: void 0
48
48
  },
49
+ /**
50
+ * 展开 / 收起箭头的自定义渲染(拿到节点与**当前展开态**)。
51
+ *
52
+ * 缺省仍是 `chevron-down` 字形 + 面层收起态转 −90°(默认路径零变化)。
53
+ * 自定义时**朝向由消费方按 `expanded` 自选字形**(`chevron-right` / `chevron-down`)——
54
+ * 与包内「方位一律用字形、不靠旋转别的图标」的口径同源;**不要再给内容加
55
+ * `mh-tree__caret-icon` 类**(那是缺省字形的转向钩子,加上会被转成 −90°)。
56
+ * 尺寸不必自管:槽位与槽内字形都取全局图标档 `--ui-font-icon`。
57
+ */
58
+ renderCaret: {
59
+ type: Function,
60
+ default: void 0
61
+ },
62
+ /**
63
+ * 标签**前**的节点图标槽(文件夹 / 文件一类)。
64
+ *
65
+ * 父节点与叶子节点**都由消费方按 `node` 判定**(组件不替它猜「是不是叶子」:同一个图标槽
66
+ * 在两种节点上都成立,判定依据是数据而不是渲染期形态)。缺省不渲染该槽,几何零变化。
67
+ * 叶子行的箭头占位仍保留,故图标列跨层级对齐。颜色不写死:跟随行色,
68
+ * 选中行的主色字与滑过态自动同步。
69
+ */
70
+ renderIcon: {
71
+ type: Function,
72
+ default: void 0
73
+ },
49
74
  /** 节点附加内容(计数徽标等),渲染在标签之后 */
50
75
  renderNode: {
51
76
  type: Function,
@@ -237,12 +262,14 @@ const Tree = /* @__PURE__ */ defineComponent({
237
262
  event.stopPropagation();
238
263
  toggleExpand(key);
239
264
  }
240
- }, [createVNode(Icon, {
265
+ }, [props.renderCaret ? props.renderCaret(node, expanded) : createVNode(Icon, {
241
266
  "name": "chevron-down",
242
267
  "class": "mh-tree__caret-icon"
243
268
  }, null)]) : createVNode("span", {
244
269
  "class": "mh-tree__caret mh-tree__caret--empty"
245
- }, null), createVNode("span", {
270
+ }, null), props.renderIcon ? createVNode("span", {
271
+ "class": "mh-tree__icon"
272
+ }, [props.renderIcon(node)]) : null, createVNode("span", {
246
273
  "class": "mh-tree__label"
247
274
  }, [props.renderLabel ? props.renderLabel(node) : String(node[props.labelKey] ?? "")]), props.renderNode ? createVNode("span", {
248
275
  "class": "mh-tree__extra"
package/dist/index.d.ts CHANGED
@@ -26,7 +26,7 @@ export { Layout, type LayoutGap, type LayoutAlign, type LayoutJustify, type Layo
26
26
  export { Avatar, type AvatarProps, type AvatarSize, type AvatarCompound } from './components/avatar';
27
27
  export { Skeleton, type SkeletonProps } from './components/skeleton';
28
28
  export { Radio, type RadioProps, type RadioCompound, type RadioCompoundType, type RadioCardItem, type RadioCardValue, } from './components/radio';
29
- export { Tag, type TagProps } from './components/tag';
29
+ export { Tag, type TagProps, type TagShape } from './components/tag';
30
30
  export { Text, type TextProps, type TextSize, type TextTone, type TextWeight, type TextTag, } from './components/text';
31
31
  export { Tree, type TreeNode } from './components/tree';
32
32
  export { ListView, type ListViewProps, type ListViewStatus, type ListViewStatusTone, type ListViewCardItemProps, type ListViewCompound, } from './components/list-view';