@manohub/ui 0.7.0 → 0.8.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 (75) hide show
  1. package/README.md +4 -4
  2. package/dist/components/button/index.d.ts +14 -8
  3. package/dist/components/capsule/index.d.ts +13 -9
  4. package/dist/components/capsule/index.js +1 -1
  5. package/dist/components/card/index.d.ts +1 -1
  6. package/dist/components/card/index.js +7 -4
  7. package/dist/components/checkbox/index.js +1 -0
  8. package/dist/components/dialog/index.d.ts +1 -1
  9. package/dist/components/form/index.d.ts +142 -62
  10. package/dist/components/form/index.js +41 -29
  11. package/dist/components/input/index.d.ts +74 -6
  12. package/dist/components/input/index.js +13 -1
  13. package/dist/components/layout/index.d.ts +43 -1
  14. package/dist/components/layout/index.js +22 -2
  15. package/dist/components/list-view/index.d.ts +1 -1
  16. package/dist/components/notice/index.d.ts +2 -2
  17. package/dist/components/notification/index.d.ts +2 -2
  18. package/dist/components/page/index.js +4 -4
  19. package/dist/components/pagination/index.d.ts +2 -2
  20. package/dist/components/pagination/index.js +1 -1
  21. package/dist/components/panel/index.js +4 -4
  22. package/dist/components/part-padding.d.ts +9 -6
  23. package/dist/components/part-padding.js +2 -1
  24. package/dist/components/query-state/index.d.ts +18 -2
  25. package/dist/components/radio/index.d.ts +1 -1
  26. package/dist/components/radio/index.js +1 -0
  27. package/dist/components/select/index.d.ts +32 -30
  28. package/dist/components/select/index.js +46 -82
  29. package/dist/components/select/select-list.d.ts +125 -0
  30. package/dist/components/select/select-list.js +95 -0
  31. package/dist/components/select/select-panel.d.ts +106 -0
  32. package/dist/components/select/select-panel.js +72 -0
  33. package/dist/components/select/select-shared.d.ts +64 -0
  34. package/dist/components/select/select-shared.js +24 -0
  35. package/dist/components/select/select-tree-filter.d.ts +61 -0
  36. package/dist/components/select/select-tree-filter.js +63 -0
  37. package/dist/components/select/select-tree.d.ts +274 -0
  38. package/dist/components/select/select-tree.js +263 -0
  39. package/dist/components/select/select-trigger.d.ts +132 -0
  40. package/dist/components/select/select-trigger.js +101 -0
  41. package/dist/components/steps/index.d.ts +232 -6
  42. package/dist/components/steps/index.js +84 -26
  43. package/dist/components/switch/index.d.ts +29 -5
  44. package/dist/components/switch/index.js +8 -2
  45. package/dist/components/tabbar/index.d.ts +184 -0
  46. package/dist/components/tabbar/index.js +161 -0
  47. package/dist/components/table/index.d.ts +99 -12
  48. package/dist/components/table/index.js +69 -16
  49. package/dist/components/tabs/index.d.ts +142 -126
  50. package/dist/components/tabs/index.js +87 -119
  51. package/dist/components/tag/index.d.ts +2 -2
  52. package/dist/components/text/index.d.ts +7 -2
  53. package/dist/components/toast/index.d.ts +8 -8
  54. package/dist/components/tree/index.d.ts +28 -2
  55. package/dist/components/tree/index.js +14 -1
  56. package/dist/index.d.ts +6 -6
  57. package/dist/index.js +6 -3
  58. package/dist/styles/components/capsule.css +4 -4
  59. package/dist/styles/components/form.css +198 -39
  60. package/dist/styles/components/input.css +53 -1
  61. package/dist/styles/components/layout.css +2 -0
  62. package/dist/styles/components/select.css +21 -0
  63. package/dist/styles/components/select.tokens.css +3 -0
  64. package/dist/styles/components/steps.css +48 -0
  65. package/dist/styles/components/{tabs.css → tabbar.css} +66 -65
  66. package/dist/styles/components/tabbar.tokens.css +30 -0
  67. package/dist/styles/components/table.css +50 -1
  68. package/dist/styles/components/table.tokens.css +3 -0
  69. package/dist/styles/index.css +3 -3
  70. package/dist/styles/operations/tabs.css +73 -0
  71. package/package.json +2 -2
  72. package/dist/components/tabset/index.d.ts +0 -109
  73. package/dist/components/tabset/index.js +0 -75
  74. package/dist/styles/components/tabs.tokens.css +0 -30
  75. package/dist/styles/operations/tabset.css +0 -57
