@manohub/ui 0.7.1 → 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 (37) hide show
  1. package/README.md +1 -1
  2. package/dist/components/button/index.d.ts +6 -0
  3. package/dist/components/card/index.d.ts +1 -1
  4. package/dist/components/form/index.d.ts +28 -46
  5. package/dist/components/form/index.js +3 -21
  6. package/dist/components/input/index.d.ts +74 -6
  7. package/dist/components/input/index.js +13 -1
  8. package/dist/components/list-view/index.d.ts +1 -1
  9. package/dist/components/pagination/index.d.ts +2 -2
  10. package/dist/components/pagination/index.js +1 -1
  11. package/dist/components/query-state/index.d.ts +18 -2
  12. package/dist/components/select/index.d.ts +32 -30
  13. package/dist/components/select/index.js +46 -82
  14. package/dist/components/select/select-list.d.ts +125 -0
  15. package/dist/components/select/select-list.js +95 -0
  16. package/dist/components/select/select-panel.d.ts +106 -0
  17. package/dist/components/select/select-panel.js +72 -0
  18. package/dist/components/select/select-shared.d.ts +64 -0
  19. package/dist/components/select/select-shared.js +24 -0
  20. package/dist/components/select/select-tree-filter.d.ts +61 -0
  21. package/dist/components/select/select-tree-filter.js +63 -0
  22. package/dist/components/select/select-tree.d.ts +274 -0
  23. package/dist/components/select/select-tree.js +263 -0
  24. package/dist/components/select/select-trigger.d.ts +132 -0
  25. package/dist/components/select/select-trigger.js +101 -0
  26. package/dist/components/steps/index.d.ts +1 -1
  27. package/dist/components/table/index.d.ts +25 -4
  28. package/dist/components/text/index.d.ts +7 -2
  29. package/dist/components/tree/index.d.ts +28 -2
  30. package/dist/components/tree/index.js +14 -1
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.js +2 -0
  33. package/dist/styles/components/form.css +0 -21
  34. package/dist/styles/components/input.css +25 -0
  35. package/dist/styles/components/select.css +21 -0
  36. package/dist/styles/components/select.tokens.css +3 -0
  37. package/package.json +2 -2
package/README.md CHANGED
@@ -58,7 +58,7 @@ import { Card, Form, Tree, Checkbox } from '@manohub/ui'
58
58
  | 容器与分区 | `Page`(页面级骨架:`.Header` / `.Body` / `.Footer` / `.Filter` / `.Toolbar` / `.Split`)、`Panel`(+ `.Header` / `.Body` / `.Footer`)、`Card`(+ 三件)、`Nav`、`Layout`(`Layout.Row` 排「行」/ `Layout.Column` 排「列」:轴向 × 间距档 × 对齐) |
59
59
  | 数据展示 | `Tree`、`ListView`(+ `.CardItem`)、`Badge`、`Tag`(常规 / 状态 / 可选 / 新增 / 筛选片) |
60
60
  | 展示 | `Avatar`(+ `.Group`)、`Skeleton`(行 / 列表 / 块状) |
61
- | 表单基础(**自绘**) | `Button`(+ `.Group` / `.Divider`;五型 / 小号 / 加载 / 菜单按钮 / 连体切换)、`Input`(+ `.Group` / `.Chip` / `.Display` / `.Textarea`;清除 / 可见性切换 / 校验)、`Search`(搜索框:值 / 搜索 / 清空三条回调)、`Select`(单选 / 多选 / 搜索 / 加载;面板用原生 popover)、`Switch`(表格档 / 表单档) |
61
+ | 表单基础(**自绘**) | `Button`(+ `.Group` / `.Divider`;五型 / 小号 / 加载 / 菜单按钮 / 连体切换)、`Input`(+ `.Group` / `.Chip` / `.Display` / `.Textarea`;清除 / 可见性切换 / 校验)、`Search`(搜索框:值 / 搜索 / 清空三条回调)、`Select`(单选 / 多选 / 搜索 / 加载;面板用原生 popover)、`SelectTree`(树形态:仅叶子可选 + 本地过滤 + 命中高亮;与 `Select` 平级导出、共用触发器与面板)、`Switch`(表格档 / 表单档) |
62
62
  | 表单与选择 | `Form`(+ `.Header` / `.Item`;标签体系、成员归位、`v-model` 值收集与命令式 `clear()`)、`Filter`(+ `.Item`;`Form` 的壳:条件字段写成子件、控件自放、查询/重置与快照归一)、`Checkbox`、`Radio`(+ `.Group` / `.Card`;卡片式单选)、`Capsule`(胶囊选择器;胶囊 / 方形两档外形,与 `Tabs` 组合即页签) |
63
63
  | 反馈与三态 | `Notice`(条状提示)、`Notification`(通知卡:标题 + 头像 / 标签 / 操作 / 关闭位)、`QueryState`(三态容器;内建空 / 错误插画取自设计交付的四张 SVG,原样内联在 `src/utils/state-illustration.ts`)、`Loading`(转圈 + 遮罩:行内 / 区域 / 全屏三形态) |
64
64
  | 反馈与引导(**自绘**) | `Toast`(消息提示:语义四档 / loading + 可选动作 + 自动消失计时 + `.Stack` 堆叠)、`Steps`(步骤条:节点式 / 燕尾段式 / `.Progress` 进度条) |
