@manohub/ui 0.10.5 → 1.0.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.
Files changed (86) hide show
  1. package/README.md +155 -155
  2. package/dist/components/form/index.js +8 -1
  3. package/dist/components/input/bordered.d.ts +15 -0
  4. package/dist/components/input/bordered.js +8 -0
  5. package/dist/components/input/index.d.ts +82 -31
  6. package/dist/components/input/index.js +16 -6
  7. package/dist/components/input/number-input.d.ts +239 -0
  8. package/dist/components/input/number-input.js +240 -0
  9. package/dist/components/select/index.d.ts +19 -0
  10. package/dist/components/select/index.js +10 -0
  11. package/dist/components/select/select-tree.d.ts +11 -0
  12. package/dist/components/select/select-tree.js +6 -0
  13. package/dist/components/select/select-trigger.d.ts +17 -0
  14. package/dist/components/select/select-trigger.js +9 -0
  15. package/dist/components/table/index.d.ts +85 -10
  16. package/dist/components/table/index.js +38 -9
  17. package/dist/index.d.ts +2 -2
  18. package/dist/index.js +2 -0
  19. package/dist/styles/components/avatar.css +131 -131
  20. package/dist/styles/components/avatar.tokens.css +24 -24
  21. package/dist/styles/components/badge.css +266 -266
  22. package/dist/styles/components/badge.tokens.css +24 -24
  23. package/dist/styles/components/button.css +460 -460
  24. package/dist/styles/components/capsule.css +128 -128
  25. package/dist/styles/components/capsule.tokens.css +27 -27
  26. package/dist/styles/components/card.css +198 -198
  27. package/dist/styles/components/checkbox.css +145 -145
  28. package/dist/styles/components/checkbox.tokens.css +26 -26
  29. package/dist/styles/components/code-block.css +216 -216
  30. package/dist/styles/components/code-block.tokens.css +40 -40
  31. package/dist/styles/components/collapse.css +62 -62
  32. package/dist/styles/components/collapse.tokens.css +20 -20
  33. package/dist/styles/components/dialog.css +158 -158
  34. package/dist/styles/components/dialog.tokens.css +24 -24
  35. package/dist/styles/components/divider.css +75 -75
  36. package/dist/styles/components/divider.tokens.css +22 -22
  37. package/dist/styles/components/drawer.css +143 -143
  38. package/dist/styles/components/drawer.tokens.css +22 -22
  39. package/dist/styles/components/filter.css +29 -29
  40. package/dist/styles/components/form.css +456 -425
  41. package/dist/styles/components/form.tokens.css +19 -15
  42. package/dist/styles/components/input.css +659 -538
  43. package/dist/styles/components/input.tokens.css +43 -43
  44. package/dist/styles/components/layout.css +200 -200
  45. package/dist/styles/components/list-view.css +244 -244
  46. package/dist/styles/components/loading.css +76 -76
  47. package/dist/styles/components/loading.tokens.css +23 -23
  48. package/dist/styles/components/nav.css +48 -48
  49. package/dist/styles/components/notice.css +153 -153
  50. package/dist/styles/components/notice.tokens.css +17 -17
  51. package/dist/styles/components/notification.css +159 -159
  52. package/dist/styles/components/notification.tokens.css +37 -37
  53. package/dist/styles/components/page.css +473 -473
  54. package/dist/styles/components/pagination.css +303 -303
  55. package/dist/styles/components/pagination.tokens.css +32 -32
  56. package/dist/styles/components/panel.css +77 -77
  57. package/dist/styles/components/query-state.css +88 -88
  58. package/dist/styles/components/radio.css +271 -271
  59. package/dist/styles/components/radio.tokens.css +26 -26
  60. package/dist/styles/components/search.css +34 -34
  61. package/dist/styles/components/select.css +326 -326
  62. package/dist/styles/components/select.tokens.css +44 -44
  63. package/dist/styles/components/skeleton.css +60 -60
  64. package/dist/styles/components/skeleton.tokens.css +27 -27
  65. package/dist/styles/components/steps.css +205 -205
  66. package/dist/styles/components/steps.tokens.css +44 -44
  67. package/dist/styles/components/switch.css +165 -165
  68. package/dist/styles/components/switch.tokens.css +26 -26
  69. package/dist/styles/components/tabbar.css +235 -235
  70. package/dist/styles/components/tabbar.tokens.css +30 -30
  71. package/dist/styles/components/table.css +510 -504
  72. package/dist/styles/components/table.tokens.css +44 -44
  73. package/dist/styles/components/tag.css +127 -127
  74. package/dist/styles/components/tag.tokens.css +24 -24
  75. package/dist/styles/components/toast.css +185 -185
  76. package/dist/styles/components/toast.tokens.css +26 -26
  77. package/dist/styles/components/tooltip.css +89 -89
  78. package/dist/styles/components/tooltip.tokens.css +25 -25
  79. package/dist/styles/components/tree.css +173 -173
  80. package/dist/styles/components/upload.css +151 -151
  81. package/dist/styles/components/upload.tokens.css +27 -27
  82. package/dist/styles/index.css +153 -153
  83. package/dist/styles/operations/collapse.css +84 -84
  84. package/dist/styles/operations/popup.css +193 -193
  85. package/dist/styles/operations/tabs.css +82 -82
  86. package/package.json +62 -62
