@manohub/ui 0.10.0 → 0.10.1
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/dist/components/input/index.d.ts +28 -13
- package/dist/components/input/index.js +41 -12
- package/dist/components/select/index.d.ts +5 -2
- package/dist/components/select/index.js +2 -3
- package/dist/components/select/select-list.d.ts +2 -0
- package/dist/components/select/select-panel.d.ts +9 -4
- package/dist/components/select/select-panel.js +4 -2
- package/dist/components/select/select-tree.d.ts +5 -3
- package/dist/components/select/select-tree.js +3 -15
- package/dist/styles/components/badge.css +3 -1
- package/dist/styles/components/button.css +5 -2
- package/dist/styles/components/input.css +13 -1
- package/dist/styles/components/input.tokens.css +5 -3
- package/dist/styles/components/pagination.css +8 -2
- package/dist/styles/components/select.css +36 -6
- package/dist/styles/components/select.tokens.css +4 -3
- package/dist/styles/components/steps.tokens.css +5 -3
- package/dist/styles/components/tag.css +7 -3
- package/dist/styles/components/upload.tokens.css +1 -1
- package/package.json +2 -2
|
@@ -22,6 +22,11 @@ export interface TextareaAutoSize {
|
|
|
22
22
|
* 附加位:`prefix` / `suffix` **插槽**里放什么由你决定(图标 / 单位 / 域名 / 你自己的可点元素),
|
|
23
23
|
* 本件不内置标签件 —— 纯装饰形态无底色、不参与 Tab 序列,可点形态的热区与底色归消费方。
|
|
24
24
|
*
|
|
25
|
+
* **属性落点**(用错位置就会「样式在壳上、属性在组件上被丢掉」):
|
|
26
|
+
* `class` / `style` → 表壳 `.mh-input__wrap`(整件的宽度与显式锚);**其余 attrs 一律透传到控件**
|
|
27
|
+
* (`id` / `name` / `aria-*` / `data-*` / `title` / 原生事件)—— 壳只是排版容器,属性落它上面等于丢掉
|
|
28
|
+
* —— 见 `forwardAttrs`;触发器形态另有 `controlClass` / `controlAttrs` 两个**专口子**(同名键以它为准)。
|
|
29
|
+
*
|
|
25
30
|
* @example 受控 + 可清除
|
|
26
31
|
* <Input modelValue={kw} placeholder="搜索" clearable prefix={<Icon name="search" />} onChange={setKw} />
|
|
27
32
|
*
|
|
@@ -115,7 +120,7 @@ export declare const Input: import("vue").DefineComponent<import("vue").ExtractP
|
|
|
115
120
|
type: NumberConstructor;
|
|
116
121
|
default: undefined;
|
|
117
122
|
};
|
|
118
|
-
/**
|
|
123
|
+
/** 右侧字数统计(**须给 `maxlength` 才渲染**:没有分母的 `n/0` 是坏值) */
|
|
119
124
|
showCount: {
|
|
120
125
|
type: BooleanConstructor;
|
|
121
126
|
default: boolean;
|
|
@@ -145,7 +150,8 @@ export declare const Input: import("vue").DefineComponent<import("vue").ExtractP
|
|
|
145
150
|
type: StringConstructor;
|
|
146
151
|
default: undefined;
|
|
147
152
|
};
|
|
148
|
-
/** 展开到**控件元素**上的属性(触发器形态要 `popovertarget` / `aria-*` / `onKeydown`
|
|
153
|
+
/** 展开到**控件元素**上的属性(触发器形态要 `popovertarget` / `aria-*` / `onKeydown` 落到控件上);
|
|
154
|
+
* 与透传的 attrs 撞名时以本 prop 为准 */
|
|
149
155
|
controlAttrs: {
|
|
150
156
|
type: PropType<Record<string, unknown>>;
|
|
151
157
|
default: undefined;
|
|
@@ -233,7 +239,7 @@ export declare const Input: import("vue").DefineComponent<import("vue").ExtractP
|
|
|
233
239
|
type: NumberConstructor;
|
|
234
240
|
default: undefined;
|
|
235
241
|
};
|
|
236
|
-
/**
|
|
242
|
+
/** 右侧字数统计(**须给 `maxlength` 才渲染**:没有分母的 `n/0` 是坏值) */
|
|
237
243
|
showCount: {
|
|
238
244
|
type: BooleanConstructor;
|
|
239
245
|
default: boolean;
|
|
@@ -263,7 +269,8 @@ export declare const Input: import("vue").DefineComponent<import("vue").ExtractP
|
|
|
263
269
|
type: StringConstructor;
|
|
264
270
|
default: undefined;
|
|
265
271
|
};
|
|
266
|
-
/** 展开到**控件元素**上的属性(触发器形态要 `popovertarget` / `aria-*` / `onKeydown`
|
|
272
|
+
/** 展开到**控件元素**上的属性(触发器形态要 `popovertarget` / `aria-*` / `onKeydown` 落到控件上);
|
|
273
|
+
* 与透传的 attrs 撞名时以本 prop 为准 */
|
|
267
274
|
controlAttrs: {
|
|
268
275
|
type: PropType<Record<string, unknown>>;
|
|
269
276
|
default: undefined;
|
|
@@ -324,7 +331,7 @@ export interface TextareaProps {
|
|
|
324
331
|
/** 是否带边框 */
|
|
325
332
|
bordered?: boolean;
|
|
326
333
|
maxlength?: number;
|
|
327
|
-
/** 右下角字数统计(须给 `maxlength`
|
|
334
|
+
/** 右下角字数统计(须给 `maxlength` 才渲染) */
|
|
328
335
|
showCount?: boolean;
|
|
329
336
|
autofocus?: boolean;
|
|
330
337
|
onChange?: (v: string) => void;
|
|
@@ -332,8 +339,9 @@ export interface TextareaProps {
|
|
|
332
339
|
/**
|
|
333
340
|
* 多行输入(同一份面;结构与单行不同,故单列一个件)。
|
|
334
341
|
*
|
|
335
|
-
* ⚠️
|
|
336
|
-
*
|
|
342
|
+
* ⚠️ **输入族没有 `mono` 档**(`Input` 与 `Textarea` 都没有)。要等宽的输入框别拿 `Text mono` 去包
|
|
343
|
+
* —— 那会丢掉输入能力;目前只能由应用侧保留一条带 `font-family: var(--ui-font-mono)` 的样式登记
|
|
344
|
+
* (缺件登记见下游仓 `aihub-frontend/docs/kit-gaps.md`,本仓不复制那份台账)。
|
|
337
345
|
*
|
|
338
346
|
* @example 备注框:自适应 2~6 行 + 限长计数
|
|
339
347
|
* <Textarea modelValue={note} autoSize={{ minRows: 2, maxRows: 6 }} maxlength={500} showCount onChange={setNote} />
|
|
@@ -388,6 +396,7 @@ export declare const Textarea: import("vue").DefineComponent<import("vue").Extra
|
|
|
388
396
|
type: NumberConstructor;
|
|
389
397
|
default: undefined;
|
|
390
398
|
};
|
|
399
|
+
/** 右下角字数统计(**须给 `maxlength` 才渲染**:没有分母的 `n/0` 是坏值) */
|
|
391
400
|
showCount: {
|
|
392
401
|
type: BooleanConstructor;
|
|
393
402
|
default: boolean;
|
|
@@ -452,6 +461,7 @@ export declare const Textarea: import("vue").DefineComponent<import("vue").Extra
|
|
|
452
461
|
type: NumberConstructor;
|
|
453
462
|
default: undefined;
|
|
454
463
|
};
|
|
464
|
+
/** 右下角字数统计(**须给 `maxlength` 才渲染**:没有分母的 `n/0` 是坏值) */
|
|
455
465
|
showCount: {
|
|
456
466
|
type: BooleanConstructor;
|
|
457
467
|
default: boolean;
|
|
@@ -559,7 +569,7 @@ export declare const InputCompound: {
|
|
|
559
569
|
type: NumberConstructor;
|
|
560
570
|
default: undefined;
|
|
561
571
|
};
|
|
562
|
-
/**
|
|
572
|
+
/** 右侧字数统计(**须给 `maxlength` 才渲染**:没有分母的 `n/0` 是坏值) */
|
|
563
573
|
showCount: {
|
|
564
574
|
type: BooleanConstructor;
|
|
565
575
|
default: boolean;
|
|
@@ -589,7 +599,8 @@ export declare const InputCompound: {
|
|
|
589
599
|
type: StringConstructor;
|
|
590
600
|
default: undefined;
|
|
591
601
|
};
|
|
592
|
-
/** 展开到**控件元素**上的属性(触发器形态要 `popovertarget` / `aria-*` / `onKeydown`
|
|
602
|
+
/** 展开到**控件元素**上的属性(触发器形态要 `popovertarget` / `aria-*` / `onKeydown` 落到控件上);
|
|
603
|
+
* 与透传的 attrs 撞名时以本 prop 为准 */
|
|
593
604
|
controlAttrs: {
|
|
594
605
|
type: PropType<Record<string, unknown>>;
|
|
595
606
|
default: undefined;
|
|
@@ -711,7 +722,7 @@ export declare const InputCompound: {
|
|
|
711
722
|
type: NumberConstructor;
|
|
712
723
|
default: undefined;
|
|
713
724
|
};
|
|
714
|
-
/**
|
|
725
|
+
/** 右侧字数统计(**须给 `maxlength` 才渲染**:没有分母的 `n/0` 是坏值) */
|
|
715
726
|
showCount: {
|
|
716
727
|
type: BooleanConstructor;
|
|
717
728
|
default: boolean;
|
|
@@ -741,7 +752,8 @@ export declare const InputCompound: {
|
|
|
741
752
|
type: StringConstructor;
|
|
742
753
|
default: undefined;
|
|
743
754
|
};
|
|
744
|
-
/** 展开到**控件元素**上的属性(触发器形态要 `popovertarget` / `aria-*` / `onKeydown`
|
|
755
|
+
/** 展开到**控件元素**上的属性(触发器形态要 `popovertarget` / `aria-*` / `onKeydown` 落到控件上);
|
|
756
|
+
* 与透传的 attrs 撞名时以本 prop 为准 */
|
|
745
757
|
controlAttrs: {
|
|
746
758
|
type: PropType<Record<string, unknown>>;
|
|
747
759
|
default: undefined;
|
|
@@ -854,7 +866,7 @@ export declare const InputCompound: {
|
|
|
854
866
|
type: NumberConstructor;
|
|
855
867
|
default: undefined;
|
|
856
868
|
};
|
|
857
|
-
/**
|
|
869
|
+
/** 右侧字数统计(**须给 `maxlength` 才渲染**:没有分母的 `n/0` 是坏值) */
|
|
858
870
|
showCount: {
|
|
859
871
|
type: BooleanConstructor;
|
|
860
872
|
default: boolean;
|
|
@@ -884,7 +896,8 @@ export declare const InputCompound: {
|
|
|
884
896
|
type: StringConstructor;
|
|
885
897
|
default: undefined;
|
|
886
898
|
};
|
|
887
|
-
/** 展开到**控件元素**上的属性(触发器形态要 `popovertarget` / `aria-*` / `onKeydown`
|
|
899
|
+
/** 展开到**控件元素**上的属性(触发器形态要 `popovertarget` / `aria-*` / `onKeydown` 落到控件上);
|
|
900
|
+
* 与透传的 attrs 撞名时以本 prop 为准 */
|
|
888
901
|
controlAttrs: {
|
|
889
902
|
type: PropType<Record<string, unknown>>;
|
|
890
903
|
default: undefined;
|
|
@@ -975,6 +988,7 @@ export declare const InputCompound: {
|
|
|
975
988
|
type: NumberConstructor;
|
|
976
989
|
default: undefined;
|
|
977
990
|
};
|
|
991
|
+
/** 右下角字数统计(**须给 `maxlength` 才渲染**:没有分母的 `n/0` 是坏值) */
|
|
978
992
|
showCount: {
|
|
979
993
|
type: BooleanConstructor;
|
|
980
994
|
default: boolean;
|
|
@@ -1039,6 +1053,7 @@ export declare const InputCompound: {
|
|
|
1039
1053
|
type: NumberConstructor;
|
|
1040
1054
|
default: undefined;
|
|
1041
1055
|
};
|
|
1056
|
+
/** 右下角字数统计(**须给 `maxlength` 才渲染**:没有分母的 `n/0` 是坏值) */
|
|
1042
1057
|
showCount: {
|
|
1043
1058
|
type: BooleanConstructor;
|
|
1044
1059
|
default: boolean;
|
|
@@ -78,7 +78,7 @@ const Input = /* @__PURE__ */ defineComponent({
|
|
|
78
78
|
type: Number,
|
|
79
79
|
default: void 0
|
|
80
80
|
},
|
|
81
|
-
/**
|
|
81
|
+
/** 右侧字数统计(**须给 `maxlength` 才渲染**:没有分母的 `n/0` 是坏值) */
|
|
82
82
|
showCount: {
|
|
83
83
|
type: Boolean,
|
|
84
84
|
default: false
|
|
@@ -104,6 +104,10 @@ const Input = /* @__PURE__ */ defineComponent({
|
|
|
104
104
|
* 原生 `<button>` 才有的语义(如 `popovertarget` 触发 popover —— 该属性只认 button)。
|
|
105
105
|
* 缺省 `as="input"` 的渲染路径除本组 prop 外零变化。
|
|
106
106
|
*
|
|
107
|
+
* **单行契约(与输入框同源,面层承担)**:控件是单行盒,值再长也不换行(超出走省略号)
|
|
108
|
+
* —— 见 `styles/components/input.css` 的 `.mh-input--trigger`。故 `default` 插槽里给**裸文本**
|
|
109
|
+
* 也安全,不需要自己再包一层截断 span。
|
|
110
|
+
*
|
|
107
111
|
* **无值时的占位**:`<button>` 没有 `::placeholder` 伪元素可用,故 `placeholder` 在这个形态下
|
|
108
112
|
* 由本件渲染成一个 span(`.mh-input__placeholder`,色档与原生占位一致);判据是
|
|
109
113
|
* `hasValue === false && placeholder` **同时**成立 —— 两者缺一即沿用消费方给的 `default` 插槽。
|
|
@@ -119,7 +123,8 @@ const Input = /* @__PURE__ */ defineComponent({
|
|
|
119
123
|
type: String,
|
|
120
124
|
default: void 0
|
|
121
125
|
},
|
|
122
|
-
/** 展开到**控件元素**上的属性(触发器形态要 `popovertarget` / `aria-*` / `onKeydown`
|
|
126
|
+
/** 展开到**控件元素**上的属性(触发器形态要 `popovertarget` / `aria-*` / `onKeydown` 落到控件上);
|
|
127
|
+
* 与透传的 attrs 撞名时以本 prop 为准 */
|
|
123
128
|
controlAttrs: {
|
|
124
129
|
type: Object,
|
|
125
130
|
default: void 0
|
|
@@ -143,6 +148,13 @@ const Input = /* @__PURE__ */ defineComponent({
|
|
|
143
148
|
const inner = ref(props.defaultValue);
|
|
144
149
|
const current = computed(() => props.modelValue ?? inner.value);
|
|
145
150
|
const revealed = ref(false);
|
|
151
|
+
const forwardAttrs = computed(() => {
|
|
152
|
+
const rest = {};
|
|
153
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
154
|
+
if (key !== "class" && key !== "style") rest[key] = value;
|
|
155
|
+
}
|
|
156
|
+
return rest;
|
|
157
|
+
});
|
|
146
158
|
const visualStatus = computed(() => props.status ?? (props.invalid ? "error" : void 0));
|
|
147
159
|
const isInvalid = computed(() => visualStatus.value === "error");
|
|
148
160
|
const commit = (value) => {
|
|
@@ -163,6 +175,7 @@ const Input = /* @__PURE__ */ defineComponent({
|
|
|
163
175
|
const isTrigger = computed(() => props.as === "button");
|
|
164
176
|
const controlClasses = computed(() => ["mh-input", isTrigger.value ? "mh-input--trigger" : "", props.size === "md" ? "" : `mh-input--${props.size}`, props.bordered ? "" : "mh-input--borderless", visualStatus.value ? `is-${visualStatus.value}` : "", props.disabled ? "is-disabled" : "", props.controlClass].filter(Boolean).join(" "));
|
|
165
177
|
const showClear = computed(() => props.clearable && (props.hasValue ?? !!current.value));
|
|
178
|
+
const showCounter = computed(() => !isTrigger.value && props.showCount && props.maxlength !== void 0);
|
|
166
179
|
const showPlaceholder = computed(() => isTrigger.value && props.hasValue === false && !!props.placeholder);
|
|
167
180
|
return () => createVNode("div", {
|
|
168
181
|
"class": ["mh-input__wrap", attrs.class].filter(Boolean).join(" "),
|
|
@@ -175,7 +188,7 @@ const Input = /* @__PURE__ */ defineComponent({
|
|
|
175
188
|
本件的 Enter / Delete 是**输入框**语义,触发器有自己的一套(如 `↓` 开面板),不在此套用。 */
|
|
176
189
|
createVNode("button", mergeProps({
|
|
177
190
|
"type": "button"
|
|
178
|
-
}, props.controlAttrs, {
|
|
191
|
+
}, forwardAttrs.value, props.controlAttrs, {
|
|
179
192
|
"class": controlClasses.value,
|
|
180
193
|
"disabled": props.disabled,
|
|
181
194
|
"aria-invalid": isInvalid.value ? "true" : void 0
|
|
@@ -185,7 +198,7 @@ const Input = /* @__PURE__ */ defineComponent({
|
|
|
185
198
|
"class": "mh-input__placeholder"
|
|
186
199
|
}, [props.placeholder])
|
|
187
200
|
) : slots.default ? slots.default() : null])
|
|
188
|
-
) : createVNode("input", {
|
|
201
|
+
) : createVNode("input", mergeProps(forwardAttrs.value, {
|
|
189
202
|
"class": controlClasses.value,
|
|
190
203
|
"type": inputType.value,
|
|
191
204
|
"value": current.value,
|
|
@@ -197,7 +210,7 @@ const Input = /* @__PURE__ */ defineComponent({
|
|
|
197
210
|
"aria-invalid": isInvalid.value ? "true" : void 0,
|
|
198
211
|
"onInput": onInput,
|
|
199
212
|
"onKeydown": onKeydown
|
|
200
|
-
}, null), showClear.value ? createVNode("button", {
|
|
213
|
+
}), null), showClear.value ? createVNode("button", {
|
|
201
214
|
"type": "button",
|
|
202
215
|
"class": "mh-input__clear",
|
|
203
216
|
"aria-label": "清除",
|
|
@@ -219,9 +232,9 @@ const Input = /* @__PURE__ */ defineComponent({
|
|
|
219
232
|
"class": "mh-input__eye-icon"
|
|
220
233
|
}, null)]) : null, slots.suffix ? createVNode("span", {
|
|
221
234
|
"class": "mh-input__suffix"
|
|
222
|
-
}, [slots.suffix()]) : null,
|
|
235
|
+
}, [slots.suffix()]) : null, showCounter.value ? createVNode("span", {
|
|
223
236
|
"class": "mh-input__counter"
|
|
224
|
-
}, [current.value?.length ?? 0, createTextVNode("/"), props.maxlength
|
|
237
|
+
}, [current.value?.length ?? 0, createTextVNode("/"), props.maxlength]) : null]);
|
|
225
238
|
}
|
|
226
239
|
});
|
|
227
240
|
const Textarea = /* @__PURE__ */ defineComponent({
|
|
@@ -277,6 +290,7 @@ const Textarea = /* @__PURE__ */ defineComponent({
|
|
|
277
290
|
type: Number,
|
|
278
291
|
default: void 0
|
|
279
292
|
},
|
|
293
|
+
/** 右下角字数统计(**须给 `maxlength` 才渲染**:没有分母的 `n/0` 是坏值) */
|
|
280
294
|
showCount: {
|
|
281
295
|
type: Boolean,
|
|
282
296
|
default: false
|
|
@@ -299,6 +313,14 @@ const Textarea = /* @__PURE__ */ defineComponent({
|
|
|
299
313
|
}) {
|
|
300
314
|
const inner = ref(props.defaultValue);
|
|
301
315
|
const current = computed(() => props.modelValue ?? inner.value);
|
|
316
|
+
const forwardAttrs = computed(() => {
|
|
317
|
+
const rest = {};
|
|
318
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
319
|
+
if (key !== "class" && key !== "style") rest[key] = value;
|
|
320
|
+
}
|
|
321
|
+
return rest;
|
|
322
|
+
});
|
|
323
|
+
const showCounter = computed(() => props.showCount && props.maxlength !== void 0);
|
|
302
324
|
const visualStatus = computed(() => props.status ?? (props.invalid ? "error" : void 0));
|
|
303
325
|
const isInvalid = computed(() => visualStatus.value === "error");
|
|
304
326
|
const autoRows = computed(() => {
|
|
@@ -320,7 +342,7 @@ const Textarea = /* @__PURE__ */ defineComponent({
|
|
|
320
342
|
return () => createVNode("div", {
|
|
321
343
|
"class": ["mh-input__wrap", attrs.class].filter(Boolean).join(" "),
|
|
322
344
|
"style": attrs.style
|
|
323
|
-
}, [createVNode("textarea", {
|
|
345
|
+
}, [createVNode("textarea", mergeProps(forwardAttrs.value, {
|
|
324
346
|
"class": ["mh-input", "mh-input--textarea", props.size === "md" ? "" : `mh-input--${props.size}`, props.bordered ? "" : "mh-input--borderless", visualStatus.value ? `is-${visualStatus.value}` : "", props.disabled ? "is-disabled" : ""].filter(Boolean).join(" "),
|
|
325
347
|
"rows": autoRows.value,
|
|
326
348
|
"value": current.value,
|
|
@@ -331,9 +353,9 @@ const Textarea = /* @__PURE__ */ defineComponent({
|
|
|
331
353
|
"autofocus": props.autofocus,
|
|
332
354
|
"aria-invalid": isInvalid.value ? "true" : void 0,
|
|
333
355
|
"onInput": onInput
|
|
334
|
-
}, null),
|
|
356
|
+
}), null), showCounter.value ? createVNode("span", {
|
|
335
357
|
"class": "mh-input__counter"
|
|
336
|
-
}, [current.value?.length ?? 0, createTextVNode("/"), props.maxlength
|
|
358
|
+
}, [current.value?.length ?? 0, createTextVNode("/"), props.maxlength]) : null]);
|
|
337
359
|
}
|
|
338
360
|
});
|
|
339
361
|
const InputDisplay = /* @__PURE__ */ defineComponent({
|
|
@@ -350,10 +372,17 @@ const InputDisplay = /* @__PURE__ */ defineComponent({
|
|
|
350
372
|
slots,
|
|
351
373
|
attrs
|
|
352
374
|
}) {
|
|
353
|
-
|
|
375
|
+
const forwardAttrs = computed(() => {
|
|
376
|
+
const rest = {};
|
|
377
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
378
|
+
if (key !== "class" && key !== "style") rest[key] = value;
|
|
379
|
+
}
|
|
380
|
+
return rest;
|
|
381
|
+
});
|
|
382
|
+
return () => createVNode("span", mergeProps(forwardAttrs.value, {
|
|
354
383
|
"class": ["mh-input--display", attrs.class].filter(Boolean).join(" "),
|
|
355
384
|
"style": attrs.style
|
|
356
|
-
}, [slots.default ? slots.default() : props.value]);
|
|
385
|
+
}), [slots.default ? slots.default() : props.value]);
|
|
357
386
|
}
|
|
358
387
|
});
|
|
359
388
|
Object.assign(Input, {
|
|
@@ -32,6 +32,9 @@
|
|
|
32
32
|
* (不做 JS 定位、不做 resize 监听)。本件只补原生做不到的:选中值回填、多选数组的增删与收纳、
|
|
33
33
|
* 搜索过滤与「创建」项、以及键盘的下拉导航(↑↓ 移动高亮行)。
|
|
34
34
|
*
|
|
35
|
+
* **面板有高度上限**:`--ui-select-panel-max-height`(默认 320px,钉在 `select.css` 的面板根上)——
|
|
36
|
+
* 超出即面板内容区(`.mh-select__body`)滚动、搜索行常驻;列表与 `SelectTree` 两形态共用这一份。
|
|
37
|
+
*
|
|
35
38
|
* ## 状态三源等价
|
|
36
39
|
*
|
|
37
40
|
* 选中行 = `[data-state='selected']`(组件下发)/ 原生 `input:checked`(静态页零 JS 路径)/
|
|
@@ -87,7 +90,7 @@ export declare const Select: import("vue").DefineComponent<import("vue").Extract
|
|
|
87
90
|
type: StringConstructor;
|
|
88
91
|
default: undefined;
|
|
89
92
|
};
|
|
90
|
-
/**
|
|
93
|
+
/** 搜索行是否出现(**只认显式开启**;缺省不出 —— 不在面板里凭空多出一行) */
|
|
91
94
|
searchable: {
|
|
92
95
|
type: BooleanConstructor;
|
|
93
96
|
default: boolean;
|
|
@@ -176,7 +179,7 @@ export declare const Select: import("vue").DefineComponent<import("vue").Extract
|
|
|
176
179
|
type: StringConstructor;
|
|
177
180
|
default: undefined;
|
|
178
181
|
};
|
|
179
|
-
/**
|
|
182
|
+
/** 搜索行是否出现(**只认显式开启**;缺省不出 —— 不在面板里凭空多出一行) */
|
|
180
183
|
searchable: {
|
|
181
184
|
type: BooleanConstructor;
|
|
182
185
|
default: boolean;
|
|
@@ -41,7 +41,7 @@ const Select = /* @__PURE__ */ defineComponent({
|
|
|
41
41
|
type: String,
|
|
42
42
|
default: void 0
|
|
43
43
|
},
|
|
44
|
-
/**
|
|
44
|
+
/** 搜索行是否出现(**只认显式开启**;缺省不出 —— 不在面板里凭空多出一行) */
|
|
45
45
|
searchable: {
|
|
46
46
|
type: Boolean,
|
|
47
47
|
default: false
|
|
@@ -122,7 +122,6 @@ const Select = /* @__PURE__ */ defineComponent({
|
|
|
122
122
|
return list;
|
|
123
123
|
});
|
|
124
124
|
const selectedOptions = computed(() => flatOptions.value.filter((option) => selectedValues.value.includes(option.value)));
|
|
125
|
-
const showSearch = computed(() => props.searchable || flatOptions.value.length > 8);
|
|
126
125
|
const matched = (option, kw) => props.filterOption ? props.filterOption(option, kw) : option.label.includes(kw);
|
|
127
126
|
const visibleOptions = computed(() => {
|
|
128
127
|
const kw = keyword.value;
|
|
@@ -215,7 +214,7 @@ const Select = /* @__PURE__ */ defineComponent({
|
|
|
215
214
|
}, null), createVNode(SelectPanel, {
|
|
216
215
|
"id": panelId,
|
|
217
216
|
"elementRef": panelRef,
|
|
218
|
-
"searchable":
|
|
217
|
+
"searchable": props.searchable,
|
|
219
218
|
"keyword": keyword.value,
|
|
220
219
|
"onKeyword": (value) => {
|
|
221
220
|
keyword.value = value;
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* 它拥有「面板里那一撮选项」的全部形态与状态行:平铺选项 / 分组(组标题不可点)/ 「创建」行 /
|
|
5
5
|
* 空态行 / 加载行。**不含面板壳与搜索行**(那是 `SelectPanel` 的事),也不持有任何状态 ——
|
|
6
6
|
* 过滤结果、选中值、是否可创建统统由消费方算好喂进来,本件只渲染。
|
|
7
|
+
* 行由面板壳装在**内容区**(`.mh-select__body`,面板里唯一的滚动容器)里,限高与滚动都归面层,
|
|
8
|
+
* 本件不写第二个滚动容器。
|
|
7
9
|
*
|
|
8
10
|
* 选项是真实 `<button role="option">`(不是 div + JS 高亮):键盘可达、`:focus-visible` 可直接
|
|
9
11
|
* 承载「高亮行」这层语义,于是不需要再维护一份 JS 高亮下标 —— **焦点即高亮**,
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SelectPanel —— 选择器族的**面板壳内件**(`Select` 与 `SelectTree` 共用,不对外导出)。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* 它只拥有四样东西:**原生 popover 元素**(开合 / Esc / 点外部关闭 / 顶层渲染 / 定位全归浏览器)、
|
|
5
|
+
* **搜索行**(面板顶部那一个输入框)、**内容区**(`.mh-select__body`,面板里唯一的滚动容器)
|
|
6
|
+
* 与**内容槽**(列表体或树体)。
|
|
6
7
|
* 「面板里装什么」不在本件 —— 这正是让列表与树共用同一份面板皮肤的原因。
|
|
7
8
|
*
|
|
9
|
+
* **限高在面层**:面板根上钉着 `--ui-select-panel-max-height`(`select.css`),超出即内容区滚动、
|
|
10
|
+
* 搜索行常驻不滚。内容区这一层因此是**结构性的**(不是可有可无的包裹):少了它,长列表 / 长树
|
|
11
|
+
* 会把面板一路撑高(实测 60 条选项 2212px),搜索行也会跟着滚走。本件只负责把它渲染出来。
|
|
12
|
+
*
|
|
8
13
|
* 组合类名与服务端三条属性都是**操作层的组合**,不是本件新造的浮层:
|
|
9
14
|
* `class="mh-popup mh-select__panel mh-popup--anchored"`(状态根 + 本族皮肤 + 显式锚档)、
|
|
10
15
|
* `data-side` / `data-align`(`popup.css` 的定位档)、`popover="auto"`(原生轻 dismiss)。
|
|
@@ -27,7 +32,7 @@ export declare const SelectPanel: import("vue").DefineComponent<import("vue").Ex
|
|
|
27
32
|
type: PropType<ElementRef>;
|
|
28
33
|
default: undefined;
|
|
29
34
|
};
|
|
30
|
-
/**
|
|
35
|
+
/** 是否渲染搜索行(**只认消费方显式开启**:列表形态与树形态同口径,件不按数据量自动决断) */
|
|
31
36
|
searchable: {
|
|
32
37
|
type: BooleanConstructor;
|
|
33
38
|
default: boolean;
|
|
@@ -66,7 +71,7 @@ export declare const SelectPanel: import("vue").DefineComponent<import("vue").Ex
|
|
|
66
71
|
type: PropType<ElementRef>;
|
|
67
72
|
default: undefined;
|
|
68
73
|
};
|
|
69
|
-
/**
|
|
74
|
+
/** 是否渲染搜索行(**只认消费方显式开启**:列表形态与树形态同口径,件不按数据量自动决断) */
|
|
70
75
|
searchable: {
|
|
71
76
|
type: BooleanConstructor;
|
|
72
77
|
default: boolean;
|
|
@@ -13,7 +13,7 @@ const SelectPanel = /* @__PURE__ */ defineComponent({
|
|
|
13
13
|
type: Object,
|
|
14
14
|
default: void 0
|
|
15
15
|
},
|
|
16
|
-
/**
|
|
16
|
+
/** 是否渲染搜索行(**只认消费方显式开启**:列表形态与树形态同口径,件不按数据量自动决断) */
|
|
17
17
|
searchable: {
|
|
18
18
|
type: Boolean,
|
|
19
19
|
default: false
|
|
@@ -64,7 +64,9 @@ const SelectPanel = /* @__PURE__ */ defineComponent({
|
|
|
64
64
|
"value": props.keyword,
|
|
65
65
|
"placeholder": props.searchPlaceholder,
|
|
66
66
|
"onInput": (event) => props.onKeyword?.(event.target.value)
|
|
67
|
-
}, null)]) : null,
|
|
67
|
+
}, null)]) : null, createVNode("div", {
|
|
68
|
+
"class": "mh-select__body"
|
|
69
|
+
}, [slots.default?.()])]);
|
|
68
70
|
}
|
|
69
71
|
});
|
|
70
72
|
export {
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
* 2. **本地过滤树**:搜索行在场时按关键词过滤,命中节点的**祖先链保留**、父节点自身命中则
|
|
11
11
|
* **整支保留**,并强制全展到命中层(算法与切分都是纯函数,见 `select-tree-filter.ts`);
|
|
12
12
|
* 命中文本按面层的 `.mh-select__hit` 高亮(靠 `Tree` 的可选 `renderLabel` 钩子落进标签槽)。
|
|
13
|
-
* 3.
|
|
13
|
+
* 3. **面板内限高滚动**:限高归面板(`.mh-select__panel` 的 `--ui-select-panel-max-height`)、
|
|
14
|
+
* 溢出由面板内容区(`.mh-select__body`)承担;本件只把树包在 `.mh-select__tree` 里给内距
|
|
15
|
+
* —— 限高不由形态自持,列表形态共用同一份。
|
|
14
16
|
*
|
|
15
17
|
* ## 与 `Select` 的三处有意差异(都是「树不是列表」决定的)
|
|
16
18
|
*
|
|
@@ -65,7 +67,7 @@ export declare const SelectTree: import("vue").DefineComponent<import("vue").Ext
|
|
|
65
67
|
type: StringConstructor;
|
|
66
68
|
default: string;
|
|
67
69
|
};
|
|
68
|
-
/**
|
|
70
|
+
/** 搜索行是否出现(**只认显式开启**;缺省不出 —— 不在面板里凭空多出一行) */
|
|
69
71
|
searchable: {
|
|
70
72
|
type: BooleanConstructor;
|
|
71
73
|
default: boolean;
|
|
@@ -174,7 +176,7 @@ export declare const SelectTree: import("vue").DefineComponent<import("vue").Ext
|
|
|
174
176
|
type: StringConstructor;
|
|
175
177
|
default: string;
|
|
176
178
|
};
|
|
177
|
-
/**
|
|
179
|
+
/** 搜索行是否出现(**只认显式开启**;缺省不出 —— 不在面板里凭空多出一行) */
|
|
178
180
|
searchable: {
|
|
179
181
|
type: BooleanConstructor;
|
|
180
182
|
default: boolean;
|
|
@@ -3,7 +3,7 @@ import { Tree } from "../tree/index.js";
|
|
|
3
3
|
import { SelectPanel } from "./select-panel.js";
|
|
4
4
|
import { SelectTrigger } from "./select-trigger.js";
|
|
5
5
|
import { createSelectPanelId, createTriggerKeydown, showPanel, hidePanel } from "./select-shared.js";
|
|
6
|
-
import { filterTreeNodes, findTreeNode, treeLabel, splitHighlight,
|
|
6
|
+
import { filterTreeNodes, findTreeNode, treeLabel, splitHighlight, treeNodeKey, treeChildren } from "./select-tree-filter.js";
|
|
7
7
|
const SelectTree = /* @__PURE__ */ defineComponent({
|
|
8
8
|
name: "SelectTree",
|
|
9
9
|
inheritAttrs: false,
|
|
@@ -42,7 +42,7 @@ const SelectTree = /* @__PURE__ */ defineComponent({
|
|
|
42
42
|
type: String,
|
|
43
43
|
default: "请选择"
|
|
44
44
|
},
|
|
45
|
-
/**
|
|
45
|
+
/** 搜索行是否出现(**只认显式开启**;缺省不出 —— 不在面板里凭空多出一行) */
|
|
46
46
|
searchable: {
|
|
47
47
|
type: Boolean,
|
|
48
48
|
default: false
|
|
@@ -132,18 +132,6 @@ const SelectTree = /* @__PURE__ */ defineComponent({
|
|
|
132
132
|
const childrenOf = (node) => treeChildren(node, props.childrenKey);
|
|
133
133
|
const current = computed(() => props.modelValue ?? inner.value);
|
|
134
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
135
|
const collectKeys = (list) => list.flatMap((node) => [keyOf(node), ...collectKeys(childrenOf(node))]);
|
|
148
136
|
const innerExpanded = ref(props.defaultExpandAll ? collectKeys(props.nodes) : [...props.defaultExpandedKeys]);
|
|
149
137
|
watch(() => props.defaultExpandedKeys, (next) => {
|
|
@@ -221,7 +209,7 @@ const SelectTree = /* @__PURE__ */ defineComponent({
|
|
|
221
209
|
}, null), createVNode(SelectPanel, {
|
|
222
210
|
"id": panelId,
|
|
223
211
|
"elementRef": panelRef,
|
|
224
|
-
"searchable":
|
|
212
|
+
"searchable": props.searchable,
|
|
225
213
|
"keyword": keyword.value,
|
|
226
214
|
"onKeyword": (value) => {
|
|
227
215
|
keyword.value = value;
|
|
@@ -89,8 +89,10 @@
|
|
|
89
89
|
border-color: transparent;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/* 中性软底取 `base-200`(中性面)—— 改前是 `base-50`:那档只比内容底低 1.7 L*,
|
|
93
|
+
软底淡到与背景不可分(同族 `--primary` 软底也走「弱底」档,此处与之等形)。 */
|
|
92
94
|
.mh-badge--soft.mh-badge--default {
|
|
93
|
-
--mh-badge-bg: var(--ui-base-
|
|
95
|
+
--mh-badge-bg: var(--ui-base-200);
|
|
94
96
|
--mh-badge-fg: var(--ui-base-content-muted);
|
|
95
97
|
}
|
|
96
98
|
|
|
@@ -165,10 +165,13 @@
|
|
|
165
165
|
color: var(--ui-button-accent);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
/* ===== 虚线:区域内添加内容 =====
|
|
168
|
+
/* ===== 虚线:区域内添加内容 =====
|
|
169
|
+
* 虚线也是「框」→ 取 `--ui-base-300`(全库唯一的「线 / 框」色,出处 `@manohub/theme` 的
|
|
170
|
+
* `default/colors.css` 头「三条口径」第 2 条;`base-400` 只服务勾选类小控件框体)。
|
|
171
|
+
* 改前取的是 `base-400`("虚线要深一档才看得见"),2026-09-30 按库级口径收敛。 */
|
|
169
172
|
.mh-button--dashed {
|
|
170
173
|
background: var(--ui-base-100);
|
|
171
|
-
border: 1px dashed var(--ui-base-
|
|
174
|
+
border: 1px dashed var(--ui-base-300);
|
|
172
175
|
color: var(--ui-button-accent);
|
|
173
176
|
}
|
|
174
177
|
.mh-button--dashed:hover,
|
|
@@ -109,15 +109,27 @@
|
|
|
109
109
|
* 这里只补**作者源没覆盖、会被 `<button>` 的原生 UA 样式漏进来的四项**(实测 Chrome 的 html.css:
|
|
110
110
|
* `button { align-items: flex-start; text-align: center; appearance: auto; font: 400 13.3333px Arial }`):
|
|
111
111
|
*
|
|
112
|
-
* - `align-items: center` ——
|
|
112
|
+
* - `align-items: center` —— 消费方把控件开成 flex 行时(如 `Select` 的 `.mh-select`)拦住 UA 的
|
|
113
|
+
* `flex-start`(它会把值 / 标签顶到上沿)。本件缺省**不给 `display`**,控件是 inline-block,此时
|
|
114
|
+
* 垂直居中来自浏览器对 `<button>` 内容的默认居中 —— 这条声明在那个形态下用不到,留着是与复合件同一份规格;
|
|
113
115
|
* - `text-align: inherit` —— UA 的 `center` 会**继承**给值文本;
|
|
114
116
|
* - `font-weight` / `line-height: inherit` —— UA 的 `font` 简写会把这两项从继承链上摘下来,
|
|
115
117
|
* 而作者源只写了 `font-family` 与 `font-size`(对比:`<input>` 的 UA 不设这两项,故输入框形态天然一致);
|
|
116
118
|
* - `appearance: none` —— 防仍画原生按钮皮肤的引擎(Safari 一类)。
|
|
117
119
|
*
|
|
120
|
+
* 外加一条**单行契约**(`white-space: nowrap` + `overflow: hidden` + `text-overflow: ellipsis`):
|
|
121
|
+
* 触发器与输入框同为**单行**控件,值再长也不换行。少了它,长值会在定高框里折成两行 ——
|
|
122
|
+
* 第二行溢出到框的下沿之外、右端图标也被挤走(实测:消费方把值当**裸文本**子节点渲染时必现,
|
|
123
|
+
* 如 `<Input as="button">{{ 部门名 }}</Input>` 放进窄表格单元格)。截断口径与
|
|
124
|
+
* `.mh-input__placeholder` / `.mh-select__value` 同源,这里补的是**控件本身**那一层
|
|
125
|
+
* —— 那两处只管各自的子节点,落在控件上的裸文本没有它们兜。
|
|
126
|
+
*
|
|
118
127
|
* 禁写:本块不碰框 / 色 / 尺寸 / 内距 —— 那些归 `.mh-input` 与表壳,重复声明就是又开一份会漂的规格。 */
|
|
119
128
|
.mh-input--trigger {
|
|
120
129
|
appearance: none;
|
|
130
|
+
white-space: nowrap;
|
|
131
|
+
overflow: hidden;
|
|
132
|
+
text-overflow: ellipsis;
|
|
121
133
|
align-items: center;
|
|
122
134
|
font-weight: inherit;
|
|
123
135
|
line-height: inherit;
|
|
@@ -28,9 +28,11 @@
|
|
|
28
28
|
--ui-input-font-size: var(--ui-font-body); /* 14:控件内容走正文档 */
|
|
29
29
|
--ui-input-font-size-sm: var(--ui-font-caption); /* 13 */
|
|
30
30
|
--ui-input-font-size-lg: var(--ui-font-heading); /* 16 */
|
|
31
|
-
/* 描边取 base-300
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
/* 描边取 base-300 —— 这**不是本件自选**,是库级口径:`base-300` 是全库唯一的「线 / 框」色
|
|
32
|
+
(输入框、按钮、表格、分页项、卡片、面板、分隔线一律取它),`base-400` 只服务勾选类小控件框体。
|
|
33
|
+
权威出处是 `@manohub/theme` 的 `default/colors.css` 头「三条口径」第 2 条(改值前先读那三条)。
|
|
34
|
+
括注的 `#e6e6e6` 是 **default** 套该槽位的值;**farris** 套同槽位取 Ne08(`#e8ebf2`)——
|
|
35
|
+
两套值不同、槽位语义相同,别按规范原文给输入框的 Ne04 来反推(那正是本件改前的 base-400)。 */
|
|
34
36
|
--ui-input-line: var(--ui-base-300);
|
|
35
37
|
--ui-input-line-hover: var(--ui-primary-hover); /* 滑过 / 聚焦转主色(规范 Th08) */
|
|
36
38
|
--ui-input-textarea-min-height: 56px; /* 多行最小高(两行正文 + 上下内距) */
|
|
@@ -253,7 +253,13 @@
|
|
|
253
253
|
--ui-pagination-font-size: var(--ui-font-body);
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
/* ===== 跳页(showQuickJumper) =====
|
|
256
|
+
/* ===== 跳页(showQuickJumper) =====
|
|
257
|
+
*
|
|
258
|
+
* 跳页框是**输入框**,画线取 `--ui-base-300` —— 与 `Input` 同一条口径(`base-300` 是全库唯一的
|
|
259
|
+
* 「线 / 框」色,出处 `@manohub/theme` 的 `default/colors.css` 头「三条口径」第 2 条;
|
|
260
|
+
* `base-400` 只服务勾选类小控件框体)。**改前取的是 `base-400`**:同一个「输入框」在库里因此
|
|
261
|
+
* 有过两种线色,属跨件口径漏改(2026-09-30 收敛)。
|
|
262
|
+
* 禁用档与 `Input` 同构:底转 `base-200`、线仍 `base-300` —— 即**靠底色区分禁用**,不再靠线深。 */
|
|
257
263
|
.mh-pagination__jump {
|
|
258
264
|
display: inline-flex;
|
|
259
265
|
align-items: center;
|
|
@@ -265,7 +271,7 @@
|
|
|
265
271
|
width: 56px;
|
|
266
272
|
height: var(--ui-pagination-item-size);
|
|
267
273
|
padding: 0 var(--ui-space-2);
|
|
268
|
-
border: 1px solid var(--ui-base-
|
|
274
|
+
border: 1px solid var(--ui-base-300);
|
|
269
275
|
border-radius: var(--ui-pagination-item-radius);
|
|
270
276
|
background: var(--ui-base-100);
|
|
271
277
|
font-family: inherit;
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
* 表壳挂 `.mh-popup-anchor`(`anchor-name`)、面板挂 `.mh-popup--anchored`(`position-anchor`),
|
|
24
24
|
* 锚点即整件;非锚定场景回落到 240px 兜底值。
|
|
25
25
|
*
|
|
26
|
+
* **面板有高度上限**:`--ui-select-panel-max-height`(默认 320px)钉在面板根上,超出即内容区
|
|
27
|
+
* (`.mh-select__body`)滚动、搜索行常驻 —— 列表与树两形态共用这一份,形态里不许各写限高。
|
|
28
|
+
*
|
|
26
29
|
* **一处有色差异**:规范的选中行是灰底(Ne10),本仓的「选中」统一走**主色浅底**
|
|
27
30
|
* `--ui-primary-subtle`(与列表 / 树的选中态同一语义槽位),否则悬停(Ne11)与选中(Ne10)两档灰
|
|
28
31
|
* 在本仓有限的灰阶里挤成同色、看不出选中。
|
|
@@ -106,10 +109,25 @@
|
|
|
106
109
|
background: var(--ui-base-200);
|
|
107
110
|
color: var(--ui-base-content-muted);
|
|
108
111
|
}
|
|
109
|
-
/* ===== 面板皮肤(与操作层并列组合:`class="mh-popup mh-select__panel mh-popup--anchored"`) =====
|
|
112
|
+
/* ===== 面板皮肤(与操作层并列组合:`class="mh-popup mh-select__panel mh-popup--anchored"`) =====
|
|
113
|
+
**面板是列容器 + 限高**:搜索行常驻(不滚),下一条的 `.mh-select__body` 承担滚动 ——
|
|
114
|
+
列表与树两形态因此共用同一份「长内容不撑破面板」的规格。
|
|
115
|
+
(历史症状:限高原先只写在树体 `.mh-select__tree` 上,而列表形态的选项是面板的**直接子节点**,
|
|
116
|
+
面板会被一路撑高;实测 60 条选项的面板高 2212px、视口只有 925px。) */
|
|
110
117
|
.mh-select__panel {
|
|
111
118
|
/* 锚 = `.mh-popup-anchor`(挂在表壳上)→ 面板与**整件**同宽;非锚定场景回落 240 */
|
|
112
119
|
width: anchor-size(width, 240px);
|
|
120
|
+
/* 列容器:搜索行 `flex: none` + 内容区 `flex: 1 / min-height: 0`。
|
|
121
|
+
⚠️ 加 `display: flex` 会以作者源压过 UA 给**收起态**定的 `display: none`,收起态靠操作层
|
|
122
|
+
`operations/popup.css` 里那条 `:is()` 兜底(把收起的浮层重新钉成 `display: none`,
|
|
123
|
+
特异性 0-3-0、高于本行)—— **那条兜底不能删**(与 `dialog.css` 同一处坑)。 */
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-direction: column;
|
|
126
|
+
/* 限高(**含搜索行**):超出即内容区滚动。面板仍贴锚点,只是不再无限长 */
|
|
127
|
+
max-height: var(--ui-select-panel-max-height);
|
|
128
|
+
/* UA 给 `[popover]` 的是 `overflow: auto`,必须归零:内容区已是滚动容器,
|
|
129
|
+
留一层就会与它叠成两条滚动条(同一个面板上实测会同时出现)。 */
|
|
130
|
+
overflow: hidden;
|
|
113
131
|
padding: var(--ui-space-1) 0;
|
|
114
132
|
border-radius: var(--ui-select-radius);
|
|
115
133
|
background: var(--ui-base-100);
|
|
@@ -119,6 +137,15 @@
|
|
|
119
137
|
box-shadow: var(--ui-shadow-popover);
|
|
120
138
|
}
|
|
121
139
|
|
|
140
|
+
/* 内容区:**面板里唯一的滚动容器**(列表体的选项行 / 树体的树都装在它里面)。
|
|
141
|
+
`min-height: 0` 是这一档生效的前提:列容器里弹性项的可收缩下限默认取内容高,
|
|
142
|
+
不给它,内容就永远压不下去、上面的限高形同虚设(与 `panel.css` 的 `.mh-panel__body` 同款)。 */
|
|
143
|
+
.mh-select__body {
|
|
144
|
+
flex: 1;
|
|
145
|
+
min-height: 0;
|
|
146
|
+
overflow-y: auto;
|
|
147
|
+
}
|
|
148
|
+
|
|
122
149
|
/* 选项行:滑过 / 选中 / 禁用 / 提示 / 空态 */
|
|
123
150
|
.mh-select__option {
|
|
124
151
|
position: relative;
|
|
@@ -213,21 +240,24 @@
|
|
|
213
240
|
cursor: default;
|
|
214
241
|
}
|
|
215
242
|
|
|
216
|
-
/*
|
|
243
|
+
/* 搜索行(**消费方显式 `searchable` 时才由组件渲染**,件不按选项条数自动决断)。
|
|
244
|
+
`flex: none`:它是面板的**常驻**行 —— 面板限高时收缩与滚动都归内容区,
|
|
245
|
+
少了它,搜索行会被一起压扁(列容器里它默认是可收缩的)。 */
|
|
217
246
|
.mh-select__search {
|
|
247
|
+
flex: none;
|
|
218
248
|
padding: var(--ui-space-2) var(--ui-space-2) var(--ui-space-1);
|
|
219
249
|
}
|
|
220
250
|
|
|
221
251
|
/* ===== 树体(树选择 `SelectTree` 的面板内容)=====
|
|
222
252
|
边界要认清楚:**面板皮肤归本文件**(上一节),**树的行态归 tree 面**(`.mh-tree__*` 那一族,
|
|
223
253
|
本文件一行都不碰 —— 跨族类名须先登记跨面豁免,这里不走那条路)。
|
|
224
|
-
|
|
225
|
-
行高 / 缩进 / 滑过与选中底色全部沿用 tree
|
|
254
|
+
故本件只做「把树安放进面板」这一件事:内距。
|
|
255
|
+
行高 / 缩进 / 滑过与选中底色全部沿用 tree 面,两处形态不会漂移;
|
|
256
|
+
**限高与滚动也不在这里** —— 那是面板(`.mh-select__panel`)与面板内容区(`.mh-select__body`)的事,
|
|
257
|
+
列表与树两形态共用同一份(形态里各写一条就会漂移,本件此前正是自己写了一条)。 */
|
|
226
258
|
.mh-select__tree {
|
|
227
259
|
/* 行底满宽、只有内容缩进(缩进公式在 tree 面),故左右各留一个最小呼吸位 */
|
|
228
260
|
padding: 0 var(--ui-space-1) var(--ui-space-1);
|
|
229
|
-
max-height: var(--ui-select-tree-max-height);
|
|
230
|
-
overflow-y: auto;
|
|
231
261
|
}
|
|
232
262
|
|
|
233
263
|
/* 命中高亮(搜索命中的那段文字,由组件的 `renderLabel` 落进 `.mh-tree__label` 槽)。
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
--ui-select-option-selected: var(--ui-primary-subtle); /* 见头部「有色差异」 */
|
|
31
31
|
--ui-select-chip-height: 24px; /* 多选标签(规范复用标签族 24 档) */
|
|
32
32
|
--ui-select-icon-size: var(--ui-font-icon); /* 下拉箭头:常规图标档 */
|
|
33
|
-
/*
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
/* 面板限高(**列表与树两形态共用**):面板整体(含搜索行)不超此高,超出即内容区滚动。
|
|
34
|
+
320px ≈ 8 行选项(选项行 36 + 面板上下内距 8);树形态可见行数按 tree 面的节距 40 折算
|
|
35
|
+
(行高 34 + 行距 6),有无搜索行会差一行上下。要改高度改这枚令牌,不要在业务侧压 max-height。 */
|
|
36
|
+
--ui-select-panel-max-height: 320px;
|
|
36
37
|
}
|
|
@@ -27,9 +27,11 @@
|
|
|
27
27
|
--ui-steps-connector-length: 58.383px; /* 规范实测最小段长(其余由 flex 撑开) */
|
|
28
28
|
--ui-steps-connector-width: 2px;
|
|
29
29
|
--ui-steps-connector-arrow: 6px;
|
|
30
|
-
/*
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
/* 连接线取 base-300(全库唯一的「线 / 框」色,见 `@manohub/theme` 的 `default/colors.css`
|
|
31
|
+
头「三条口径」第 2 条)—— 改前取的是 base-400。设计稿里**只有三个向导页**画了连接线
|
|
32
|
+
(`#cdd2de`)而向导页已判定为旧稿,四个品牌页没有步骤条样张 → 按库级口径取线色,
|
|
33
|
+
不做推测性改动(这枚令牌的档位语义与连接线宽度 / 燕尾尺寸一样,只在「线」这一族内变)。 */
|
|
34
|
+
--ui-steps-connector-line: var(--ui-base-300);
|
|
33
35
|
--ui-steps-connector-line-active: var(--ui-primary); /* 规范 #4999fe */
|
|
34
36
|
--ui-steps-title-gap: var(--ui-space-3); /* 节点与标题间距 12 */
|
|
35
37
|
--ui-steps-seg-width: 140px;
|
|
@@ -132,8 +132,10 @@
|
|
|
132
132
|
appearance: none;
|
|
133
133
|
pointer-events: none;
|
|
134
134
|
}
|
|
135
|
+
/* 悬停底取 `base-200`(中性面)—— 改前是 `base-50`:那档只比内容底低 1.7 L*,
|
|
136
|
+
在多数屏幕上看不出悬停反馈(`base-50` 不再作为「更轻底」的默认去处,见 colors 口径第 1 条)。 */
|
|
135
137
|
.mh-tag--select:hover {
|
|
136
|
-
background: var(--ui-base-
|
|
138
|
+
background: var(--ui-base-200);
|
|
137
139
|
border: 1px solid var(--ui-base-300);
|
|
138
140
|
}
|
|
139
141
|
.mh-tag--select:has(input:focus-visible) {
|
|
@@ -160,11 +162,13 @@
|
|
|
160
162
|
cursor: not-allowed;
|
|
161
163
|
}
|
|
162
164
|
|
|
163
|
-
/* ===== 新增档(虚线框 + 加号由 @manohub/icon 的 plus 渲染)=====
|
|
165
|
+
/* ===== 新增档(虚线框 + 加号由 @manohub/icon 的 plus 渲染)=====
|
|
166
|
+
* 虚线框取 `--ui-base-300`(全库唯一的「线 / 框」色,见 `@manohub/theme` 的 `default/colors.css`
|
|
167
|
+
* 头「三条口径」第 2 条);改前取 `base-400`,2026-09-30 按口径收敛。 */
|
|
164
168
|
.mh-tag--add {
|
|
165
169
|
height: var(--ui-tag-height);
|
|
166
170
|
padding: 0 var(--ui-space-2);
|
|
167
|
-
border: 1px dashed var(--ui-base-
|
|
171
|
+
border: 1px dashed var(--ui-base-300);
|
|
168
172
|
border-radius: var(--ui-radius-full);
|
|
169
173
|
background: transparent;
|
|
170
174
|
color: var(--ui-base-content-muted);
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
--ui-upload-padding: 32px; /* 规范间距档 8(本仓间距刻度最大 24,故按实测值给) */
|
|
19
19
|
--ui-upload-zone-max-width: 500px; /* 规范拖拽区帧宽 */
|
|
20
20
|
--ui-upload-radius: var(--ui-radius-control);
|
|
21
|
-
--ui-upload-line: var(--ui-base-
|
|
21
|
+
--ui-upload-line: var(--ui-base-300); /* 虚线边:全库唯一的「线 / 框」色(`base-400` 只服务勾选类小控件) */
|
|
22
22
|
--ui-upload-text: var(--ui-base-content-muted); /* 主提示 Te05 */
|
|
23
23
|
--ui-upload-hint: var(--ui-base-content-subtle); /* 副提示 Te07 */
|
|
24
24
|
--ui-upload-meta-size: var(--ui-font-caption); /* 大小/缩略图文字:规范 12,本仓字阶最小 13 */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@manohub/ui",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "AIHub 自绘组件层:不依赖 farris 的手写 Vue TSX 组件 + 自带令牌层(Farris 规范视觉 · daisyUI 式语义令牌 · shadcn 式 API 词汇)。",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"vue": "^3.5.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@manohub/icon": "^0.10.
|
|
49
|
+
"@manohub/icon": "^0.10.1"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/node": "^24.0.0",
|