@@ -7,6 +7,11 @@ import type { ControlSize } from '../control-size';
7
7
  * `danger` 保留在枚举里是**向后兼容**:它等价于 `variant="primary"` 叠加 `danger` 布尔,
8
8
  * 语义上「危险」是色彩角色(与主色正交),不是第六种结构形态 ——
9
9
  * 故新代码请用 `danger` 布尔修饰任意型,`variant="danger"` 只在旧调用点继续有效。
10
+ *
11
+ * `text` 与 `link` 都是无底色档,但**语义不同,别混**:
12
+ * `text` = 「行内文本操作」(编辑 / 删除 / 更多 / 展开,无下划线);
13
+ * `link` = 「跳转语义」(带下划线,像超链接)。
14
+ *「行内可点文字」一律用它 → `text`。
10
15
  */
11
16
  export type ButtonVariant = 'primary' | 'secondary' | 'dashed' | 'danger' | 'text' | 'link' | 'icon' | 'off';
12
17
  /** 外形档:`round` = 胶囊;`circle` = 正圆(配 `variant="icon"` 用) */
@@ -28,6 +33,7 @@ export type ButtonShape = 'default' | 'round' | 'circle';
28
33
  * @example 六型
29
34
  * <Button variant="primary" onClick={save}>保存</Button>
30
35
  * <Button variant="secondary">取消</Button>
36
+ * <Button variant="text">编辑</Button>
31
37
  * <Button variant="link">查看详情</Button>
32
38
  *
33
39
  * @example 修饰叠加
@@ -13,7 +13,7 @@ export type CardHeaderIcon = string | (() => VNodeChild);
13
13
  * 也不得在应用侧写这四类属性 —— 需要不同形态就用本件的维度(`variant` / `interactive` /
14
14
  * `selected` / `disabled`),或提为骨架层待建件。
15
15
  *
16
- * ## 几何来源(portal 技能市场卡片实测,逐项落 `--ui-*` 令牌)
16
+ * ## 几何来源(技能市场卡片实测,逐项落 `--ui-*` 令牌)
17
17
  *
18
18
  * - 圆角 8(`--ui-radius-md`)、内距 20(`--mh-card-padding`,原型 `p-5`)
19
19
  * - 底 `--ui-base-100`、弱描边 `--ui-base-300`(原型 `border-border`)