package/README.md CHANGED
@@ -1,155 +1,155 @@
1
- # @manohub/ui
2
-
3
- AIHub **自绘组件层**:不依赖 farris 的手写 Vue TSX 组件 + 自带令牌层。
4
-
5
- 本包分三层,**可分别引用**:
6
-
7
- | 层 | 入口 | 内容 |
8
- |---|---|---|
9
- | ① 主题 | `@manohub/theme/default.css`(兜底)/ `@manohub/theme/farris.css`(Farris 规范) | **全局**令牌(值)—— 随主题包独立发布,换主题只换这一行 |
10
- | ② 组件面 | `@manohub/ui/styles.css` | **元素级 reset**(只有盒模型 + 容器基座两面,见下)+ 类与状态协议 + 组件令牌基础值,**无需 JS** 即可使用 |
11
- | ③ Vue 层 | `@manohub/ui` | Vue 组件:渲染 ② 的类 + 行为增强 |
12
- | ④ 服务层 | `@manohub/ui` | 命令式反馈:`toast()` / `confirm()` / `alert()` / `showLoading()`(宿主 + 队列 + 生命周期,共用 ③ 的组件) |
13
-
14
- - **视觉与几何**:《Farris Web 基础视觉规范》(`@aihub/theme` 的令牌与组件实测值)
15
- - **令牌命名**:daisyUI 式语义槽位(`--ui-primary` / `--ui-base-content` / `--ui-info*` / `--ui-warning*` …,`default ≠ primary`)
16
- - **组件 API**:shadcn 通行词汇(变体维度 `tone` × `shape` × `size`、compound 点号子件、受控 prop + 语义事件)
17
- - **实现**:零依赖手写 CSS,类名 `.mh-*`(bem-lite)
18
-
19
- > **0.5.0(破坏性变更)**:`Section` 组件已退役 —— 它的「标题行 + 右端操作位(`extra`)」能力并入
20
- > **`Form.Header`**(支持 `title` / `description` / `extra`,且成员归位到表单最前)。
21
- > 旧写法 `Section` / `Form.Section` 统一改为 `Form.Header`;需要「区块分组」时用 `Panel` 或嵌套容器。
22
-
23
- ## 安装
24
-
25
- ```bash
26
- pnpm add @manohub/ui @manohub/theme
27
- ```
28
-
29
- ## 接入
30
-
31
- 样式两行,顺序固定(先主题后组件面;若应用的样式链已级联本包,则不必单独引):
32
-
33
- ```css
34
- @import "@manohub/theme/default.css"; /* ① 全局令牌(值) */
35
- @import "@manohub/ui/styles.css"; /* ② 组件面(类 + 组件令牌值) */
36
- ```
37
-
38
- 只写静态页(不装 Vue)时引这两行即可 —— 类名与 `data-*` 状态协议直接可用。
39
-
40
- **② 的第一条 import 就是元素级 reset**(随包内置,不需要第三行),它只有两面:
41
-
42
- 1. **盒模型**:`box-sizing: border-box` 覆盖容器、全部后代与伪元素(本包所有面的几何前提);
43
- 2. **容器基座**:`[data-manohub-ui]` 上的字族 / 字号 / 行高 / 字色 / 底色 / 整链高度(令牌的落点)。
44
-
45
- 作用域锚 `[data-manohub-ui]` 且特异性为 0 —— 应用侧写任何元素选择器都能盖过它。
46
- **它有意不再归零标题字阶、列表序号、链接下划线、控件外观**:那些规则在 Tailwind 的层外
47
- (未分层 > 分层),会把消费方的工具类整片压住(`border*`、`img` 上的 `h-*`、`hidden` …);
48
- 需要归零的元素各自归到渲染它的组件面上(`img` 在 Avatar / Upload、`a` 在 Button、
49
- `textarea` 的 `resize` 在 `.mh-input--textarea`、`h1`-`h6` 在 `.mh-text`)。
50
- Tailwind 消费方另有 preflight 兜底,两者不冲突。
51
-
52
- 所以**不必再自己写一份全局 reset**;也**不要**用元素级选择器去补(那正是被压住的成因)。
53
-
54
- 本包**可独立发布**:组件与类型一律从 `@manohub/ui` 直接引;下游(如 `@manohub/kit`)若转出口,是下游自己的选择。
55
-
56
- ```tsx
57
- import { Card, Form, Tree, Checkbox } from '@manohub/ui'
58
- ```
59
-
60
- ## 组件清单
61
-
62
- | 分组 | 组件 |
63
- |---|---|
64
- | 容器与分区 | `Page`(页面级骨架:`.Header` / `.Body` / `.Footer` / `.Filter` / `.Toolbar` / `.Split`)、`Panel`(+ `.Header` / `.Body` / `.Footer`)、`Card`(+ 三件)、`Nav`、`Layout`(`Layout.Row` 排「行」/ `Layout.Column` 排「列」:轴向 × 间距档 × 对齐) |
65
- | 数据展示 | `Tree`、`ListView`(+ `.CardItem`)、`Badge`、`Tag`(`Badge` 的可关闭 / 可新增封装) |
66
- | 展示 | `Avatar`(+ `.Group`)、`Skeleton`(行 / 列表 / 块状) |
67
- | 表单基础(**自绘**) | `Button`(+ `.Group` / `.Divider`;五型 / 小号 / 加载 / 菜单按钮 / 连体切换)、`Input`(+ `.Group` / `.Chip` / `.Display` / `.Textarea`;清除 / 可见性切换 / 校验)、`Search`(搜索框:值 / 搜索 / 清空三条回调)、`Select`(单选 / 多选 / 搜索 / 加载;面板用原生 popover)、`SelectTree`(树形态:仅叶子可选 + 本地过滤 + 命中高亮;与 `Select` 平级导出、共用触发器与面板)、`Switch`(表格档 / 表单档) |
68
- | 表单与选择 | `Form`(+ `.Header` / `.Item`;标签体系、成员归位、`v-model` 值收集与命令式 `clear()`)、`Filter`(+ `.Item`;`Form` 的壳:条件字段写成子件、控件自放、查询/重置与快照归一)、`Checkbox`、`Radio`(+ `.Group` / `.Card`;卡片式单选)、`Capsule`(胶囊选择器;胶囊 / 方形两档外形,与 `Tabs` 组合即页签) |
69
- | 反馈与三态 | `Notice`(条状提示)、`Notification`(通知卡:标题 + 头像 / 标签 / 操作 / 关闭位)、`QueryState`(三态容器;内建空 / 错误插画取自设计交付的四张 SVG,原样内联在 `src/utils/state-illustration.ts`)、`Loading`(转圈 + 遮罩:行内 / 区域 / 全屏三形态) |
70
- | 反馈与引导(**自绘**) | `Toast`(消息提示:语义四档 / loading + 可选动作 + 自动消失计时 + `.Stack` 堆叠)、`Steps`(步骤条:节点式 / 燕尾段式 / `.Progress` 进度条) |
71
- | 数据展示(**自绘**) | `Table`(列 × 数据 + 选择列 + 排序事件 + 固定表头 / 固定列 + 批量栏 / 合计行;排序与列宽拖拽算法归调用方)、`Pagination`(总数 / 每页条数 / 页码窗口 / 简单档 / 外框档)、`TabBar`(下划线页签 + 「更多」收纳) |
72
- | 内容与文件 | `CodeBlock`(**一族三件**:组合件 `CodeBlock` 按 `editable` 切只读 / 编辑两形态;`.View` 只读展示 —— 行内 chip / 代码块 + 语言角标 + 图标复制位;`.Editor` 可编辑 —— 等宽多行 + 按行自适应 / 限高内滚 / 只读、禁用)、`Upload`(+ `.Thumbs`;拖拽 / 点击上传区 + 已传文件列表) |
73
- | 浮层(**自绘**) | `Dialog`(原生 `<dialog>` + 模态皮肤;关闭受控)、`Tooltip`(**零 JS**:显隐与定位全走操作层 hover 档) |
74
- | 操作层(跨件原语) | `Collapse`(+ `.Stack`)、`Tabs`(+ `TabPanel`,面板切换骨架 —— 显隐归组件层,非零 JS 原语);浮层只提供样式层(`styles/operations/popup.css`,零 JS 可直接用),Vue 浮层仍用 farris 适配件 |
75
-
76
- ## 三态的操作按钮
77
-
78
- `QueryState` / `Tree` / `ListView` 的空态·错误态操作按钮就是**本包的 `Button`**(`variant="primary"`),
79
- 与全站按钮同族 —— 应用**不需要**注入任何东西,只要把文案与回调给上:
80
-
81
- ```vue
82
- <QueryState
83
- :empty="list.length === 0"
84
- empty-action-text="新建"
85
- :on-empty-action="openCreate"
86
- >
87
- <MyList />
88
- </QueryState>
89
- ```
90
-
91
- - 要换外观:改主题令牌(`Button` 的外观档位是 `variant`);
92
- - 要换结构(多个按钮 / 自定义动作区):用 `actions` 插槽自绘。
93
-
94
- ## 服务层(组件树之外调反馈)
95
-
96
- 组件是**受控**的(可见性归调用方);组件树之外(请求回调、路由守卫、工具函数里)要弹提示时用服务层:
97
-
98
- ```ts
99
- import { toast, confirm, alert, showLoading, configureHost } from '@manohub/ui'
100
-
101
- toast('success', '保存成功') // 3 秒自动消失
102
- toast('error', '导出失败', { action: { text: '重试', onClick: retry } })
103
- const pending = toast('loading', '正在导入…', { duration: 0 }) // 常驻,完成后自己收
104
- pending.close()
105
-
106
- if (await confirm({ title: '删除确认', detail: '删除后不可恢复', okDanger: true })) await remove()
107
- await alert({ title: '导入完成', detail: '共 128 条' })
108
-
109
- const done = showLoading('保存中…') // 引用计数:并发调用要各关各的
110
- try { await save() } finally { done() }
111
- ```
112
-
113
- **四件必须知道的事**:
114
-
115
- 1. **服务层不新造浮层**:`toast()` 渲染的是本包的 `Toast`、`confirm()` 渲染的是本包的 `Dialog`、
116
- `showLoading()` 渲染的是本包的 `Loading` —— 外观改一处两处一起变。
117
- 2. **宿主即渲染位置**:解析顺序是「**显式 `configureHost(el)`** → 带 `[data-manohub-ui]` 作用域锚的容器
118
- → `document.body`」。微前端下**必须落回本应用自己的容器**:本包全部令牌(全局值 + 组件值)都锚在
119
- `[data-manohub-ui]` 上,落在容器外的浮层会「样式全丢」(变量解析为空 +
120
- 应用侧样式被 `scopecss` 前缀化后匹配不到)。骨架层只需在包裹节点上补一个
121
- `data-manohub-ui` 属性,服务层不必反向依赖它。
122
- 注:`.app-container` 是 `@manohub/kit` 的输出类名(内部命名),**不是**跨包契约,服务层不认它。
123
- 3. **按需挂载、自动收尾**:首次调用才建宿主与 Vue 实例(模块顶层零 DOM 操作);
124
- `toast` 队列空了、`loading` 计数归零即收掉宿主 —— 反复重挂不留悬挂实例。
125
- 4. **`loading` 是引用计数语义**:并发的两次「显示」只关一次时遮罩仍在(先结束的那个不会把
126
- 别人的遮罩一起收掉)。推荐用返回的句柄在 `finally` 里关掉自己那一次。
127
-
128
- ## 主题层
129
-
130
- 全局令牌(`--ui-primary` / `--ui-space-*` / `--ui-font-*` 这类跨件共用的值)**不在本包**,已拆成独立包
131
- **`@manohub/theme`**(值层与面层分开:值随主题变、面不随主题变)。每个主题分五片:
132
- `colors` / `typography` / `spacing` / `radius` / `shadows`,一律锚定 **`[data-manohub-ui]`**
133
- (库品牌作用域锚;micro-app 的 scopecss 不作用域化 `:root`,定义在 `:root` 会泄漏到宿主)。
134
-
135
- 只服务单一组件的令牌分两半落在本包:
136
-
137
- | 位置 | 内容 | 锚 |
138
- |---|---|---|
139
- | `styles/components/<件>.tokens.css` | **基础值**(唯一的、两套主题同值的那一条) | `[data-manohub-ui]` |
140
- | `styles/components/<件>.css` | **档位改写**(`.mh-<件>--<档>` 覆盖同一令牌)+ `--mh-*` 内部状态变量 | 元件根类 / 档位类 |
141
-
142
- 后一半必须在面里:它们靠「同一元素上两个选择器改写同一个令牌」生效,搬走就断链。
143
-
144
- 随包两套:
145
-
146
- | 主题 | 入口 | 锚点 | 取值来源 |
147
- |---|---|---|---|
148
- | `default` | `@manohub/theme/default.css` | `[data-manohub-ui]`(兜底套) | AIHub 品牌蓝(primary `#2660FF`)|
149
- | `farris` | `@manohub/theme/farris.css` | `[data-manohub-ui][data-theme='farris']` | 《Farris Web 基础视觉规范》(primary `#2a87ff`,色板编号 Th/Au/Ne/Te 逐条标注)|
150
-
151
- 换皮只需给容器加属性(`createSubApp({ theme: 'farris' })` 会写 `data-theme`),不动类名、不动组件代码、不重打包。
152
- 新增主题 = `packages/theme/src/<name>/` 新目录,令牌锚 `[data-manohub-ui][data-theme='<name>']`,
153
- 且必须**全量覆盖** default 的令牌。
154
-
155
- 局部换主题:在更深的容器上重设变量即可,例如 `.role-tree-pane { --ui-primary: #7C3AED; }`。
1
+ # @manohub/ui
2
+
3
+ AIHub **自绘组件层**:不依赖 farris 的手写 Vue TSX 组件 + 自带令牌层。
4
+
5
+ 本包分三层,**可分别引用**:
6
+
7
+ | 层 | 入口 | 内容 |
8
+ |---|---|---|
9
+ | ① 主题 | `@manohub/theme/default.css`(兜底)/ `@manohub/theme/farris.css`(Farris 规范) | **全局**令牌(值)—— 随主题包独立发布,换主题只换这一行 |
10
+ | ② 组件面 | `@manohub/ui/styles.css` | **元素级 reset**(只有盒模型 + 容器基座两面,见下)+ 类与状态协议 + 组件令牌基础值,**无需 JS** 即可使用 |
11
+ | ③ Vue 层 | `@manohub/ui` | Vue 组件:渲染 ② 的类 + 行为增强 |
12
+ | ④ 服务层 | `@manohub/ui` | 命令式反馈:`toast()` / `confirm()` / `alert()` / `showLoading()`(宿主 + 队列 + 生命周期,共用 ③ 的组件) |
13
+
14
+ - **视觉与几何**:《Farris Web 基础视觉规范》(`@aihub/theme` 的令牌与组件实测值)
15
+ - **令牌命名**:daisyUI 式语义槽位(`--ui-primary` / `--ui-base-content` / `--ui-info*` / `--ui-warning*` …,`default ≠ primary`)
16
+ - **组件 API**:shadcn 通行词汇(变体维度 `tone` × `shape` × `size`、compound 点号子件、受控 prop + 语义事件)
17
+ - **实现**:零依赖手写 CSS,类名 `.mh-*`(bem-lite)
18
+
19
+ > **0.5.0(破坏性变更)**:`Section` 组件已退役 —— 它的「标题行 + 右端操作位(`extra`)」能力并入
20
+ > **`Form.Header`**(支持 `title` / `description` / `extra`,且成员归位到表单最前)。
21
+ > 旧写法 `Section` / `Form.Section` 统一改为 `Form.Header`;需要「区块分组」时用 `Panel` 或嵌套容器。
22
+
23
+ ## 安装
24
+
25
+ ```bash
26
+ pnpm add @manohub/ui @manohub/theme
27
+ ```
28
+
29
+ ## 接入
30
+
31
+ 样式两行,顺序固定(先主题后组件面;若应用的样式链已级联本包,则不必单独引):
32
+
33
+ ```css
34
+ @import "@manohub/theme/default.css"; /* ① 全局令牌(值) */
35
+ @import "@manohub/ui/styles.css"; /* ② 组件面(类 + 组件令牌值) */
36
+ ```
37
+
38
+ 只写静态页(不装 Vue)时引这两行即可 —— 类名与 `data-*` 状态协议直接可用。
39
+
40
+ **② 的第一条 import 就是元素级 reset**(随包内置,不需要第三行),它只有两面:
41
+
42
+ 1. **盒模型**:`box-sizing: border-box` 覆盖容器、全部后代与伪元素(本包所有面的几何前提);
43
+ 2. **容器基座**:`[data-manohub-ui]` 上的字族 / 字号 / 行高 / 字色 / 底色 / 整链高度(令牌的落点)。
44
+
45
+ 作用域锚 `[data-manohub-ui]` 且特异性为 0 —— 应用侧写任何元素选择器都能盖过它。
46
+ **它有意不再归零标题字阶、列表序号、链接下划线、控件外观**:那些规则在 Tailwind 的层外
47
+ (未分层 > 分层),会把消费方的工具类整片压住(`border*`、`img` 上的 `h-*`、`hidden` …);
48
+ 需要归零的元素各自归到渲染它的组件面上(`img` 在 Avatar / Upload、`a` 在 Button、
49
+ `textarea` 的 `resize` 在 `.mh-input--textarea`、`h1`-`h6` 在 `.mh-text`)。
50
+ Tailwind 消费方另有 preflight 兜底,两者不冲突。
51
+
52
+ 所以**不必再自己写一份全局 reset**;也**不要**用元素级选择器去补(那正是被压住的成因)。
53
+
54
+ 本包**可独立发布**:组件与类型一律从 `@manohub/ui` 直接引;下游(如 `@manohub/kit`)若转出口,是下游自己的选择。
55
+
56
+ ```tsx
57
+ import { Card, Form, Tree, Checkbox } from '@manohub/ui'
58
+ ```
59
+
60
+ ## 组件清单
61
+
62
+ | 分组 | 组件 |
63
+ |---|---|
64
+ | 容器与分区 | `Page`(页面级骨架:`.Header` / `.Body` / `.Footer` / `.Filter` / `.Toolbar` / `.Split`)、`Panel`(+ `.Header` / `.Body` / `.Footer`)、`Card`(+ 三件)、`Nav`、`Layout`(`Layout.Row` 排「行」/ `Layout.Column` 排「列」:轴向 × 间距档 × 对齐) |
65
+ | 数据展示 | `Tree`、`ListView`(+ `.CardItem`)、`Badge`、`Tag`(`Badge` 的可关闭 / 可新增封装) |
66
+ | 展示 | `Avatar`(+ `.Group`)、`Skeleton`(行 / 列表 / 块状) |
67
+ | 表单基础(**自绘**) | `Button`(+ `.Group` / `.Divider`;五型 / 小号 / 加载 / 菜单按钮 / 连体切换)、`Input`(+ `.Group` / `.Chip` / `.Display` / `.Textarea`;清除 / 可见性切换 / 校验)、`Search`(搜索框:值 / 搜索 / 清空三条回调)、`Select`(单选 / 多选 / 搜索 / 加载;面板用原生 popover)、`SelectTree`(树形态:仅叶子可选 + 本地过滤 + 命中高亮;与 `Select` 平级导出、共用触发器与面板)、`Switch`(表格档 / 表单档) |
68
+ | 表单与选择 | `Form`(+ `.Header` / `.Item`;标签体系、成员归位、`v-model` 值收集与命令式 `clear()`)、`Filter`(+ `.Item`;`Form` 的壳:条件字段写成子件、控件自放、查询/重置与快照归一)、`Checkbox`、`Radio`(+ `.Group` / `.Card`;卡片式单选)、`Capsule`(胶囊选择器;胶囊 / 方形两档外形,与 `Tabs` 组合即页签) |
69
+ | 反馈与三态 | `Notice`(条状提示)、`Notification`(通知卡:标题 + 头像 / 标签 / 操作 / 关闭位)、`QueryState`(三态容器;内建空 / 错误插画取自设计交付的四张 SVG,原样内联在 `src/utils/state-illustration.ts`)、`Loading`(转圈 + 遮罩:行内 / 区域 / 全屏三形态) |
70
+ | 反馈与引导(**自绘**) | `Toast`(消息提示:语义四档 / loading + 可选动作 + 自动消失计时 + `.Stack` 堆叠)、`Steps`(步骤条:节点式 / 燕尾段式 / `.Progress` 进度条) |
71
+ | 数据展示(**自绘**) | `Table`(列 × 数据 + 选择列 + 排序事件 + 固定表头 / 固定列 + 批量栏 / 合计行;排序与列宽拖拽算法归调用方)、`Pagination`(总数 / 每页条数 / 页码窗口 / 简单档 / 外框档)、`TabBar`(下划线页签 + 「更多」收纳) |
72
+ | 内容与文件 | `CodeBlock`(**一族三件**:组合件 `CodeBlock` 按 `editable` 切只读 / 编辑两形态;`.View` 只读展示 —— 行内 chip / 代码块 + 语言角标 + 图标复制位;`.Editor` 可编辑 —— 等宽多行 + 按行自适应 / 限高内滚 / 只读、禁用)、`Upload`(+ `.Thumbs`;拖拽 / 点击上传区 + 已传文件列表) |
73
+ | 浮层(**自绘**) | `Dialog`(原生 `<dialog>` + 模态皮肤;关闭受控)、`Tooltip`(**零 JS**:显隐与定位全走操作层 hover 档) |
74
+ | 操作层(跨件原语) | `Collapse`(+ `.Stack`)、`Tabs`(+ `TabPanel`,面板切换骨架 —— 显隐归组件层,非零 JS 原语);浮层只提供样式层(`styles/operations/popup.css`,零 JS 可直接用),Vue 浮层仍用 farris 适配件 |
75
+
76
+ ## 三态的操作按钮
77
+
78
+ `QueryState` / `Tree` / `ListView` 的空态·错误态操作按钮就是**本包的 `Button`**(`variant="primary"`),
79
+ 与全站按钮同族 —— 应用**不需要**注入任何东西,只要把文案与回调给上:
80
+
81
+ ```vue
82
+ <QueryState
83
+ :empty="list.length === 0"
84
+ empty-action-text="新建"
85
+ :on-empty-action="openCreate"
86
+ >
87
+ <MyList />
88
+ </QueryState>
89
+ ```
90
+
91
+ - 要换外观:改主题令牌(`Button` 的外观档位是 `variant`);
92
+ - 要换结构(多个按钮 / 自定义动作区):用 `actions` 插槽自绘。
93
+
94
+ ## 服务层(组件树之外调反馈)
95
+
96
+ 组件是**受控**的(可见性归调用方);组件树之外(请求回调、路由守卫、工具函数里)要弹提示时用服务层:
97
+
98
+ ```ts
99
+ import { toast, confirm, alert, showLoading, configureHost } from '@manohub/ui'
100
+
101
+ toast('success', '保存成功') // 3 秒自动消失
102
+ toast('error', '导出失败', { action: { text: '重试', onClick: retry } })
103
+ const pending = toast('loading', '正在导入…', { duration: 0 }) // 常驻,完成后自己收
104
+ pending.close()
105
+
106
+ if (await confirm({ title: '删除确认', detail: '删除后不可恢复', okDanger: true })) await remove()
107
+ await alert({ title: '导入完成', detail: '共 128 条' })
108
+
109
+ const done = showLoading('保存中…') // 引用计数:并发调用要各关各的
110
+ try { await save() } finally { done() }
111
+ ```
112
+
113
+ **四件必须知道的事**:
114
+
115
+ 1. **服务层不新造浮层**:`toast()` 渲染的是本包的 `Toast`、`confirm()` 渲染的是本包的 `Dialog`、
116
+ `showLoading()` 渲染的是本包的 `Loading` —— 外观改一处两处一起变。
117
+ 2. **宿主即渲染位置**:解析顺序是「**显式 `configureHost(el)`** → 带 `[data-manohub-ui]` 作用域锚的容器
118
+ → `document.body`」。微前端下**必须落回本应用自己的容器**:本包全部令牌(全局值 + 组件值)都锚在
119
+ `[data-manohub-ui]` 上,落在容器外的浮层会「样式全丢」(变量解析为空 +
120
+ 应用侧样式被 `scopecss` 前缀化后匹配不到)。骨架层只需在包裹节点上补一个
121
+ `data-manohub-ui` 属性,服务层不必反向依赖它。
122
+ 注:`.app-container` 是 `@manohub/kit` 的输出类名(内部命名),**不是**跨包契约,服务层不认它。
123
+ 3. **按需挂载、自动收尾**:首次调用才建宿主与 Vue 实例(模块顶层零 DOM 操作);
124
+ `toast` 队列空了、`loading` 计数归零即收掉宿主 —— 反复重挂不留悬挂实例。
125
+ 4. **`loading` 是引用计数语义**:并发的两次「显示」只关一次时遮罩仍在(先结束的那个不会把
126
+ 别人的遮罩一起收掉)。推荐用返回的句柄在 `finally` 里关掉自己那一次。
127
+
128
+ ## 主题层
129
+
130
+ 全局令牌(`--ui-primary` / `--ui-space-*` / `--ui-font-*` 这类跨件共用的值)**不在本包**,已拆成独立包
131
+ **`@manohub/theme`**(值层与面层分开:值随主题变、面不随主题变)。每个主题分五片:
132
+ `colors` / `typography` / `spacing` / `radius` / `shadows`,一律锚定 **`[data-manohub-ui]`**
133
+ (库品牌作用域锚;micro-app 的 scopecss 不作用域化 `:root`,定义在 `:root` 会泄漏到宿主)。
134
+
135
+ 只服务单一组件的令牌分两半落在本包:
136
+
137
+ | 位置 | 内容 | 锚 |
138
+ |---|---|---|
139
+ | `styles/components/<件>.tokens.css` | **基础值**(唯一的、两套主题同值的那一条) | `[data-manohub-ui]` |
140
+ | `styles/components/<件>.css` | **档位改写**(`.mh-<件>--<档>` 覆盖同一令牌)+ `--mh-*` 内部状态变量 | 元件根类 / 档位类 |
141
+
142
+ 后一半必须在面里:它们靠「同一元素上两个选择器改写同一个令牌」生效,搬走就断链。
143
+
144
+ 随包两套:
145
+
146
+ | 主题 | 入口 | 锚点 | 取值来源 |
147
+ |---|---|---|---|
148
+ | `default` | `@manohub/theme/default.css` | `[data-manohub-ui]`(兜底套) | AIHub 品牌蓝(primary `#2660FF`)|
149
+ | `farris` | `@manohub/theme/farris.css` | `[data-manohub-ui][data-theme='farris']` | 《Farris Web 基础视觉规范》(primary `#2a87ff`,色板编号 Th/Au/Ne/Te 逐条标注)|
150
+
151
+ 换皮只需给容器加属性(`createSubApp({ theme: 'farris' })` 会写 `data-theme`),不动类名、不动组件代码、不重打包。
152
+ 新增主题 = `packages/theme/src/<name>/` 新目录,令牌锚 `[data-manohub-ui][data-theme='<name>']`,
153
+ 且必须**全量覆盖** default 的令牌。
154
+
155
+ 局部换主题:在更深的容器上重设变量即可,例如 `.role-tree-pane { --ui-primary: #7C3AED; }`。
@@ -111,7 +111,14 @@ const FormItem = /* @__PURE__ */ defineComponent({
111
111
  "data-invalid": props.error ? "true" : "false",
112
112
  "style": styles
113
113
  }, [showLabel ? createVNode("label", {
114
- "class": "mh-form-item__label"
114
+ "class": "mh-form-item__label",
115
+ "onClick": (
116
+ // 只在真有点击可转发(有 tip ⇒ label 里才有 labelable 元素)时挂;理由见下方 ③
117
+ props.tip ? (event) => {
118
+ if (event.target?.closest(".mh-form-item__tip-btn")) return;
119
+ event.preventDefault();
120
+ } : void 0
121
+ )
115
122
  }, [createVNode("span", {
116
123
  "class": "mh-form-item__label-text"
117
124
  }, [props.label]), props.required ? createVNode("span", {
@@ -0,0 +1,15 @@
1
+ /**
2
+ * `bordered` 三档 → 控件类名(**唯一来源**)。
3
+ *
4
+ * 输入族三个件(`Input` / `Textarea` / `NumberInput`)共用同一份面与同一个 prop,判定表抄三份必然漂,
5
+ * 故收在这里。类名口径:prop 是**肯定式**(`bordered`)、类名是**否定式**(`--borderless`)——
6
+ * 那一对的反差是库内既定口径(见 `docs/guide/naming-and-defaults.md` §2.1);
7
+ * `'focus'` 档的类名 `--border-on-focus` 是这套口径下的新成员:它说的就是「聚焦时才有框」。
8
+ *
9
+ * | `bordered` | 类名 | 常态 | 悬停 | 聚焦 |
10
+ * | --- | --- | --- | --- | --- |
11
+ * | `true`(缺省) | (无) | 有框 | 框变色 | 框变色 + 主色环 |
12
+ * | `'focus'` | `.mh-input--border-on-focus` | 无框无底 | 复用有框档那句 | 显框 + 主色环(复用) |
13
+ * | `false` | `.mh-input--borderless` | 无框无底 | 无 | 无(本档效果只有一种) |
14
+ */
15
+ export declare const borderedClassOf: (bordered: boolean | "focus" | undefined) => string;
@@ -0,0 +1,8 @@
1
+ const borderedClassOf = (bordered) => {
2
+ if (bordered === "focus") return "mh-input--border-on-focus";
3
+ if (bordered === false) return "mh-input--borderless";
4
+ return "";
5
+ };
6
+ export {
7
+ borderedClassOf
8
+ };
@@ -109,11 +109,19 @@ export declare const Input: import("vue").DefineComponent<import("vue").ExtractP
109
109
  default: boolean;
110
110
  };
111
111
  /**
112
- * 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底,靠悬停与聚焦给反馈)。
113
- * 配 `readonly` 即**只读值档**:连悬停底与聚焦框环都退掉(见 `readonly`)。
112
+ * 有无边框,三档(判定表见 `./bordered.ts`,输入族三件共用):
113
+ *
114
+ * - `true`(缺省)**常有框**;
115
+ * - `false` **无框** —— 常态、悬停、聚焦都不出现(本档的效果只有一种);
116
+ * - `'focus'` **聚焦(与悬停)才显出框** —— 行内编辑那档:常态看着像「值」,点进去才亮框;
117
+ * 悬停 / 聚焦的表现**复用有框档那两条**,不另写一套。
118
+ *
119
+ * 配 `readonly` 即**只读值档**(摘要 / 详情里回显业务值的推荐写法,也是 `Form.Item` 的
120
+ * `readonly` 行配的那一档):几何与可编辑输入框完全一致,但不给任何「可编辑」线索。
121
+ * 只读值档请写 `readonly` + `bordered={false}`;`readonly` + `'focus'` 会被中和(兜底,不推荐)。
114
122
  */
115
123
  bordered: {
116
- type: BooleanConstructor;
124
+ type: PropType<boolean | "focus">;
117
125
  default: boolean;
118
126
  };
119
127
  maxlength: {
@@ -228,11 +236,19 @@ export declare const Input: import("vue").DefineComponent<import("vue").ExtractP
228
236
  default: boolean;
229
237
  };
230
238
  /**
231
- * 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底,靠悬停与聚焦给反馈)。
232
- * 配 `readonly` 即**只读值档**:连悬停底与聚焦框环都退掉(见 `readonly`)。
239
+ * 有无边框,三档(判定表见 `./bordered.ts`,输入族三件共用):
240
+ *
241
+ * - `true`(缺省)**常有框**;
242
+ * - `false` **无框** —— 常态、悬停、聚焦都不出现(本档的效果只有一种);
243
+ * - `'focus'` **聚焦(与悬停)才显出框** —— 行内编辑那档:常态看着像「值」,点进去才亮框;
244
+ * 悬停 / 聚焦的表现**复用有框档那两条**,不另写一套。
245
+ *
246
+ * 配 `readonly` 即**只读值档**(摘要 / 详情里回显业务值的推荐写法,也是 `Form.Item` 的
247
+ * `readonly` 行配的那一档):几何与可编辑输入框完全一致,但不给任何「可编辑」线索。
248
+ * 只读值档请写 `readonly` + `bordered={false}`;`readonly` + `'focus'` 会被中和(兜底,不推荐)。
233
249
  */
234
250
  bordered: {
235
- type: BooleanConstructor;
251
+ type: PropType<boolean | "focus">;
236
252
  default: boolean;
237
253
  };
238
254
  maxlength: {
@@ -289,7 +305,7 @@ export declare const Input: import("vue").DefineComponent<import("vue").ExtractP
289
305
  type: string;
290
306
  onChange: (v: string) => void;
291
307
  invalid: boolean;
292
- bordered: boolean;
308
+ bordered: boolean | "focus";
293
309
  defaultValue: string;
294
310
  placeholder: string;
295
311
  autofocus: boolean;
@@ -297,10 +313,10 @@ export declare const Input: import("vue").DefineComponent<import("vue").ExtractP
297
313
  status: InputStatus;
298
314
  as: "button" | "input";
299
315
  readonly: boolean;
316
+ onEnter: (v: string) => void;
300
317
  clearable: boolean;
301
318
  maxlength: number;
302
319
  showCount: boolean;
303
- onEnter: (v: string) => void;
304
320
  controlClass: string;
305
321
  controlAttrs: Record<string, unknown>;
306
322
  hasValue: boolean;
@@ -328,8 +344,8 @@ export interface TextareaProps {
328
344
  invalid?: boolean;
329
345
  /** 校验状态(`invalid` 的显式写法) */
330
346
  status?: InputStatus;
331
- /** 是否带边框 */
332
- bordered?: boolean;
347
+ /** 是否带边框(三档口径见 `Input` 的同名 prop:`true` / `false` / `'focus'`) */
348
+ bordered?: boolean | 'focus';
333
349
  maxlength?: number;
334
350
  /** 右下角字数统计(须给 `maxlength` 才渲染) */
335
351
  showCount?: boolean;
@@ -388,8 +404,9 @@ export declare const Textarea: import("vue").DefineComponent<import("vue").Extra
388
404
  type: PropType<InputStatus>;
389
405
  default: undefined;
390
406
  };
407
+ /** 是否带边框(三档口径见 `Input` 的同名 prop:`true` 常有框 / `false` 无框 / `'focus'` 聚焦才显框) */
391
408
  bordered: {
392
- type: BooleanConstructor;
409
+ type: PropType<boolean | "focus">;
393
410
  default: boolean;
394
411
  };
395
412
  maxlength: {
@@ -453,8 +470,9 @@ export declare const Textarea: import("vue").DefineComponent<import("vue").Extra
453
470
  type: PropType<InputStatus>;
454
471
  default: undefined;
455
472
  };
473
+ /** 是否带边框(三档口径见 `Input` 的同名 prop:`true` 常有框 / `false` 无框 / `'focus'` 聚焦才显框) */
456
474
  bordered: {
457
- type: BooleanConstructor;
475
+ type: PropType<boolean | "focus">;
458
476
  default: boolean;
459
477
  };
460
478
  maxlength: {
@@ -481,7 +499,7 @@ export declare const Textarea: import("vue").DefineComponent<import("vue").Extra
481
499
  disabled: boolean;
482
500
  onChange: (v: string) => void;
483
501
  invalid: boolean;
484
- bordered: boolean;
502
+ bordered: boolean | "focus";
485
503
  defaultValue: string;
486
504
  placeholder: string;
487
505
  rows: number;
@@ -558,11 +576,19 @@ export declare const InputCompound: {
558
576
  default: boolean;
559
577
  };
560
578
  /**
561
- * 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底,靠悬停与聚焦给反馈)。
562
- * 配 `readonly` 即**只读值档**:连悬停底与聚焦框环都退掉(见 `readonly`)。
579
+ * 有无边框,三档(判定表见 `./bordered.ts`,输入族三件共用):
580
+ *
581
+ * - `true`(缺省)**常有框**;
582
+ * - `false` **无框** —— 常态、悬停、聚焦都不出现(本档的效果只有一种);
583
+ * - `'focus'` **聚焦(与悬停)才显出框** —— 行内编辑那档:常态看着像「值」,点进去才亮框;
584
+ * 悬停 / 聚焦的表现**复用有框档那两条**,不另写一套。
585
+ *
586
+ * 配 `readonly` 即**只读值档**(摘要 / 详情里回显业务值的推荐写法,也是 `Form.Item` 的
587
+ * `readonly` 行配的那一档):几何与可编辑输入框完全一致,但不给任何「可编辑」线索。
588
+ * 只读值档请写 `readonly` + `bordered={false}`;`readonly` + `'focus'` 会被中和(兜底,不推荐)。
563
589
  */
564
590
  bordered: {
565
- type: BooleanConstructor;
591
+ type: PropType<boolean | "focus">;
566
592
  default: boolean;
567
593
  };
568
594
  maxlength: {
@@ -621,7 +647,7 @@ export declare const InputCompound: {
621
647
  type: string;
622
648
  onChange: (v: string) => void;
623
649
  invalid: boolean;
624
- bordered: boolean;
650
+ bordered: boolean | "focus";
625
651
  defaultValue: string;
626
652
  placeholder: string;
627
653
  autofocus: boolean;
@@ -629,10 +655,10 @@ export declare const InputCompound: {
629
655
  status: InputStatus;
630
656
  as: "button" | "input";
631
657
  readonly: boolean;
658
+ onEnter: (v: string) => void;
632
659
  clearable: boolean;
633
660
  maxlength: number;
634
661
  showCount: boolean;
635
- onEnter: (v: string) => void;
636
662
  controlClass: string;
637
663
  controlAttrs: Record<string, unknown>;
638
664
  hasValue: boolean;
@@ -711,11 +737,19 @@ export declare const InputCompound: {
711
737
  default: boolean;
712
738
  };
713
739
  /**
714
- * 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底,靠悬停与聚焦给反馈)。
715
- * 配 `readonly` 即**只读值档**:连悬停底与聚焦框环都退掉(见 `readonly`)。
740
+ * 有无边框,三档(判定表见 `./bordered.ts`,输入族三件共用):
741
+ *
742
+ * - `true`(缺省)**常有框**;
743
+ * - `false` **无框** —— 常态、悬停、聚焦都不出现(本档的效果只有一种);
744
+ * - `'focus'` **聚焦(与悬停)才显出框** —— 行内编辑那档:常态看着像「值」,点进去才亮框;
745
+ * 悬停 / 聚焦的表现**复用有框档那两条**,不另写一套。
746
+ *
747
+ * 配 `readonly` 即**只读值档**(摘要 / 详情里回显业务值的推荐写法,也是 `Form.Item` 的
748
+ * `readonly` 行配的那一档):几何与可编辑输入框完全一致,但不给任何「可编辑」线索。
749
+ * 只读值档请写 `readonly` + `bordered={false}`;`readonly` + `'focus'` 会被中和(兜底,不推荐)。
716
750
  */
717
751
  bordered: {
718
- type: BooleanConstructor;
752
+ type: PropType<boolean | "focus">;
719
753
  default: boolean;
720
754
  };
721
755
  maxlength: {
@@ -772,7 +806,7 @@ export declare const InputCompound: {
772
806
  type: string;
773
807
  onChange: (v: string) => void;
774
808
  invalid: boolean;
775
- bordered: boolean;
809
+ bordered: boolean | "focus";
776
810
  defaultValue: string;
777
811
  placeholder: string;
778
812
  autofocus: boolean;
@@ -780,10 +814,10 @@ export declare const InputCompound: {
780
814
  status: InputStatus;
781
815
  as: "button" | "input";
782
816
  readonly: boolean;
817
+ onEnter: (v: string) => void;
783
818
  clearable: boolean;
784
819
  maxlength: number;
785
820
  showCount: boolean;
786
- onEnter: (v: string) => void;
787
821
  controlClass: string;
788
822
  controlAttrs: Record<string, unknown>;
789
823
  hasValue: boolean;
@@ -855,11 +889,19 @@ export declare const InputCompound: {
855
889
  default: boolean;
856
890
  };
857
891
  /**
858
- * 是否带边框;`false` 用于表格内编辑 / 行内编辑(无框、无底,靠悬停与聚焦给反馈)。
859
- * 配 `readonly` 即**只读值档**:连悬停底与聚焦框环都退掉(见 `readonly`)。
892
+ * 有无边框,三档(判定表见 `./bordered.ts`,输入族三件共用):
893
+ *
894
+ * - `true`(缺省)**常有框**;
895
+ * - `false` **无框** —— 常态、悬停、聚焦都不出现(本档的效果只有一种);
896
+ * - `'focus'` **聚焦(与悬停)才显出框** —— 行内编辑那档:常态看着像「值」,点进去才亮框;
897
+ * 悬停 / 聚焦的表现**复用有框档那两条**,不另写一套。
898
+ *
899
+ * 配 `readonly` 即**只读值档**(摘要 / 详情里回显业务值的推荐写法,也是 `Form.Item` 的
900
+ * `readonly` 行配的那一档):几何与可编辑输入框完全一致,但不给任何「可编辑」线索。
901
+ * 只读值档请写 `readonly` + `bordered={false}`;`readonly` + `'focus'` 会被中和(兜底,不推荐)。
860
902
  */
861
903
  bordered: {
862
- type: BooleanConstructor;
904
+ type: PropType<boolean | "focus">;
863
905
  default: boolean;
864
906
  };
865
907
  maxlength: {
@@ -918,7 +960,7 @@ export declare const InputCompound: {
918
960
  type: string;
919
961
  onChange: (v: string) => void;
920
962
  invalid: boolean;
921
- bordered: boolean;
963
+ bordered: boolean | "focus";
922
964
  defaultValue: string;
923
965
  placeholder: string;
924
966
  autofocus: boolean;
@@ -926,10 +968,10 @@ export declare const InputCompound: {
926
968
  status: InputStatus;
927
969
  as: "button" | "input";
928
970
  readonly: boolean;
971
+ onEnter: (v: string) => void;
929
972
  clearable: boolean;
930
973
  maxlength: number;
931
974
  showCount: boolean;
932
- onEnter: (v: string) => void;
933
975
  controlClass: string;
934
976
  controlAttrs: Record<string, unknown>;
935
977
  hasValue: boolean;
@@ -980,8 +1022,9 @@ export declare const InputCompound: {
980
1022
  type: PropType<InputStatus>;
981
1023
  default: undefined;
982
1024
  };
1025
+ /** 是否带边框(三档口径见 `Input` 的同名 prop:`true` 常有框 / `false` 无框 / `'focus'` 聚焦才显框) */
983
1026
  bordered: {
984
- type: BooleanConstructor;
1027
+ type: PropType<boolean | "focus">;
985
1028
  default: boolean;
986
1029
  };
987
1030
  maxlength: {
@@ -1045,8 +1088,9 @@ export declare const InputCompound: {
1045
1088
  type: PropType<InputStatus>;
1046
1089
  default: undefined;
1047
1090
  };
1091
+ /** 是否带边框(三档口径见 `Input` 的同名 prop:`true` 常有框 / `false` 无框 / `'focus'` 聚焦才显框) */
1048
1092
  bordered: {
1049
- type: BooleanConstructor;
1093
+ type: PropType<boolean | "focus">;
1050
1094
  default: boolean;
1051
1095
  };
1052
1096
  maxlength: {
@@ -1073,7 +1117,7 @@ export declare const InputCompound: {
1073
1117
  disabled: boolean;
1074
1118
  onChange: (v: string) => void;
1075
1119
  invalid: boolean;
1076
- bordered: boolean;
1120
+ bordered: boolean | "focus";
1077
1121
  defaultValue: string;
1078
1122
  placeholder: string;
1079
1123
  rows: number;
@@ -1102,3 +1146,10 @@ export declare const InputCompound: {
1102
1146
  }>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
1103
1147
  };
1104
1148
  export type InputCompoundType = typeof InputCompound;
1149
+ /**
1150
+ * 数字输入与 `Input` **同族**(同一个表壳 / 内距 / 状态协议,同一份面 `input.css`),
1151
+ * 故与 `Textarea` 同款处置:**平级导出、共用本目录**(`AGENTS.md` 硬约束 1)。
1152
+ * 它**不是** `Input` 的点号成员 —— 成员位留给「同一件自己的形态」(`.Textarea` / `.Display`),
1153
+ * 而数字输入是另一件(值模型 `number | null` vs `string`)。
1154
+ */
1155
+ export { NumberInput, type NumberInputProps } from './number-input';