@@ -0,0 +1,263 @@
1
+ import { defineComponent, ref, computed, watch, createVNode, createTextVNode, Fragment } from "vue";
2
+ import { Tree } from "../tree/index.js";
3
+ import { SelectPanel } from "./select-panel.js";
4
+ import { SelectTrigger } from "./select-trigger.js";
5
+ import { createSelectPanelId, createTriggerKeydown, showPanel, hidePanel } from "./select-shared.js";
6
+ import { filterTreeNodes, findTreeNode, treeLabel, splitHighlight, treeChildren, treeNodeKey } from "./select-tree-filter.js";
7
+ const SelectTree = /* @__PURE__ */ defineComponent({
8
+ name: "SelectTree",
9
+ inheritAttrs: false,
10
+ props: {
11
+ /** 树数据(与 `Tree` 同一个 `TreeNode` 类型,不另造节点形状) */
12
+ nodes: {
13
+ type: Array,
14
+ required: true
15
+ },
16
+ /** 节点主键:字段名或取值函数(与 `Tree` 同义) */
17
+ rowKey: {
18
+ type: [String, Function],
19
+ required: true
20
+ },
21
+ /** 标签字段名(默认 'label') */
22
+ labelKey: {
23
+ type: String,
24
+ default: "label"
25
+ },
26
+ /** 子节点字段名(默认 'children') */
27
+ childrenKey: {
28
+ type: String,
29
+ default: "children"
30
+ },
31
+ /** 受控值(选中叶子的主键;树选择只单选) */
32
+ modelValue: {
33
+ type: String,
34
+ default: void 0
35
+ },
36
+ /** 非受控初始值 */
37
+ defaultValue: {
38
+ type: String,
39
+ default: void 0
40
+ },
41
+ placeholder: {
42
+ type: String,
43
+ default: "请选择"
44
+ },
45
+ /** 搜索:`true` 强制开;不给时节点总数多于 8 自动开(与 `Select` 同口径) */
46
+ searchable: {
47
+ type: Boolean,
48
+ default: false
49
+ },
50
+ /** 自定义命中判定(缺省:标签区分大小写地包含关键词) */
51
+ filterNode: {
52
+ type: Function,
53
+ default: void 0
54
+ },
55
+ /** 命中文本是否高亮(默认开) */
56
+ highlight: {
57
+ type: Boolean,
58
+ default: true
59
+ },
60
+ /** 受控展开集(传了就完全受控;**过滤态下组件强制全展**,不受本 prop 约束) */
61
+ expandedKeys: {
62
+ type: Array,
63
+ default: void 0
64
+ },
65
+ /** 非受控展开初值 */
66
+ defaultExpandedKeys: {
67
+ type: Array,
68
+ default: () => []
69
+ },
70
+ /** 初始展开全部(与 `defaultExpandedKeys` 互斥,它优先) */
71
+ defaultExpandAll: {
72
+ type: Boolean,
73
+ default: false
74
+ },
75
+ /** 展开集变化回调 */
76
+ onExpandChange: {
77
+ type: Function,
78
+ default: void 0
79
+ },
80
+ clearable: {
81
+ type: Boolean,
82
+ default: false
83
+ },
84
+ disabled: {
85
+ type: Boolean,
86
+ default: false
87
+ },
88
+ /** 加载中(面板里出一行提示,不渲染树) */
89
+ loading: {
90
+ type: Boolean,
91
+ default: false
92
+ },
93
+ emptyText: {
94
+ type: String,
95
+ default: "暂无数据"
96
+ },
97
+ /** 尺寸档(24 / 28 / 32) */
98
+ size: {
99
+ type: String,
100
+ default: "md"
101
+ },
102
+ /** 校验状态(红 / 黄;与 `Input` 同一词表) */
103
+ status: {
104
+ type: String,
105
+ default: void 0
106
+ },
107
+ /** 值变更回调(选中叶子的主键) */
108
+ onChange: {
109
+ type: Function,
110
+ default: void 0
111
+ },
112
+ /** 搜索词变更(远程换数据用;本地过滤不受它影响) */
113
+ onSearch: {
114
+ type: Function,
115
+ default: void 0
116
+ }
117
+ },
118
+ emits: {
119
+ "update:modelValue": (v) => true
120
+ },
121
+ slots: Object,
122
+ setup(props, {
123
+ emit,
124
+ slots,
125
+ attrs
126
+ }) {
127
+ const panelId = createSelectPanelId();
128
+ const panelRef = ref(null);
129
+ const inner = ref(props.defaultValue);
130
+ const keyword = ref("");
131
+ const keyOf = (node) => treeNodeKey(node, props.rowKey);
132
+ const childrenOf = (node) => treeChildren(node, props.childrenKey);
133
+ const current = computed(() => props.modelValue ?? inner.value);
134
+ const hasValue = computed(() => current.value !== void 0 && current.value !== "");
135
+ const nodeCount = computed(() => {
136
+ let count = 0;
137
+ const walk = (list) => {
138
+ for (const node of list) {
139
+ count += 1;
140
+ walk(childrenOf(node));
141
+ }
142
+ };
143
+ walk(props.nodes);
144
+ return count;
145
+ });
146
+ const showSearch = computed(() => props.searchable || nodeCount.value > 8);
147
+ const collectKeys = (list) => list.flatMap((node) => [keyOf(node), ...collectKeys(childrenOf(node))]);
148
+ const innerExpanded = ref(props.defaultExpandAll ? collectKeys(props.nodes) : [...props.defaultExpandedKeys]);
149
+ watch(() => props.defaultExpandedKeys, (next) => {
150
+ if (props.expandedKeys === void 0) innerExpanded.value = [...next];
151
+ });
152
+ const filtered = computed(() => keyword.value ? filterTreeNodes(props.nodes, {
153
+ rowKey: props.rowKey,
154
+ childrenKey: props.childrenKey,
155
+ labelKey: props.labelKey,
156
+ keyword: keyword.value,
157
+ match: props.filterNode
158
+ }) : void 0);
159
+ const visibleNodes = computed(() => filtered.value?.nodes ?? props.nodes);
160
+ const expandedKeys = computed(() => filtered.value ? filtered.value.keys : props.expandedKeys ?? innerExpanded.value);
161
+ const handleExpandChange = (keys) => {
162
+ if (props.expandedKeys === void 0) innerExpanded.value = keys;
163
+ props.onExpandChange?.(keys);
164
+ };
165
+ const displayLabel = computed(() => {
166
+ if (!hasValue.value) return void 0;
167
+ const found = findTreeNode(props.nodes, String(current.value), {
168
+ rowKey: props.rowKey,
169
+ childrenKey: props.childrenKey
170
+ });
171
+ return found ? treeLabel(found, props.labelKey) : String(current.value);
172
+ });
173
+ const commit = (value) => {
174
+ if (props.modelValue === void 0) inner.value = value;
175
+ emit("update:modelValue", value);
176
+ props.onChange?.(value);
177
+ };
178
+ const hide = () => hidePanel(panelRef.value);
179
+ const toggleExpand = (key) => {
180
+ const next = new Set(expandedKeys.value);
181
+ if (next.has(key)) next.delete(key);
182
+ else next.add(key);
183
+ handleExpandChange([...next]);
184
+ };
185
+ const pick = (node) => {
186
+ if (childrenOf(node).length > 0) {
187
+ toggleExpand(keyOf(node));
188
+ return;
189
+ }
190
+ commit(keyOf(node));
191
+ hide();
192
+ };
193
+ const clear = () => commit("");
194
+ const onTriggerKeydown = createTriggerKeydown({
195
+ clearable: props.clearable,
196
+ disabled: props.disabled,
197
+ clear,
198
+ openPanel: () => showPanel(panelRef.value)
199
+ /* 有意不给 `focusFirst`:树的行是 `div`、没有 tabindex,面板内不做 ↑↓ 行导航 */
200
+ });
201
+ const renderHitLabel = (node) => createVNode(Fragment, null, [splitHighlight(treeLabel(node, props.labelKey), keyword.value).map((segment, index) => segment.hit ? createVNode("span", {
202
+ "key": index,
203
+ "class": "mh-select__hit"
204
+ }, [segment.text]) : segment.text)]);
205
+ return () => {
206
+ const highlightLabel = props.highlight && !!keyword.value ? renderHitLabel : void 0;
207
+ return createVNode("div", {
208
+ "class": ["mh-popup-host", attrs.class].filter(Boolean).join(" "),
209
+ "style": attrs.style
210
+ }, [createVNode(SelectTrigger, {
211
+ "panelId": panelId,
212
+ "size": props.size,
213
+ "status": props.status,
214
+ "disabled": props.disabled,
215
+ "clearable": props.clearable,
216
+ "hasValue": hasValue.value,
217
+ "placeholder": props.placeholder,
218
+ "label": displayLabel.value,
219
+ "onKeydown": onTriggerKeydown,
220
+ "onClear": clear
221
+ }, null), createVNode(SelectPanel, {
222
+ "id": panelId,
223
+ "elementRef": panelRef,
224
+ "searchable": showSearch.value,
225
+ "keyword": keyword.value,
226
+ "onKeyword": (value) => {
227
+ keyword.value = value;
228
+ props.onSearch?.(value);
229
+ }
230
+ }, {
231
+ default: () => {
232
+ if (props.loading) {
233
+ return createVNode("span", {
234
+ "class": "mh-select__option mh-select__option--hint"
235
+ }, [createTextVNode("加载中…")]);
236
+ }
237
+ if (visibleNodes.value.length === 0) {
238
+ const custom = slots.empty?.();
239
+ return custom !== void 0 && custom !== null ? custom : createVNode("span", {
240
+ "class": "mh-select__option mh-select__option--empty"
241
+ }, [props.emptyText]);
242
+ }
243
+ return createVNode("div", {
244
+ "class": "mh-select__tree"
245
+ }, [createVNode(Tree, {
246
+ "nodes": visibleNodes.value,
247
+ "rowKey": props.rowKey,
248
+ "labelKey": props.labelKey,
249
+ "childrenKey": props.childrenKey,
250
+ "selected": current.value === "" ? void 0 : current.value,
251
+ "expandedKeys": expandedKeys.value,
252
+ "onExpandChange": handleExpandChange,
253
+ "onSelect": pick,
254
+ "renderLabel": highlightLabel
255
+ }, null)]);
256
+ }
257
+ })]);
258
+ };
259
+ }
260
+ });
261
+ export {
262
+ SelectTree
263
+ };
@@ -0,0 +1,132 @@
1
+ import { type PropType, type VNodeChild } from 'vue';
2
+ import { type InputStatus } from '../input';
3
+ import type { ControlSize } from '../control-size';
4
+ export declare const SelectTrigger: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
5
+ /** 面板 id(`popovertarget` 的目标,由 `createSelectPanelId()` 产出的全页唯一 id) */
6
+ panelId: {
7
+ type: StringConstructor;
8
+ required: true;
9
+ };
10
+ /** 尺寸档(24 / 28 / 32):框高与字号归输入面,本件只透传 */
11
+ size: {
12
+ type: PropType<ControlSize>;
13
+ default: string;
14
+ };
15
+ /** 校验状态(红 / 黄;与输入框同一词表) */
16
+ status: {
17
+ type: PropType<InputStatus>;
18
+ default: undefined;
19
+ };
20
+ disabled: {
21
+ type: BooleanConstructor;
22
+ default: boolean;
23
+ };
24
+ clearable: {
25
+ type: BooleanConstructor;
26
+ default: boolean;
27
+ };
28
+ /** 显式判定清除位显隐(多选的值是数组、单选显示的是 label,都没有「一个字符串模型」可喂) */
29
+ hasValue: {
30
+ type: BooleanConstructor;
31
+ default: boolean;
32
+ };
33
+ /** 多选形态(控件上多一个 `mh-select--multiple`,标签不换行) */
34
+ multiple: {
35
+ type: BooleanConstructor;
36
+ default: boolean;
37
+ };
38
+ placeholder: {
39
+ type: StringConstructor;
40
+ default: string;
41
+ };
42
+ /** 单选显示文本(无值时不渲染它,改渲染 `placeholder`) */
43
+ label: {
44
+ type: StringConstructor;
45
+ default: undefined;
46
+ };
47
+ /** 触发器上的键盘契约(`createTriggerKeydown()` 产出,两件同源) */
48
+ onKeydown: {
49
+ type: PropType<(event: KeyboardEvent) => void>;
50
+ default: undefined;
51
+ };
52
+ /** 清除位被点击(输入面的内建清除位回调;本件只把它转给消费方) */
53
+ onClear: {
54
+ type: PropType<() => void>;
55
+ default: undefined;
56
+ };
57
+ /** 值区自定义渲染(多选标签收纳用;不给则走单选那一行) */
58
+ renderValue: {
59
+ type: PropType<() => VNodeChild>;
60
+ default: undefined;
61
+ };
62
+ }>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
63
+ /** 面板 id(`popovertarget` 的目标,由 `createSelectPanelId()` 产出的全页唯一 id) */
64
+ panelId: {
65
+ type: StringConstructor;
66
+ required: true;
67
+ };
68
+ /** 尺寸档(24 / 28 / 32):框高与字号归输入面,本件只透传 */
69
+ size: {
70
+ type: PropType<ControlSize>;
71
+ default: string;
72
+ };
73
+ /** 校验状态(红 / 黄;与输入框同一词表) */
74
+ status: {
75
+ type: PropType<InputStatus>;
76
+ default: undefined;
77
+ };
78
+ disabled: {
79
+ type: BooleanConstructor;
80
+ default: boolean;
81
+ };
82
+ clearable: {
83
+ type: BooleanConstructor;
84
+ default: boolean;
85
+ };
86
+ /** 显式判定清除位显隐(多选的值是数组、单选显示的是 label,都没有「一个字符串模型」可喂) */
87
+ hasValue: {
88
+ type: BooleanConstructor;
89
+ default: boolean;
90
+ };
91
+ /** 多选形态(控件上多一个 `mh-select--multiple`,标签不换行) */
92
+ multiple: {
93
+ type: BooleanConstructor;
94
+ default: boolean;
95
+ };
96
+ placeholder: {
97
+ type: StringConstructor;
98
+ default: string;
99
+ };
100
+ /** 单选显示文本(无值时不渲染它,改渲染 `placeholder`) */
101
+ label: {
102
+ type: StringConstructor;
103
+ default: undefined;
104
+ };
105
+ /** 触发器上的键盘契约(`createTriggerKeydown()` 产出,两件同源) */
106
+ onKeydown: {
107
+ type: PropType<(event: KeyboardEvent) => void>;
108
+ default: undefined;
109
+ };
110
+ /** 清除位被点击(输入面的内建清除位回调;本件只把它转给消费方) */
111
+ onClear: {
112
+ type: PropType<() => void>;
113
+ default: undefined;
114
+ };
115
+ /** 值区自定义渲染(多选标签收纳用;不给则走单选那一行) */
116
+ renderValue: {
117
+ type: PropType<() => VNodeChild>;
118
+ default: undefined;
119
+ };
120
+ }>> & Readonly<{}>, {
121
+ size: ControlSize;
122
+ multiple: boolean;
123
+ disabled: boolean;
124
+ label: string;
125
+ onKeydown: (event: KeyboardEvent) => void;
126
+ placeholder: string;
127
+ status: InputStatus;
128
+ clearable: boolean;
129
+ hasValue: boolean;
130
+ onClear: () => void;
131
+ renderValue: () => VNodeChild;
132
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -0,0 +1,101 @@
1
+ import { defineComponent, createVNode } from "vue";
2
+ import { Icon } from "@manohub/icon";
3
+ import { Input } from "../input/index.js";
4
+ const SelectTrigger = /* @__PURE__ */ defineComponent({
5
+ name: "SelectTrigger",
6
+ inheritAttrs: false,
7
+ props: {
8
+ /** 面板 id(`popovertarget` 的目标,由 `createSelectPanelId()` 产出的全页唯一 id) */
9
+ panelId: {
10
+ type: String,
11
+ required: true
12
+ },
13
+ /** 尺寸档(24 / 28 / 32):框高与字号归输入面,本件只透传 */
14
+ size: {
15
+ type: String,
16
+ default: "md"
17
+ },
18
+ /** 校验状态(红 / 黄;与输入框同一词表) */
19
+ status: {
20
+ type: String,
21
+ default: void 0
22
+ },
23
+ disabled: {
24
+ type: Boolean,
25
+ default: false
26
+ },
27
+ clearable: {
28
+ type: Boolean,
29
+ default: false
30
+ },
31
+ /** 显式判定清除位显隐(多选的值是数组、单选显示的是 label,都没有「一个字符串模型」可喂) */
32
+ hasValue: {
33
+ type: Boolean,
34
+ default: false
35
+ },
36
+ /** 多选形态(控件上多一个 `mh-select--multiple`,标签不换行) */
37
+ multiple: {
38
+ type: Boolean,
39
+ default: false
40
+ },
41
+ placeholder: {
42
+ type: String,
43
+ default: "请选择"
44
+ },
45
+ /** 单选显示文本(无值时不渲染它,改渲染 `placeholder`) */
46
+ label: {
47
+ type: String,
48
+ default: void 0
49
+ },
50
+ /** 触发器上的键盘契约(`createTriggerKeydown()` 产出,两件同源) */
51
+ onKeydown: {
52
+ type: Function,
53
+ default: void 0
54
+ },
55
+ /** 清除位被点击(输入面的内建清除位回调;本件只把它转给消费方) */
56
+ onClear: {
57
+ type: Function,
58
+ default: void 0
59
+ },
60
+ /** 值区自定义渲染(多选标签收纳用;不给则走单选那一行) */
61
+ renderValue: {
62
+ type: Function,
63
+ default: void 0
64
+ }
65
+ },
66
+ setup(props) {
67
+ const controlClass = () => ["mh-select", props.multiple ? "mh-select--multiple" : "", props.size === "md" ? "" : `mh-select--${props.size}`].filter(Boolean).join(" ");
68
+ return () => createVNode(Input, {
69
+ "as": "button",
70
+ "size": props.size,
71
+ "status": props.status,
72
+ "disabled": props.disabled,
73
+ "clearable": props.clearable,
74
+ "hasValue": props.hasValue,
75
+ "class": "mh-popup-anchor mh-select-host",
76
+ "controlClass": controlClass(),
77
+ "controlAttrs": {
78
+ popovertarget: props.panelId,
79
+ "aria-haspopup": "listbox",
80
+ /* 开合态由原生 popover 与 popup.css 的箭头转向承担,本件不维护第二份;
81
+ 恒定 'false' 是既有口径(输入面的触发器形态不跟踪 popover 的开关状态) */
82
+ "aria-expanded": "false",
83
+ onKeydown: props.onKeydown
84
+ },
85
+ "onChange": () => props.onClear?.()
86
+ }, {
87
+ default: () => props.renderValue ? props.renderValue() : createVNode("span", {
88
+ "class": ["mh-select__value", props.hasValue ? "" : "mh-select__value--placeholder"].filter(Boolean).join(" ")
89
+ }, [props.hasValue ? props.label : props.placeholder]),
90
+ /* 箭头沿用输入框的右侧图标位(槽内尺寸收敛见 `select.css` 的 `.mh-select-host` 一节);
91
+ 展开转向由 `popup.css` 的 `.mh-popup-caret` 承担(含触发器进表壳后的兄弟档) */
92
+ suffix: () => createVNode(Icon, {
93
+ "name": "chevron-down",
94
+ "class": "mh-select__caret mh-popup-caret"
95
+ }, null)
96
+ });
97
+ }
98
+ });
99
+ export {
100
+ SelectTrigger
101
+ };