@@ -133,21 +133,15 @@ export interface FormItemProps {
133
133
  /** 在两列表单里跨满整行(说明行 / 卡片组 / 表格 / 文本域等整块内容) */
134
134
  fullWidth?: boolean;
135
135
  /**
136
- * **只读文本形态**:传了它(含空串)即不走控件插槽,改为在控件位渲染纯文本 ——
137
- * 用于「摘要 / 详情」这类静态表单行(同一个表单里 label 列、行距、错误、说明全部复用)。
136
+ * **只读展示行**:把 label 降一档**次要色**(与值拉开层级),内容仍走默认插槽。
138
137
  *
139
- * 空值(`''` / 全空白)由组件统一显示 `—`,消费方不必每处写 `value || '—'`;
140
- * 换行符原样保留(SQL where 这类多行文本回显不会被压成一行)。
138
+ * 用于「摘要 / 详情」这类只读行 —— 控件位可以是文本值,也可以是徽标 / 链接这类非文本内容:
141
139
  *
142
- * 传了它这一行的 label 会降一档**次要色**(与正文色的值拉开层级),控件行不受影响。
143
- */
144
- text?: string;
145
- /**
146
- * **只读展示行**:只把 label 弱化(次要色),内容仍走默认插槽 ——
147
- * 用于控件位不是纯文本的只读内容(徽标 / 链接这类)。
148
- *
149
- * 纯文本一律用 `text`(它自带只读语义,不必再传本开关);本开关与 `text` 互斥不会冲突 ——
150
- * 两个都给时 `text` 优先(`readonly` 只是把 label 的弱化带过去)。
140
+ * - **文本值**:控件位放 `<Input model-value={…} readonly bordered={false} />` ——
141
+ * 几何复用输入面(与可编辑输入框**同高、同内距**,值文字落在同一条竖线与中线上),
142
+ * 并且「无框 + 只读」会自动退掉输入框的交互线索(不显悬停底与聚焦框环,仍可选中复制);
143
+ * 空值占位由业务侧自己拼(如 `model-value="v || '—'"`);
144
+ * - **非文本内容**:内容直接从默认插槽进,`readonly` 只负责弱化 label。
151
145
  */
152
146
  readonly?: boolean;
153
147
  /**
@@ -199,14 +193,24 @@ export interface FormItemProps {
199
193
  * `{ y, x }` 上下 / 左右分别给,缺省 0(贴容器边)。页面里的表单在 `Panel.Body` 里、内距由那块给;
200
194
  * **弹窗 / 抽屉里没有别的容器**给内距,就用表单自带的 `padding={{ y: 16, x: 20 }}`。
201
195
  *
202
- * ## 静态表单(摘要 / 详情)
196
+ * ## 只读值(摘要 / 详情)
197
+ *
198
+ * 「向导最后一步的确认摘要」「详情弹窗」不需要另起一套描述列表件 —— 只用表单行本身,
199
+ * label 列宽、行距、错误与说明全部复用。控件位放**只读 + 无框**的输入框即可(本件不给
200
+ * 「静态文本」形态:那套几何与输入框迟早会漂开 —— 从前它没有高度与内距,值文字既不在
201
+ * 输入框那条竖线上、也不在同一条中线上):
202
+ *
203
+ * ```tsx
204
+ * <Form.Item label="创建人" readonly>
205
+ * <Input model-value={row.creator || '—'} readonly bordered={false} />
206
+ * </Form.Item>
207
+ * ```
203
208
  *
204
- * `Form.Item` 传 `text` 即切成**只读文本行**,三类行可以在同一个表单里混排 ——
205
- * label 列宽、行距、错误与说明全部共用,所以「向导最后一步的确认摘要」「详情弹窗」不需要
206
- * 另起一套描述列表件(自绘表格的 label/value 几何与表单行迟早会漂开)。
207
- * 空值占位 `—`、长文案折行、换行符保留都由组件给;只读行的 label 自动降一档次要色,
208
- * 字段名与取值一眼分得开(应用侧写不了 `color`,这层区分只能由组件给)。
209
- * 控件位要放徽标 / 链接这类**非文本**只读内容时传 `readonly`:内容走默认插槽,label 的弱化照旧。
209
+ * - **几何与可编辑行完全一致**:值文字与输入框内的值同高、同左缘、同中线,混排时节奏整齐;
210
+ * - **没有「可编辑」的线索**:不显悬停底色、不给聚焦边框与主色环,但仍可点进去选中 / 复制;
211
+ * - **空值占位归业务**:组件不再替你把 `''` 显示成 `—`,按上面的写法自己拼;
212
+ * - **label 弱化**:传 `readonly` 把 label 降一档次要色,字段名与取值一眼分得开;
213
+ * - 控件位是徽标 / 链接这类**非文本**只读内容时同样传 `readonly`:内容直接走默认插槽。
210
214
  *
211
215
  * @example
212
216
  * <Form labelWidth={120} columns={2}>
@@ -259,12 +263,7 @@ export declare const FormItem: import("vue").DefineComponent<import("vue").Extra
259
263
  type: BooleanConstructor;
260
264
  default: boolean;
261
265
  };
262
- /** `undefined` 与「传了空串」必须区分:前者走插槽(控件形态),后者是「值为空的只读行」 */
263
- text: {
264
- type: StringConstructor;
265
- default: undefined;
266
- };
267
- /** 只读展示行(label 弱化,内容走插槽):用于控件位是徽标 / 链接这类非文本的只读内容 */
266
+ /** 只读展示行(label 弱化):文本值放 Input 的只读 + 无框形态,非文本内容走默认插槽 */
268
267
  readonly: {
269
268
  type: BooleanConstructor;
270
269
  default: boolean;
@@ -312,12 +311,7 @@ export declare const FormItem: import("vue").DefineComponent<import("vue").Extra
312
311
  type: BooleanConstructor;
313
312
  default: boolean;
314
313
  };
315
- /** `undefined` 与「传了空串」必须区分:前者走插槽(控件形态),后者是「值为空的只读行」 */
316
- text: {
317
- type: StringConstructor;
318
- default: undefined;
319
- };
320
- /** 只读展示行(label 弱化,内容走插槽):用于控件位是徽标 / 链接这类非文本的只读内容 */
314
+ /** 只读展示行(label 弱化):文本值放 Input 的只读 + 无框形态,非文本内容走默认插槽 */
321
315
  readonly: {
322
316
  type: BooleanConstructor;
323
317
  default: boolean;
@@ -328,7 +322,6 @@ export declare const FormItem: import("vue").DefineComponent<import("vue").Extra
328
322
  default: undefined;
329
323
  };
330
324
  }>> & Readonly<{}>, {
331
- text: string;
332
325
  name: string;
333
326
  label: string;
334
327
  error: string;
@@ -714,12 +707,7 @@ export declare const Form: {
714
707
  type: BooleanConstructor;
715
708
  default: boolean;
716
709
  };
717
- /** `undefined` 与「传了空串」必须区分:前者走插槽(控件形态),后者是「值为空的只读行」 */
718
- text: {
719
- type: StringConstructor;
720
- default: undefined;
721
- };
722
- /** 只读展示行(label 弱化,内容走插槽):用于控件位是徽标 / 链接这类非文本的只读内容 */
710
+ /** 只读展示行(label 弱化):文本值放 Input 的只读 + 无框形态,非文本内容走默认插槽 */
723
711
  readonly: {
724
712
  type: BooleanConstructor;
725
713
  default: boolean;
@@ -767,12 +755,7 @@ export declare const Form: {
767
755
  type: BooleanConstructor;
768
756
  default: boolean;
769
757
  };
770
- /** `undefined` 与「传了空串」必须区分:前者走插槽(控件形态),后者是「值为空的只读行」 */
771
- text: {
772
- type: StringConstructor;
773
- default: undefined;
774
- };
775
- /** 只读展示行(label 弱化,内容走插槽):用于控件位是徽标 / 链接这类非文本的只读内容 */
758
+ /** 只读展示行(label 弱化):文本值放 Input 的只读 + 无框形态,非文本内容走默认插槽 */
776
759
  readonly: {
777
760
  type: BooleanConstructor;
778
761
  default: boolean;
@@ -783,7 +766,6 @@ export declare const Form: {
783
766
  default: undefined;
784
767
  };
785
768
  }>> & Readonly<{}>, {
786
- text: string;
787
769
  name: string;
788
770
  label: string;
789
771
  error: string;
@@ -3,7 +3,6 @@ import { mergePartStyle } from "../part-padding.js";
3
3
  const FORM_LABEL_WIDTH = /* @__PURE__ */ Symbol("mh-form-label-width");
4
4
  const FORM_CONTROL_WIDTH = /* @__PURE__ */ Symbol("mh-form-control-width");
5
5
  const FORM_LABEL_ALIGN = /* @__PURE__ */ Symbol("mh-form-label-align");
6
- const FORM_TEXT_EMPTY = "—";
7
6
  const FORM_STORE = /* @__PURE__ */ Symbol("mh-form-store");
8
7
  const FormItem = /* @__PURE__ */ defineComponent({
9
8
  name: "FormItem",
@@ -46,12 +45,7 @@ const FormItem = /* @__PURE__ */ defineComponent({
46
45
  type: Boolean,
47
46
  default: false
48
47
  },
49
- /** `undefined` 与「传了空串」必须区分:前者走插槽(控件形态),后者是「值为空的只读行」 */
50
- text: {
51
- type: String,
52
- default: void 0
53
- },
54
- /** 只读展示行(label 弱化,内容走插槽):用于控件位是徽标 / 链接这类非文本的只读内容 */
48
+ /** 只读展示行(label 弱化):文本值放 Input 的只读 + 无框形态,非文本内容走默认插槽 */
55
49
  readonly: {
56
50
  type: Boolean,
57
51
  default: false
@@ -92,9 +86,6 @@ const FormItem = /* @__PURE__ */ defineComponent({
92
86
  "--mh-form-label-width": `${labelWidth}px`
93
87
  } : null].filter(Boolean);
94
88
  const showLabel = !!props.label || props.required;
95
- const hasText = props.text !== void 0;
96
- const textIsEmpty = hasText && !props.text.trim();
97
- const isReadonlyRow = hasText || props.readonly;
98
89
  return createVNode("div", {
99
90
  "class": [
100
91
  "mh-form-item",
@@ -107,7 +98,7 @@ const FormItem = /* @__PURE__ */ defineComponent({
107
98
  // 右对齐是基类默认值,只有左对齐需要额外的修饰类(两处都挂会让「谁胜出」依赖源码顺序)
108
99
  labelAlign === "left" ? "mh-form-item--label-left" : "",
109
100
  // 只读展示行:label 降一档为次要色,与值拉开层级(样式见 `.mh-form-item--readonly .mh-form-item__label`)
110
- isReadonlyRow ? "mh-form-item--readonly" : "",
101
+ props.readonly ? "mh-form-item--readonly" : "",
111
102
  attrs.class
112
103
  ].filter(Boolean).join(" "),
113
104
  "data-invalid": props.error ? "true" : "false",
@@ -123,16 +114,7 @@ const FormItem = /* @__PURE__ */ defineComponent({
123
114
  "style": controlWidth ? {
124
115
  maxWidth: `${controlWidth}px`
125
116
  } : void 0
126
- }, [hasText ? (
127
- /*
128
- * 只读文本形态(摘要 / 详情行):控件位直接渲染业务值,占位与换行规则都在组件里
129
- * —— 应用侧不再自绘 label/value 表格(那套几何与表单行迟早会漂开)。
130
- */
131
- createVNode("div", {
132
- "class": "mh-form-item__text",
133
- "data-empty": textIsEmpty ? "true" : "false"
134
- }, [textIsEmpty ? FORM_TEXT_EMPTY : props.text])
135
- ) : slots.default?.(scope())]), props.error ? createVNode("div", {
117
+ }, [slots.default?.(scope())]), props.error ? createVNode("div", {
136
118
  "class": "mh-form-item__error"
137
119
  }, [props.error]) : null, props.hint ? createVNode("div", {
138
120
  "class": "mh-form-item__hint"
@@ -68,6 +68,14 @@ export declare const Input: import("vue").DefineComponent<import("vue").ExtractP
68
68
  type: BooleanConstructor;
69
69
  default: boolean;
70
70
  };
71
+ /**
72
+ * **只读**:值可选中 / 复制,但不能改。
73
+ *
74
+ * 与 `bordered={false}` 组合即**只读值档**(摘要 / 详情里回显业务值,也是 `Form.Item`
75
+ * 的 `readonly` 行推荐的写法):几何与可编辑输入框**完全一致**(同高、同内距、值文字同左缘),
76
+ * 但滑过不亮浅底、聚焦不给边框与主色环 —— 一眼看去是「值」而不是「输入位」。
77
+ * 只读 ≠ 禁用:它照旧可聚焦、可选中复制,也不变灰。
78
+ */
71
79
  readonly: {
72
80
  type: BooleanConstructor;
73
81
  default: boolean;
@@ -87,7 +95,10 @@ export declare const Input: import("vue").DefineComponent<import("vue").ExtractP
87
95
  type: BooleanConstructor;
88
96
  default: boolean;
89
97
  };
90
- /** 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底) */
98
+ /**
99
+ * 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底,靠悬停与聚焦给反馈)。
100
+ * 配 `readonly` 即**只读值档**:连悬停底与聚焦框环都退掉(见 `readonly`)。
101
+ */
91
102
  bordered: {
92
103
  type: BooleanConstructor;
93
104
  default: boolean;
@@ -167,6 +178,14 @@ export declare const Input: import("vue").DefineComponent<import("vue").ExtractP
167
178
  type: BooleanConstructor;
168
179
  default: boolean;
169
180
  };
181
+ /**
182
+ * **只读**:值可选中 / 复制,但不能改。
183
+ *
184
+ * 与 `bordered={false}` 组合即**只读值档**(摘要 / 详情里回显业务值,也是 `Form.Item`
185
+ * 的 `readonly` 行推荐的写法):几何与可编辑输入框**完全一致**(同高、同内距、值文字同左缘),
186
+ * 但滑过不亮浅底、聚焦不给边框与主色环 —— 一眼看去是「值」而不是「输入位」。
187
+ * 只读 ≠ 禁用:它照旧可聚焦、可选中复制,也不变灰。
188
+ */
170
189
  readonly: {
171
190
  type: BooleanConstructor;
172
191
  default: boolean;
@@ -186,7 +205,10 @@ export declare const Input: import("vue").DefineComponent<import("vue").ExtractP
186
205
  type: BooleanConstructor;
187
206
  default: boolean;
188
207
  };
189
- /** 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底) */
208
+ /**
209
+ * 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底,靠悬停与聚焦给反馈)。
210
+ * 配 `readonly` 即**只读值档**:连悬停底与聚焦框环都退掉(见 `readonly`)。
211
+ */
190
212
  bordered: {
191
213
  type: BooleanConstructor;
192
214
  default: boolean;
@@ -276,6 +298,7 @@ export interface TextareaProps {
276
298
  /** 尺寸档 */
277
299
  size?: ControlSize;
278
300
  disabled?: boolean;
301
+ /** 只读(配 `bordered={false}` 即只读值档,口径见 `Input` 的同名 prop) */
279
302
  readonly?: boolean;
280
303
  /** 校验态(下发 `aria-invalid`) */
281
304
  invalid?: boolean;
@@ -289,7 +312,15 @@ export interface TextareaProps {
289
312
  autofocus?: boolean;
290
313
  onChange?: (v: string) => void;
291
314
  }
292
- /** 多行输入(同一份面;结构与单行不同,故单列一个件) */
315
+ /**
316
+ * 多行输入(同一份面;结构与单行不同,故单列一个件)。
317
+ *
318
+ * ⚠️ 本件**没有** `mono` 档(`Input` 有)。要等宽的输入框别拿 `Text mono` 去包 —— 那会丢掉输入能力;
319
+ * 该写法目前只能保留一条带 `font-family: var(--ui-font-mono)` 的应用侧样式登记(缺口见包内 `docs/kit-gaps.md`)。
320
+ *
321
+ * @example 备注框:自适应 2~6 行 + 限长计数
322
+ * <Textarea modelValue={note} autoSize={{ minRows: 2, maxRows: 6 }} maxlength={500} showCount onChange={setNote} />
323
+ */
293
324
  export declare const Textarea: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
294
325
  modelValue: {
295
326
  type: StringConstructor;
@@ -319,6 +350,7 @@ export declare const Textarea: import("vue").DefineComponent<import("vue").Extra
319
350
  type: BooleanConstructor;
320
351
  default: boolean;
321
352
  };
353
+ /** 只读:值可选中 / 复制;配 `bordered={false}` 即只读值档(口径见 `Input` 的同名 prop) */
322
354
  readonly: {
323
355
  type: BooleanConstructor;
324
356
  default: boolean;
@@ -382,6 +414,7 @@ export declare const Textarea: import("vue").DefineComponent<import("vue").Extra
382
414
  type: BooleanConstructor;
383
415
  default: boolean;
384
416
  };
417
+ /** 只读:值可选中 / 复制;配 `bordered={false}` 即只读值档(口径见 `Input` 的同名 prop) */
385
418
  readonly: {
386
419
  type: BooleanConstructor;
387
420
  default: boolean;
@@ -463,6 +496,14 @@ export declare const InputCompound: {
463
496
  type: BooleanConstructor;
464
497
  default: boolean;
465
498
  };
499
+ /**
500
+ * **只读**:值可选中 / 复制,但不能改。
501
+ *
502
+ * 与 `bordered={false}` 组合即**只读值档**(摘要 / 详情里回显业务值,也是 `Form.Item`
503
+ * 的 `readonly` 行推荐的写法):几何与可编辑输入框**完全一致**(同高、同内距、值文字同左缘),
504
+ * 但滑过不亮浅底、聚焦不给边框与主色环 —— 一眼看去是「值」而不是「输入位」。
505
+ * 只读 ≠ 禁用:它照旧可聚焦、可选中复制,也不变灰。
506
+ */
466
507
  readonly: {
467
508
  type: BooleanConstructor;
468
509
  default: boolean;
@@ -482,7 +523,10 @@ export declare const InputCompound: {
482
523
  type: BooleanConstructor;
483
524
  default: boolean;
484
525
  };
485
- /** 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底) */
526
+ /**
527
+ * 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底,靠悬停与聚焦给反馈)。
528
+ * 配 `readonly` 即**只读值档**:连悬停底与聚焦框环都退掉(见 `readonly`)。
529
+ */
486
530
  bordered: {
487
531
  type: BooleanConstructor;
488
532
  default: boolean;
@@ -596,6 +640,14 @@ export declare const InputCompound: {
596
640
  type: BooleanConstructor;
597
641
  default: boolean;
598
642
  };
643
+ /**
644
+ * **只读**:值可选中 / 复制,但不能改。
645
+ *
646
+ * 与 `bordered={false}` 组合即**只读值档**(摘要 / 详情里回显业务值,也是 `Form.Item`
647
+ * 的 `readonly` 行推荐的写法):几何与可编辑输入框**完全一致**(同高、同内距、值文字同左缘),
648
+ * 但滑过不亮浅底、聚焦不给边框与主色环 —— 一眼看去是「值」而不是「输入位」。
649
+ * 只读 ≠ 禁用:它照旧可聚焦、可选中复制,也不变灰。
650
+ */
599
651
  readonly: {
600
652
  type: BooleanConstructor;
601
653
  default: boolean;
@@ -615,7 +667,10 @@ export declare const InputCompound: {
615
667
  type: BooleanConstructor;
616
668
  default: boolean;
617
669
  };
618
- /** 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底) */
670
+ /**
671
+ * 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底,靠悬停与聚焦给反馈)。
672
+ * 配 `readonly` 即**只读值档**:连悬停底与聚焦框环都退掉(见 `readonly`)。
673
+ */
619
674
  bordered: {
620
675
  type: BooleanConstructor;
621
676
  default: boolean;
@@ -720,6 +775,14 @@ export declare const InputCompound: {
720
775
  type: BooleanConstructor;
721
776
  default: boolean;
722
777
  };
778
+ /**
779
+ * **只读**:值可选中 / 复制,但不能改。
780
+ *
781
+ * 与 `bordered={false}` 组合即**只读值档**(摘要 / 详情里回显业务值,也是 `Form.Item`
782
+ * 的 `readonly` 行推荐的写法):几何与可编辑输入框**完全一致**(同高、同内距、值文字同左缘),
783
+ * 但滑过不亮浅底、聚焦不给边框与主色环 —— 一眼看去是「值」而不是「输入位」。
784
+ * 只读 ≠ 禁用:它照旧可聚焦、可选中复制,也不变灰。
785
+ */
723
786
  readonly: {
724
787
  type: BooleanConstructor;
725
788
  default: boolean;
@@ -739,7 +802,10 @@ export declare const InputCompound: {
739
802
  type: BooleanConstructor;
740
803
  default: boolean;
741
804
  };
742
- /** 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底) */
805
+ /**
806
+ * 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底,靠悬停与聚焦给反馈)。
807
+ * 配 `readonly` 即**只读值档**:连悬停底与聚焦框环都退掉(见 `readonly`)。
808
+ */
743
809
  bordered: {
744
810
  type: BooleanConstructor;
745
811
  default: boolean;
@@ -847,6 +913,7 @@ export declare const InputCompound: {
847
913
  type: BooleanConstructor;
848
914
  default: boolean;
849
915
  };
916
+ /** 只读:值可选中 / 复制;配 `bordered={false}` 即只读值档(口径见 `Input` 的同名 prop) */
850
917
  readonly: {
851
918
  type: BooleanConstructor;
852
919
  default: boolean;
@@ -910,6 +977,7 @@ export declare const InputCompound: {
910
977
  type: BooleanConstructor;
911
978
  default: boolean;
912
979
  };
980
+ /** 只读:值可选中 / 复制;配 `bordered={false}` 即只读值档(口径见 `Input` 的同名 prop) */
913
981
  readonly: {
914
982
  type: BooleanConstructor;
915
983
  default: boolean;
@@ -32,6 +32,14 @@ const Input = /* @__PURE__ */ defineComponent({
32
32
  type: Boolean,
33
33
  default: false
34
34
  },
35
+ /**
36
+ * **只读**:值可选中 / 复制,但不能改。
37
+ *
38
+ * 与 `bordered={false}` 组合即**只读值档**(摘要 / 详情里回显业务值,也是 `Form.Item`
39
+ * 的 `readonly` 行推荐的写法):几何与可编辑输入框**完全一致**(同高、同内距、值文字同左缘),
40
+ * 但滑过不亮浅底、聚焦不给边框与主色环 —— 一眼看去是「值」而不是「输入位」。
41
+ * 只读 ≠ 禁用:它照旧可聚焦、可选中复制,也不变灰。
42
+ */
35
43
  readonly: {
36
44
  type: Boolean,
37
45
  default: false
@@ -51,7 +59,10 @@ const Input = /* @__PURE__ */ defineComponent({
51
59
  type: Boolean,
52
60
  default: false
53
61
  },
54
- /** 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底) */
62
+ /**
63
+ * 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底,靠悬停与聚焦给反馈)。
64
+ * 配 `readonly` 即**只读值档**:连悬停底与聚焦框环都退掉(见 `readonly`)。
65
+ */
55
66
  bordered: {
56
67
  type: Boolean,
57
68
  default: true
@@ -225,6 +236,7 @@ const Textarea = /* @__PURE__ */ defineComponent({
225
236
  type: Boolean,
226
237
  default: false
227
238
  },
239
+ /** 只读:值可选中 / 复制;配 `bordered={false}` 即只读值档(口径见 `Input` 的同名 prop) */
228
240
  readonly: {
229
241
  type: Boolean,
230
242
  default: false
@@ -84,7 +84,7 @@ export interface ListViewProps {
84
84
  *
85
85
  * 以 `Card interactive` 为壳,卡片外观与悬浮反馈都来自它;本件只管卡片**内部**的排版与几何
86
86
  * (最小高 144、图标 36、标题 16/600、描述两行截断、尾部贴底、胶囊与标签片),
87
- * 几何来源同 `Card`(portal 技能市场卡片实测)。
87
+ * 几何来源同 `Card`(技能市场卡片实测)。
88
88
  *
89
89
  * 三处「有意为之」:
90
90
  * 1. **操作位默认隐藏**(`opacity: 0`),卡片悬浮或卡内获得焦点时显形;触屏(无 hover 能力)恒显形 ——
@@ -34,7 +34,7 @@ export declare const Pagination: import("vue").DefineComponent<import("vue").Ext
34
34
  type: NumberConstructor;
35
35
  default: number;
36
36
  };
37
- /** 当前页(**1 起**) */
37
+ /** 当前页(**1 起**;接口用 0 基的话在调用方 ±1 换算,别把 0 基值直接接进来) */
38
38
  modelValue: {
39
39
  type: NumberConstructor;
40
40
  default: number;
@@ -96,7 +96,7 @@ export declare const Pagination: import("vue").DefineComponent<import("vue").Ext
96
96
  type: NumberConstructor;
97
97
  default: number;
98
98
  };
99
- /** 当前页(**1 起**) */
99
+ /** 当前页(**1 起**;接口用 0 基的话在调用方 ±1 换算,别把 0 基值直接接进来) */
100
100
  modelValue: {
101
101
  type: NumberConstructor;
102
102
  default: number;
@@ -9,7 +9,7 @@ const Pagination = /* @__PURE__ */ defineComponent({
9
9
  type: Number,
10
10
  default: 0
11
11
  },
12
- /** 当前页(**1 起**) */
12
+ /** 当前页(**1 起**;接口用 0 基的话在调用方 ±1 换算,别把 0 基值直接接进来) */
13
13
  modelValue: {
14
14
  type: Number,
15
15
  default: 1
@@ -45,13 +45,29 @@ export interface QueryStateProps {
45
45
  *
46
46
  * 文案 props 可覆盖(i18n 由消费方传 t() 结果;本包不内置文案)。
47
47
  *
48
- * @example 空态带 CTA、错误态带重试
48
+ * ## 两个容易拿不准的点
49
+ *
50
+ * - `empty` 是**触发位**,不是「文案 prop」:`true` 触发并用默认文案;给字符串则**既触发、
51
+ * 又当空态标题**(副文案仍走 `emptyDescription`);
52
+ * - **本件不自带数据源**:`loading` / `error` / `empty` 都由调用方按自己的请求结果给。
53
+ * `Table` 自带加载与空两态却**没有错误态** —— 要么只把 error 交给本件,要么整块三态由本件在外层表达:
54
+ *
55
+ * @example 列表页:三态在外层,表格只管数据(经典写法)
56
+ * <QueryState
57
+ * loading={isLoading}
58
+ * error={loadError}
59
+ * :empty="rows.length === 0"
60
+ * :error-action-text="t('common.retry')"
61
+ * onErrorAction={() => refetch()}>
62
+ * <Table :columns="columns" :data="rows" row-key="id" />
63
+ * </QueryState>
64
+ *
65
+ * @example 三态齐备(空态带 CTA、错误态带重试)
49
66
  * <QueryState
50
67
  * loading={isLoading}
51
68
  * error={loadError.value}
52
69
  * errorActionText={t('common.retry')}
53
70
  * onErrorAction={() => reload()}
54
- * empty={topics.length === 0}
55
71
  * empty={t('topic.empty')}
56
72
  * emptyDescription={t('topic.emptyDescription')}
57
73
  * emptyActionText={t('topic.create')}
@@ -1,33 +1,29 @@
1
- import { type PropType, type SlotsType, type VNodeChild } from 'vue';
2
- import type { ControlSize } from '../control-size';
3
- import { type InputStatus } from '../input';
4
- export interface SelectOption {
5
- label: string;
6
- value: string | number;
7
- disabled?: boolean;
8
- /** 分组标题用的标记(内部判别用,消费方不必写) */
9
- options?: never;
10
- }
11
- /** 分组选项(`{ label: '进行中', options: [...] }`,组内选项与平铺选项同构) */
12
- export interface SelectOptionGroup {
13
- label: string;
14
- options: SelectOption[];
15
- }
16
- export type SelectItem = SelectOption | SelectOptionGroup;
17
- export type SelectValue = string | number | Array<string | number>;
18
1
  /**
19
- * 选择器(**自绘**,不封装 farris)。
2
+ * Select —— 选择器(**自绘**,不封装 farris);同目录另出 `SelectTree`(树选择)。
3
+ *
4
+ * 本文件是选择器族的**组装层与出口**:`Select` 与 `SelectTree` 都只是把下面这几个内件拼起来,
5
+ * 内件一律不对外导出(`src/index.ts` 只出 `Select` 与 `SelectTree`,成员不平级、也不挂点号)。
20
6
  *
21
- * ## 触发器复用的是输入框的「触发器形态」(不自绘框)
7
+ * ```
8
+ * components/select/
9
+ * index.tsx 组装层与出口(Select / SelectTree)
10
+ * select-shared.ts 叶子模块:类型、面板 id、popover 开合、触发器键盘契约
11
+ * select-trigger.tsx 触发器内件(输入框的「触发器形态」)
12
+ * select-panel.tsx 面板壳内件(原生 popover + 搜索行 + 内容槽)
13
+ * select-list.tsx 选项列表体内件(平铺 / 分组 / 创建行 / 空态与加载行)
14
+ * select-tree.tsx 树体内件(SelectTree 的实现)
15
+ * select-tree-filter.ts 纯函数:树过滤与命中切分
16
+ * ```
22
17
  *
23
- * 触发器由 `<Input as="button">` 渲染 —— 框 / 内距 / 边 / 圆角 / 字号 / hover / 聚焦 / 禁用 /
24
- * 校验色(红·黄)/ **内建清除位** / **右侧图标位**全部来自输入面与它的表壳,本件只提供值 / 标签的排版。
25
- * 好处是「像输入框」这件事只剩一份规格(改输入框即自动跟随),且清除与右侧图标不必再自绘一套。
26
- * 三条落点各有其处:`class` → 表壳(显式锚 + 本件对壳的微调)、`controlClass` → 壳内控件(本件排版类)、
27
- * `controlAttrs` → 控件上的原生语义与键盘契约(`popovertarget` / `aria-*` / `keydown`)。
28
- * 触发器仍是原生 `<button>`(`popovertarget` 只认 button),故零 JS 开合与隐式锚路径不变;
29
- * 面板改挂 `mh-popup--anchored`:锚显式钉在**表壳**上 —— 否则隐式锚会缩成壳内控件,
30
- * 面板不再与整件同宽(`anchor-size(width)` 取的是锚的宽)。
18
+ * 拆件的收益是**规格只剩一份**:触发器(像输入框 / 清除位 / 箭头 / 键盘开面板)与面板壳
19
+ * (皮肤 / 定位档 / 显式锚 / 搜索行)在「列表形态」与「树形态」之间是同一份代码,改一处两处都跟着走。
20
+ * 面上仍是**一个** `styles/components/select.css`(一件的多种形态共用一份面与一个目录)。
21
+ *
22
+ * ## 触发器的框不在本件
23
+ *
24
+ * 触发器 = 输入框的「触发器形态」(`<Input as="button">`,见 `select-trigger.tsx`):框 / 内距 /
25
+ * 边 / 圆角 / 底 / 字 / hover / 聚焦 / 禁用 / 校验色与**清除位**、**右侧图标位**全部归输入面与它的表壳。
26
+ * 本件只给选择器自己的排版(值 / 标签 / 面板内容)。
31
27
  *
32
28
  * ## 浮层边界(与「popup 只给零 JS 面」的取舍一致)
33
29
  *
@@ -39,7 +35,7 @@ export type SelectValue = string | number | Array<string | number>;
39
35
  * ## 状态三源等价
40
36
  *
41
37
  * 选中行 = `[data-state='selected']`(组件下发)/ 原生 `input:checked`(静态页零 JS 路径)/
42
- * `.is-selected`(规格冻结档),样式层只写一份声明。
38
+ * `.is-selected`(规格冻结档),样式层只写一份声明(见 `select-list.tsx`)。
43
39
  *
44
40
  * @example 单选
45
41
  * <Select options={opts} modelValue={v} placeholder="请选择" onChange={setV} />
@@ -50,6 +46,12 @@ export type SelectValue = string | number | Array<string | number>;
50
46
  * @example 分组 + 搜索 + 可创建
51
47
  * <Select searchable allowCreate multiple options={grouped} modelValue={list} onChange={setList} />
52
48
  */
49
+ import { type PropType, type SlotsType, type VNodeChild } from 'vue';
50
+ import type { ControlSize } from '../control-size';
51
+ import type { InputStatus } from '../input';
52
+ import { type SelectItem, type SelectOption, type SelectValue } from './select-shared';
53
+ export type { SelectItem, SelectOption, SelectOptionGroup, SelectValue } from './select-shared';
54
+ export { SelectTree } from './select-tree';
53
55
  export declare const Select: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
54
56
  /** 选项表(平铺项与分组项可混用) */
55
57
  options: {
@@ -241,12 +243,12 @@ export declare const Select: import("vue").DefineComponent<import("vue").Extract
241
243
  status: InputStatus;
242
244
  clearable: boolean;
243
245
  onSearch: (keyword: string) => void;
246
+ emptyText: string;
247
+ searchable: boolean;
244
248
  maxTagCount: number;
245
249
  maxTagPlaceholder: string;
246
- searchable: boolean;
247
250
  filterOption: (option: SelectOption, keyword: string) => boolean;
248
251
  allowCreate: boolean;
249
- emptyText: string;
250
252
  }, SlotsType<{
251
253
  empty?: () => VNodeChild;
252
254
  }>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;