dsh-plugin-core 0.35.2 → 0.35.4
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/adv/explorer/FileBrowser.js +4 -4
- package/dist/adv/explorer/FileBrowser.js.map +1 -1
- package/dist/adv/explorer/crumb-nav.js +1 -1
- package/dist/adv/explorer/crumb-nav.js.map +1 -1
- package/dist/adv/explorer/explorer-css.generated.d.ts +1 -1
- package/dist/adv/explorer/explorer-css.generated.d.ts.map +1 -1
- package/dist/adv/explorer/explorer-css.generated.js +1 -1
- package/dist/adv/explorer/explorer-css.generated.js.map +1 -1
- package/dist/adv/explorer/landing.js +1 -1
- package/dist/adv/explorer/landing.js.map +1 -1
- package/dist/adv/explorer/preview/TextPreview.js +1 -1
- package/dist/adv/explorer/preview/TextPreview.js.map +1 -1
- package/dist/design/tokens-css.generated.d.ts +1 -1
- package/dist/design/tokens-css.generated.d.ts.map +1 -1
- package/dist/design/tokens-css.generated.js +1 -1
- package/dist/design/tokens-css.generated.js.map +1 -1
- package/dist/ui/actions/Button.d.ts +4 -0
- package/dist/ui/actions/Button.d.ts.map +1 -1
- package/dist/ui/actions/Button.js +7 -5
- package/dist/ui/actions/Button.js.map +1 -1
- package/dist/ui/actions/button-css.generated.d.ts +1 -1
- package/dist/ui/actions/button-css.generated.d.ts.map +1 -1
- package/dist/ui/actions/button-css.generated.js +1 -1
- package/dist/ui/actions/button-css.generated.js.map +1 -1
- package/dist/ui/display/Card.d.ts +4 -0
- package/dist/ui/display/Card.d.ts.map +1 -1
- package/dist/ui/display/Card.js +2 -0
- package/dist/ui/display/Card.js.map +1 -1
- package/dist/ui/display/CodeViewer.js +1 -1
- package/dist/ui/display/CodeViewer.js.map +1 -1
- package/dist/ui/display/ListRow.d.ts +38 -0
- package/dist/ui/display/ListRow.d.ts.map +1 -0
- package/dist/ui/display/ListRow.js +69 -0
- package/dist/ui/display/ListRow.js.map +1 -0
- package/dist/ui/display/card-css.generated.d.ts +1 -1
- package/dist/ui/display/card-css.generated.d.ts.map +1 -1
- package/dist/ui/display/card-css.generated.js +1 -1
- package/dist/ui/display/card-css.generated.js.map +1 -1
- package/dist/ui/display/code-viewer-css.generated.d.ts +1 -1
- package/dist/ui/display/code-viewer-css.generated.d.ts.map +1 -1
- package/dist/ui/display/code-viewer-css.generated.js +1 -1
- package/dist/ui/display/code-viewer-css.generated.js.map +1 -1
- package/dist/ui/display/list-row-css.d.ts +2 -0
- package/dist/ui/display/list-row-css.d.ts.map +1 -0
- package/dist/ui/display/list-row-css.generated.d.ts +2 -0
- package/dist/ui/display/list-row-css.generated.d.ts.map +1 -0
- package/dist/ui/display/list-row-css.generated.js +4 -0
- package/dist/ui/display/list-row-css.generated.js.map +1 -0
- package/dist/ui/display/list-row-css.js +7 -0
- package/dist/ui/display/list-row-css.js.map +1 -0
- package/dist/ui/feedback/Modal.js +1 -1
- package/dist/ui/feedback/Modal.js.map +1 -1
- package/dist/ui/feedback/SideSheet.js +1 -1
- package/dist/ui/feedback/SideSheet.js.map +1 -1
- package/dist/ui/feedback/Toast.d.ts.map +1 -1
- package/dist/ui/feedback/Toast.js +15 -8
- package/dist/ui/feedback/Toast.js.map +1 -1
- package/dist/ui/feedback/feedback-css.generated.d.ts +1 -1
- package/dist/ui/feedback/feedback-css.generated.d.ts.map +1 -1
- package/dist/ui/feedback/feedback-css.generated.js +1 -1
- package/dist/ui/feedback/feedback-css.generated.js.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +2 -0
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/infra/panel-css.generated.d.ts +1 -1
- package/dist/ui/infra/panel-css.generated.d.ts.map +1 -1
- package/dist/ui/infra/panel-css.generated.js +1 -1
- package/dist/ui/infra/panel-css.generated.js.map +1 -1
- package/dist/ui/infra/side-panel-css.generated.d.ts +1 -1
- package/dist/ui/infra/side-panel-css.generated.d.ts.map +1 -1
- package/dist/ui/infra/side-panel-css.generated.js +1 -1
- package/dist/ui/infra/side-panel-css.generated.js.map +1 -1
- package/dist/ui/inputs/datetime-css.generated.d.ts +1 -1
- package/dist/ui/inputs/datetime-css.generated.d.ts.map +1 -1
- package/dist/ui/inputs/datetime-css.generated.js +1 -1
- package/dist/ui/inputs/datetime-css.generated.js.map +1 -1
- package/dist/ui/inputs/picker-css.generated.d.ts +1 -1
- package/dist/ui/inputs/picker-css.generated.d.ts.map +1 -1
- package/dist/ui/inputs/picker-css.generated.js +1 -1
- package/dist/ui/inputs/picker-css.generated.js.map +1 -1
- package/dist/ui/layout/PanelDock.js +2 -2
- package/dist/ui/layout/PanelDock.js.map +1 -1
- package/docs/design/structure.md +3 -3
- package/docs/design/style.md +102 -45
- package/docs/help/ui.doc.ts +66 -1
- package/docs/help/ui.md +61 -1
- package/package.json +1 -1
package/docs/design/style.md
CHANGED
|
@@ -156,58 +156,83 @@ EN: Welding controls into one visual unit has exactly one recipe: the shell owns
|
|
|
156
156
|
`SplitGroup` overrides inner classes with descendant selectors, so those class names are the coupling point —
|
|
157
157
|
rename them and you must update `src/ui/layout/split-group.css`, or the weld fails silently.
|
|
158
158
|
|
|
159
|
-
##
|
|
159
|
+
## 滚动条让位:只挂 `.dshpc-scroll-thin`
|
|
160
160
|
|
|
161
|
-
> EN:
|
|
161
|
+
> EN: Scrollbar inset: just hang `.dshpc-scroll-thin`
|
|
162
162
|
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
**唯一真源**:`src/design/tokens.css` 的 tokens 域(`ensureComponentStyles` 每次必注册、且排在
|
|
164
|
+
所有控件域之前 ⇒ 任何组件挂类即生效,不必额外注入 `panel` 域)。这个类**同时**给两样东西:
|
|
165
165
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
166
|
+
| 它给什么 | 唯一可调值 |
|
|
167
|
+
|---|---|
|
|
168
|
+
| 细滚动条:线宽、thumb 上下端圆角、track 透明 | 字面量三处同值(`width` / `height` / `border-radius`,现行值见 `tokens.css`) |
|
|
169
|
+
| 离边 1px 让位(容器自己的透明边框) | `--dshpc-scroll-inset`(现 1px) |
|
|
169
170
|
|
|
170
|
-
|
|
171
|
+
⚠️ **线宽不能放进 CSS 变量**:WebKit 的 `::-webkit-scrollbar` 系列伪元素不解析 `var()`,带变量的
|
|
172
|
+
声明会被静默丢弃 ⇒ 宽度整条作废、退回默认值。2026-11-06 把 `width:2px` 改成
|
|
173
|
+
`var(--dshpc-scroll-w)` 后真机当场打回,用户发现。所以线宽与圆角**必须写字面量**,改宽度只改
|
|
174
|
+
那一个规则块的三处数字;让位在普通元素上,仍走 `--dshpc-scroll-inset` 一行。
|
|
175
|
+
**不在任何别的 CSS 里抄规则、也不在组件里另写透明边框**。
|
|
171
176
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
-
|
|
175
|
-
|
|
176
|
-
- 消费方自己的滚动容器同理,自己加那条边框。
|
|
177
|
+
原理:原生滚动条画在 padding box 之外、border 之内 —— 只有 border 能把它推离边缘,加 padding 无效。
|
|
178
|
+
实现只写 `::-webkit-scrollbar`,**不要写 `scrollbar-width`**:Chrome 一见它就切到规范化滚动条、
|
|
179
|
+
忽略这里设的宽度(2026-11-06 实测宽度回到 ~10px)。2026-11-06 用户口径:1px 太细 ⇒ 线宽起步
|
|
180
|
+
2px、thumb 上下端圆角;后续宽度调整只改 `tokens.css` 那三处,自检不写死数字。
|
|
177
181
|
|
|
178
|
-
|
|
179
|
-
所以只能每个容器自己声明,由 `test/quality.test.mjs` 扫源码兜住漏写。
|
|
182
|
+
### 方向档(CSS 没有 `:scrollable`,推不出滚动方向,只能显式声明)
|
|
180
183
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
184
|
+
| 滚动方向 | 挂什么 | 让位落在 |
|
|
185
|
+
|---|---|---|
|
|
186
|
+
| 竖向(`overflow-y:auto`) | `dshpc-scroll-thin` | 右 1px |
|
|
187
|
+
| 横向(`overflow-x:auto`) | `dshpc-scroll-thin dshpc-scroll-thin--x` | 底 1px、右归零 |
|
|
188
|
+
| 双向(`overflow:auto`)且底部也要让 | `dshpc-scroll-thin dshpc-scroll-thin--both` | 右 + 底各 1px |
|
|
189
|
+
|
|
190
|
+
方向档**必须与基类同挂**(线宽规则只认基类),`test/quality.test.mjs` 盯着。只要右让位的双向容器
|
|
191
|
+
(贴边面板 body、弹窗 body)挂基类即可,不必加 `--both`。
|
|
192
|
+
|
|
193
|
+
### 四处例外
|
|
194
|
+
|
|
195
|
+
- **本身已有实色边框**(▾ 选层菜单挂 `.dshpc-panel`):`:not(.dshpc-panel)` 跳过让位,滚动条贴
|
|
196
|
+
那条实色线即算让位;无条件叠透明边框会把实色右边框覆盖成透明。挂基类仍拿细滚动条。
|
|
197
|
+
- **CodeMirror**:`.cm-scroller` 由官方生成、挂不上类 ⇒ 类挂外层 `.dshpc-cv__editor`,tokens.css
|
|
198
|
+
用后代选择器一并命中线宽与**右**让位;只让右不让底(给底会改 scroller 高度,CodeMirror 的
|
|
199
|
+
测量循环会多算一条横滚条)。外层自己 `overflow:hidden` 不滚 ⇒ 在 `code-viewer.css` 把
|
|
200
|
+
`--dshpc-scroll-inset-right` 设 `0px` 关掉自身让位,免得两层各 1px 叠成 2px。
|
|
201
|
+
- **消费方自己的滚动容器**(demo 左导航 / main 主区 / 表格横滚区):配方相同,挂类即可,
|
|
202
|
+
**不再自己写 `border-right: 1px solid transparent`**。2026-11-02 起 `--dshpc-dock-gap` 已删,
|
|
203
|
+
dock 不替主区让 margin。
|
|
204
|
+
- **面板边框本身**:`test/quality.test.mjs` 仍断言 `.dshpc-panel` 带边框(上一条例外的前提)。
|
|
205
|
+
|
|
206
|
+
### 自检
|
|
207
|
+
|
|
208
|
+
`test/quality.test.mjs` 三条:① 所有滚动容器都挂基类(含 demo 的内联 `overflowY/X` —— 旧自检
|
|
209
|
+
只扫 CSS 文件,漏过 demo);② 1px 让位边框不许在 tokens.css 之外出现;③ `::-webkit-scrollbar`
|
|
210
|
+
只许在 tokens.css 出现一份,且线宽 / 圆角必须是同一个字面量(防这次的 var 化回潮)。旧口径
|
|
211
|
+
「CSS 没有 `:scrollable`,只能每个容器自己声明」已被类取代。
|
|
212
|
+
|
|
213
|
+
EN: `.dshpc-scroll-thin` in `src/design/tokens.css` (tokens domain, registered on every
|
|
214
|
+
`ensureComponentStyles` call and sorted first) is the single source of truth for **both** the hairline
|
|
215
|
+
(a literal, identical value in `width` / `height` / `border-radius`) and the 1px inset
|
|
216
|
+
(`--dshpc-scroll-inset`, 1px). Never put the line width in a CSS variable: WebKit scrollbar
|
|
217
|
+
pseudo-elements do not resolve `var()`, so such declarations are silently dropped and the width falls
|
|
218
|
+
back to the default (hit in a real client on 2026-11-06). Hang the class; never duplicate the rules.
|
|
219
|
+
Native scrollbars sit between the padding box and the border, so only a border can push them off the
|
|
220
|
+
edge — padding does nothing. Use `::-webkit-scrollbar` only: adding `scrollbar-width` makes Chrome
|
|
221
|
+
switch to the standardized scrollbar and ignore the width (measured ~10px on 2026-11-06).
|
|
222
|
+
|
|
223
|
+
Direction variants (no `:scrollable` selector exists, so direction must be stated explicitly):
|
|
224
|
+
`dshpc-scroll-thin` for vertical (right inset), `+ dshpc-scroll-thin--x` for horizontal (bottom inset,
|
|
225
|
+
right zeroed), `+ dshpc-scroll-thin--both` for two-axis (right + bottom). Variants must be hung together
|
|
226
|
+
with the base class.
|
|
227
|
+
|
|
228
|
+
Exceptions: containers already carrying a solid border (`.dshpc-panel`) are skipped via
|
|
229
|
+
`:not(.dshpc-panel)` — their border is the inset. CodeMirror generates `.cm-scroller` and cannot take a
|
|
230
|
+
class, so the class goes on `.dshpc-cv__editor` and a descendant selector in tokens.css covers line width
|
|
231
|
+
and the **right** inset only (a bottom border changes scroller height and makes CodeMirror's measurement
|
|
232
|
+
loop add a phantom horizontal scrollbar); the non-scrolling outer layer sets
|
|
233
|
+
`--dshpc-scroll-inset-right:0px` in `code-viewer.css` to avoid stacking two 1px gaps. Consumer-owned
|
|
234
|
+
scroll containers (demo sidebar / main / table wrappers) just hang the class. `test/quality.test.mjs`
|
|
235
|
+
checks all three, including inline `overflowY/X` in the demo.
|
|
211
236
|
|
|
212
237
|
## 浮层三档:模态 / 锚点面板 / 菜单
|
|
213
238
|
|
|
@@ -249,3 +274,35 @@ one-liner; `--dshpc-dock-gap` was removed on 2026-11-02. A test enforces the bor
|
|
|
249
274
|
|
|
250
275
|
EN: No second `document.createElement('style')` — always go through the kit. No second style tag id — one kit
|
|
251
276
|
per plugin. Rules inside a domain consume `var(--dshpc-*)` only; never hard-code colors, sizes or fonts.
|
|
277
|
+
|
|
278
|
+
## 控件默认字号
|
|
279
|
+
|
|
280
|
+
> EN: Default type scale for controls
|
|
281
|
+
|
|
282
|
+
`Surface` 是纯容器,只给外框,`font:inherit` 刻意不接管内容排版;**但承载文字的控件必须在自己的域内给默认字号**
|
|
283
|
+
(`font-size:var(--dshpc-font-md)` + `line-height:var(--dshpc-line-md)`,与 `Table` / `Input md` 同档)。
|
|
284
|
+
不许把这件事推给每个消费者重复补一遍 —— 2026-11-07 用户拍板:`ListRow` 曾漏设,会话列表标题继承到宿主默认
|
|
285
|
+
16px,消费者被迫自己写 `fontSize` 压回去。调用方仍可用内联 style 覆盖这一档。
|
|
286
|
+
|
|
287
|
+
EN: `Surface` is a pure frame and deliberately inherits typography, but any control that renders text must set its
|
|
288
|
+
own default `font-size` / `line-height` from tokens (same tier as `Table` / `Input md`). Do not make every consumer
|
|
289
|
+
repeat it; callers can still override that tier inline.
|
|
290
|
+
|
|
291
|
+
## 卡片整卡 hover 与底色轴
|
|
292
|
+
|
|
293
|
+
> EN: Whole-card hover and fill switch
|
|
294
|
+
|
|
295
|
+
`Card` / `StripCard` 的整卡悬停统一吃 `--dshpc-card-hover`(亮 `rgba(37,99,235,.07)` / 暗
|
|
296
|
+
`rgba(96,165,250,.13)`),由 `hoverable` 开启、**默认关**;底色由 `filled` 控制、**默认有底**。
|
|
297
|
+
两者都只改 `--surface-bg` 一个变量,不许写 `background` / `background-image` 属性去覆盖底色 ——
|
|
298
|
+
`StripCard` 的浅底也必须走这个变量,否则覆盖顺序会打架(兄弟项目曾用渐变叠加绕过,2026-11-07 收编)。
|
|
299
|
+
|
|
300
|
+
EN: Whole-card hover on `Card` / `StripCard` uses `--dshpc-card-hover` only; it is opt-in via `hoverable`
|
|
301
|
+
(default off). The fill is controlled by `filled` (default on). Both only rewrite `--surface-bg`; never
|
|
302
|
+
override the fill with `background` / `background-image` properties.
|
|
303
|
+
|
|
304
|
+
开启 `hoverable` 时**同时**给 `cursor:pointer` —— 既然声明整卡可交互,手势不能只换背景不变;
|
|
305
|
+
没开这根轴的卡片保持默认光标(2026-11-07 用户反馈)。
|
|
306
|
+
|
|
307
|
+
EN: Enabling `hoverable` also sets `cursor:pointer`: a card that declares itself interactive must change
|
|
308
|
+
the pointer too, not just its background. Cards without the axis keep the default cursor.
|
package/docs/help/ui.doc.ts
CHANGED
|
@@ -28,6 +28,7 @@ const cardExample = `import { Card, Chip, StripCard } from 'dsh-plugin-core/ui'
|
|
|
28
28
|
h(Card, {
|
|
29
29
|
accent: 'warning',
|
|
30
30
|
bar: { side: 'left', inset: 12, gap: 12 }, // 色条可放 left / right / top / bottom
|
|
31
|
+
hoverable: true, // 整卡 hover 变色(默认关)
|
|
31
32
|
action: h(Button, { size: 'sm', variant: 'ghost' }, '跳过'),
|
|
32
33
|
},
|
|
33
34
|
h('div', null, '标题'),
|
|
@@ -36,6 +37,7 @@ h(Card, {
|
|
|
36
37
|
|
|
37
38
|
h(StripCard, {
|
|
38
39
|
accent: 'success',
|
|
40
|
+
hoverable: true, filled: false, // 底色关掉 = 透明(默认有底)
|
|
39
41
|
expanded: open, onToggle: () => setOpen(!open),
|
|
40
42
|
panel: h('div', null, '展开详情'),
|
|
41
43
|
}, h('div', null, '标题 + 元信息'))`
|
|
@@ -51,6 +53,25 @@ h(Surface, { border: 'faint', padding: 'lg' }, h(Card, null, '框里再放卡'))
|
|
|
51
53
|
// 虚线占位框也走它,不要自己写 1px dashed:
|
|
52
54
|
h(Surface, { tone: 'sunken', border: 'dashed', padding: 'lg' }, '还没做的东西')`
|
|
53
55
|
|
|
56
|
+
const listRowExample = `import { ListRow, Button, Chip } from 'dsh-plugin-core/ui'
|
|
57
|
+
|
|
58
|
+
// 一行一条:常态带底,点整行打开 / 选中,右侧小操作不连带触发整行
|
|
59
|
+
h(ListRow, {
|
|
60
|
+
onClick: () => openFile(entry),
|
|
61
|
+
selected: entry.path === currentPath,
|
|
62
|
+
actions: h(Button, {
|
|
63
|
+
size: 'sm', variant: 'ghost', onClick: () => removeEntry(entry),
|
|
64
|
+
}, '移除'),
|
|
65
|
+
},
|
|
66
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
67
|
+
h(Chip, null, 'TS'),
|
|
68
|
+
h('span', null, entry.name),
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
// 带边 / 关掉 hover / 纯展示(不可点)
|
|
73
|
+
h(ListRow, { bordered: true, hoverable: false }, '只读的一行')`
|
|
74
|
+
|
|
54
75
|
const panelDockExample = `const dock = createPanelDock({ maxPanels: 3 }) // 一次,挂在插件级
|
|
55
76
|
|
|
56
77
|
// 页面里:结构就在这儿写,不塞进任何调用参数
|
|
@@ -284,7 +305,7 @@ const doc: DocTopic = {
|
|
|
284
305
|
exports: [
|
|
285
306
|
'Button', 'IconButton', 'Segmented', 'SplitGroup', 'Input', 'PrefixedInput', 'NumberInput', 'Textarea',
|
|
286
307
|
'Checkbox', 'fieldMetricsOf', 'Picker', 'WorkspacePicker', 'ModelPicker', 'DateTimePicker', 'Surface', 'Card', 'StripCard',
|
|
287
|
-
'Chip', 'Table', 'Pagination', 'Modal', 'SideSheet', 'FieldHint', 'setTooltipRenderer', 'showToast', 'notify', 'showHostToast',
|
|
308
|
+
'Chip', 'Table', 'ListRow', 'Pagination', 'Modal', 'SideSheet', 'FieldHint', 'setTooltipRenderer', 'showToast', 'notify', 'showHostToast',
|
|
288
309
|
'Loader', 'LoaderBar', 'Loading', 'RunningBlocks', 'SectionHead', 'MarqueeText', 'FlipText', 'LiveText',
|
|
289
310
|
'QuickToolbar', 'startResizeLayoutWidth', 'PanelDock', 'createPanelDock', 'DockPanel',
|
|
290
311
|
'ensureControls', 'setStyleKit',
|
|
@@ -321,6 +342,10 @@ const doc: DocTopic = {
|
|
|
321
342
|
zh: 'IconButton:variant: plain|outline|danger(默认 plain,**无底色**)、size(默认 lg)、icon(必填)、label(必填,无障碍名兼 title)、color(图标色)、background(底色,不传即无底色)。',
|
|
322
343
|
en: 'IconButton: variant plain|outline|danger (default plain, **no background**), size (default lg), icon (required), label (required, doubles as a11y name and title), color (icon colour), background (fill; omitted = no fill).',
|
|
323
344
|
},
|
|
345
|
+
{
|
|
346
|
+
zh: '`dense` 是两者共用的紧凑图标档:`size` / 高度一律不变,图标放大一档、内距收紧(Button 的 `--btn-pad` 从 10/12/14 收到 6,图标容器从 0.875em 到 1.25em;IconButton 把图标 `<svg>` 的 `width/height` 从 1em 提到 1.25em,正方形宽高不变、四周留白变小)。两者 dense 下图标都是 sm 15px / md·lg 16.25px。IconButton 的按钮显式 `padding:0`、图标 `flex:none`:svg 是 flex item,不禁止收缩会被内距压成窄条,此时改 `width` 或加 `!important` 都无效。给描边细、缩到 sm 看不清的图标用(如文件类型 / 语言图标);默认 false,原有按钮外观不变。',
|
|
347
|
+
en: '`dense` is a shared compact-icon tier for both: `size` and height stay put while the icon grows one step and padding tightens (Button: `--btn-pad` 10/12/14 → 6, icon container 0.875em → 1.25em; IconButton: the icon `<svg>` goes from `width/height:1em` to `1.25em`, same square box, less breathing room). Both give sm 15px / md·lg 16.25px icons. An IconButton sets `padding:0` on itself and `flex:none` on the icon: the svg is a flex item, and without disabling shrinking the padding squeezes it into a narrow strip — changing `width` or adding `!important` cannot fix that. Use it for thin-stroke icons that vanish at `sm` (file-type / language glyphs). Defaults to false, so existing buttons are unchanged.',
|
|
348
|
+
},
|
|
324
349
|
{
|
|
325
350
|
zh: '两者的 loading 语义一致:`onClick` 返回 Promise 就转圈(纯图标钮把图标位换成小圈,`aria-label` 不变、补 `aria-busy`),`onBusyChange` 把「在跑」报出去;受控 `loading` 与它取或 —— 别处刷新也能点亮同一颗按钮。转圈期间按钮自己的图标位(`icon` / `iconRight`)全部让位,只留转圈 + 文字。',
|
|
326
351
|
en: 'Both share one loading semantics: a Promise-returning `onClick` spins (an icon button swaps its icon for the spinner, keeps `aria-label` and adds `aria-busy`), `onBusyChange` reports "running", and the controlled `loading` prop is OR-ed in — an external refresh lights the same button too. While spinning, the button’s own icon slots (`icon` / `iconRight`) all step aside: spinner plus label only.',
|
|
@@ -340,6 +365,7 @@ const doc: DocTopic = {
|
|
|
340
365
|
rows: [
|
|
341
366
|
{ name: 'variant', type: "上表 18 档(primary/secondary/danger/success/warning/info/outline/danger-outline/ghost/link + 扩展 purple/pink/cyan/teal/orange/indigo/rose/slate)", def: "'outline'", desc: { zh: '外观档', en: 'Appearance tier' } },
|
|
342
367
|
{ name: 'size', type: "'sm' | 'md' | 'lg'", def: "'lg'", desc: { zh: '高度档(24 / 28 / 32,有边无边同高)', en: 'Height tier (24 / 28 / 32, same height with or without border)' } },
|
|
368
|
+
{ name: 'dense', type: 'boolean', def: 'false', desc: { zh: '紧凑图标档:高度不变,图标放大、内距收紧(Button / IconButton 同名同义)', en: 'Compact-icon tier: same height, larger icon and tighter padding (same prop on Button and IconButton)' } },
|
|
343
369
|
{ name: 'icon', type: 'ReactNode', def: '—', desc: { zh: '前置图标(官方图标组件 / 字符串均可)', en: 'Leading icon (official icon component or string)' } },
|
|
344
370
|
{ name: 'iconRight', type: 'ReactNode', def: '—', desc: { zh: '后置图标(展开箭头、外链)', en: 'Trailing icon (expand arrow, external link)' } },
|
|
345
371
|
{ name: 'color', type: 'string', def: '—', desc: { zh: '自定义主色:实心档作底色,线框 / 无底档作主色', en: 'Custom main color: background for solid tiers, main color for outline/bare' } },
|
|
@@ -606,6 +632,10 @@ const doc: DocTopic = {
|
|
|
606
632
|
zh: '头部整体垂直居中,`action` 里裸放一个元素即居中。颜色全走 token,明暗跟随宿主。',
|
|
607
633
|
en: 'The header centers vertically as a whole; drop a bare element into `action` and it centers. Colors come from tokens, so light/dark follows the host.',
|
|
608
634
|
},
|
|
635
|
+
{
|
|
636
|
+
zh: '`hoverable` 开启后**整卡**悬停变色、同时把光标改成 `pointer`,统一吃 `--dshpc-card-hover`(亮 7% 蓝 / 暗 13% 蓝),默认关;`filled` 切底色,默认有底(Card = surface-1,StripCard = accent 浅底),`false` 时底透明但边框仍在。两根轴都只改 `--surface-bg`,插件不用再自己写 `:hover` 规则;透明底 + hover 也照常变色。',
|
|
637
|
+
en: '`hoverable` (default off) changes the **whole card** on hover and switches the cursor to `pointer`, using `--dshpc-card-hover` (7% blue light / 13% blue dark). `filled` (default on) controls the background — Card uses surface-1, StripCard the accent tint; `false` makes it transparent but keeps the border. Both axes only rewrite `--surface-bg`, so plugins no longer need their own `:hover` rules; a transparent card still changes color on hover.',
|
|
638
|
+
},
|
|
609
639
|
],
|
|
610
640
|
api: [
|
|
611
641
|
{
|
|
@@ -618,12 +648,47 @@ const doc: DocTopic = {
|
|
|
618
648
|
{ name: 'action', type: 'ReactNode', def: '—', desc: { zh: '右上角操作区', en: 'Top-right action area' } },
|
|
619
649
|
{ name: 'children', type: 'ReactNode', def: '—', desc: { zh: '主内容,排版随你', en: 'Main content, layout is yours' } },
|
|
620
650
|
{ name: 'panel', type: 'ReactNode', def: '—', desc: { zh: '展开后才显示的额外内容', en: 'Extra content shown only when expanded' } },
|
|
651
|
+
{ name: 'hoverable', type: 'boolean', def: "'false'", desc: { zh: '整卡 hover 变色 + 光标变 pointer,统一 --dshpc-card-hover;默认关', en: 'Whole-card hover plus a pointer cursor, using --dshpc-card-hover; default off' } },
|
|
652
|
+
{ name: 'filled', type: 'boolean', def: "'true'", desc: { zh: '是否带底色;false 时底透明、边框仍在。默认有底(Card = surface-1 / StripCard = accent 浅底)', en: 'Background fill; false makes it transparent while keeping the border. Default on (Card = surface-1 / StripCard = accent tint)' } },
|
|
621
653
|
{ name: 'className / style', type: '—', def: '—', desc: { zh: '追加类 / 样式', en: 'Append class / style' } },
|
|
622
654
|
],
|
|
623
655
|
},
|
|
624
656
|
],
|
|
625
657
|
code: [{ id: 'ui-card-example', lang: 'tsx', src: cardExample }],
|
|
626
658
|
},
|
|
659
|
+
{
|
|
660
|
+
id: 'list-row',
|
|
661
|
+
heading: { zh: 'ListRow(一行一条)', en: 'ListRow (one item per line)' },
|
|
662
|
+
prose: [
|
|
663
|
+
{
|
|
664
|
+
zh: '一行代表一个可选 / 可点的对象(文件、任务、候选项):常态就带底色,鼠标移上去按 Button 灰色档加深一档;选中换成 slate 的极浅蓝灰单色背景,不描边、不画竖条。左主内容、右侧一组小操作,点右侧不连带触发整行。底 / 边 / 圆角复用 Surface,颜色全走 token,插件不写第二套皮肤。',
|
|
665
|
+
en: 'One line per selectable / clickable object (file, task, candidate): a visible background at rest that darkens one step on hover using the same grey tokens as Button; selected rows swap to a single pale slate-blue-grey background — no border, no rail. Main content on the left, a group of small actions on the right that never trigger the row. Background / border / radius come from Surface and colors from tokens, so plugins write no second skin.',
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
zh: '默认中号(min-height 28,与 Button md 同档),不设 size 轴;四边内距 8px 等距;默认字号 13px / 行高 20px 由核心给(`--dshpc-font-md` / `--dshpc-line-md`,与 Table 同档),调用方不必自己设、可用内联 style 覆盖。`bordered` 切带边 / 不带边,无边档仍是 1px 透明占位,两档同高。`selected` 只改外观,状态由调用方管。',
|
|
669
|
+
en: 'One medium tier by default (min-height 28, same as Button md); no size axis; equal 8px padding on all four sides. The core sets a default 13px / 20px type scale (`--dshpc-font-md` / `--dshpc-line-md`, same as Table) — callers need not set it and can override it inline. `bordered` switches the edge on/off — the borderless tier keeps a transparent 1px placeholder so heights stay equal. `selected` only changes appearance; the caller owns the state.',
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
zh: '`hoverable` 默认跟随「整行可点」(可点行开、纯展示行关,与 Table 的 hover 口径一致),可显式开关;hover / 选中色取 `--dshpc-active` / `--dshpc-listrow-selected-bg`(由 `--dshpc-slate` 派生,亮色 14% / 暗色 26% 两档,暗色不发黑)。整行不是 `<button>`:调用方要往里放 Button / IconButton,嵌 button 是非法 HTML;键盘走 Enter / Space,右侧操作槽挡住冒泡。',
|
|
673
|
+
en: '`hoverable` follows row clickability by default (on for clickable rows, off for static ones, matching Table), and can be forced on/off; hover / selected use `--dshpc-active` / `--dshpc-listrow-selected-bg` (derived from `--dshpc-slate`, 14% light / 26% dark, so the dark theme never goes black). The row is not a `<button>`: callers put Buttons inside it and nested buttons are invalid HTML. Keyboard uses Enter / Space; the actions slot stops propagation.',
|
|
674
|
+
},
|
|
675
|
+
],
|
|
676
|
+
api: [
|
|
677
|
+
{
|
|
678
|
+
id: 'ui-list-row',
|
|
679
|
+
rows: [
|
|
680
|
+
{ name: 'children', type: 'ReactNode', def: '—', desc: { zh: '主内容:图标 / 名字 / 标签 / 左右分区自己排', en: 'Main content: icons, labels, tags, left/right layout are yours' } },
|
|
681
|
+
{ name: 'actions', type: 'ReactNode', def: '—', desc: { zh: '右侧操作槽;核心挡住事件冒泡,不连带整行', en: 'Right-side actions; the core stops propagation so the row is not triggered' } },
|
|
682
|
+
{ name: 'onClick', type: '() => void', def: '—', desc: { zh: '给了才让整行可点(role=button / tabIndex / Enter·Space)', en: 'Only makes the row clickable when provided (role=button / tabIndex / Enter·Space)' } },
|
|
683
|
+
{ name: 'selected', type: 'boolean', def: "'false'", desc: { zh: '选中态:slate 极浅蓝灰单色背景(明暗各一档),不描边 / 不画竖条,压在 hover 之上', en: 'Selected: a single pale slate-blue-grey background (separate light / dark tiers), no border or rail, above hover' } },
|
|
684
|
+
{ name: 'bordered', type: 'boolean', def: "'false'", desc: { zh: '带边 / 不带边,两档同高', en: 'Bordered / borderless; both tiers share the same height' } },
|
|
685
|
+
{ name: 'hoverable', type: 'boolean', def: '自动', desc: { zh: 'hover 灰态加深开关;默认跟随整行可点', en: 'Grey-state hover switch; defaults to row clickability' } },
|
|
686
|
+
{ name: 'className / style', type: '—', def: '—', desc: { zh: '追加类 / 样式,只允许补布局', en: 'Append class / style, layout patches only' } },
|
|
687
|
+
],
|
|
688
|
+
},
|
|
689
|
+
],
|
|
690
|
+
code: [{ id: 'ui-listrow-example', lang: 'tsx', src: listRowExample }],
|
|
691
|
+
},
|
|
627
692
|
{
|
|
628
693
|
id: 'chip',
|
|
629
694
|
heading: { zh: 'Chip', en: 'Chip' },
|
package/docs/help/ui.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
> EN: ui — base controls
|
|
6
6
|
|
|
7
|
-
导出:Button、IconButton、Segmented、SplitGroup、Input、PrefixedInput、NumberInput、Textarea、Checkbox、fieldMetricsOf、Picker、WorkspacePicker、ModelPicker、DateTimePicker、Surface、Card、StripCard、Chip、Table、Pagination、Modal、SideSheet、FieldHint、setTooltipRenderer、showToast、notify、showHostToast、Loader、LoaderBar、Loading、RunningBlocks、SectionHead、MarqueeText、FlipText、LiveText、QuickToolbar、startResizeLayoutWidth、PanelDock、createPanelDock、DockPanel、ensureControls、setStyleKit
|
|
7
|
+
导出:Button、IconButton、Segmented、SplitGroup、Input、PrefixedInput、NumberInput、Textarea、Checkbox、fieldMetricsOf、Picker、WorkspacePicker、ModelPicker、DateTimePicker、Surface、Card、StripCard、Chip、Table、ListRow、Pagination、Modal、SideSheet、FieldHint、setTooltipRenderer、showToast、notify、showHostToast、Loader、LoaderBar、Loading、RunningBlocks、SectionHead、MarqueeText、FlipText、LiveText、QuickToolbar、startResizeLayoutWidth、PanelDock、createPanelDock、DockPanel、ensureControls、setStyleKit
|
|
8
8
|
|
|
9
9
|
## 概览
|
|
10
10
|
|
|
@@ -43,6 +43,10 @@ IconButton:variant: plain|outline|danger(默认 plain,**无底色**)、s
|
|
|
43
43
|
|
|
44
44
|
> EN: IconButton: variant plain|outline|danger (default plain, **no background**), size (default lg), icon (required), label (required, doubles as a11y name and title), color (icon colour), background (fill; omitted = no fill).
|
|
45
45
|
|
|
46
|
+
`dense` 是两者共用的紧凑图标档:`size` / 高度一律不变,图标放大一档、内距收紧(Button 的 `--btn-pad` 从 10/12/14 收到 6,图标容器从 0.875em 到 1.25em;IconButton 把图标 `<svg>` 的 `width/height` 从 1em 提到 1.25em,正方形宽高不变、四周留白变小)。两者 dense 下图标都是 sm 15px / md·lg 16.25px。IconButton 的按钮显式 `padding:0`、图标 `flex:none`:svg 是 flex item,不禁止收缩会被内距压成窄条,此时改 `width` 或加 `!important` 都无效。给描边细、缩到 sm 看不清的图标用(如文件类型 / 语言图标);默认 false,原有按钮外观不变。
|
|
47
|
+
|
|
48
|
+
> EN: `dense` is a shared compact-icon tier for both: `size` and height stay put while the icon grows one step and padding tightens (Button: `--btn-pad` 10/12/14 → 6, icon container 0.875em → 1.25em; IconButton: the icon `<svg>` goes from `width/height:1em` to `1.25em`, same square box, less breathing room). Both give sm 15px / md·lg 16.25px icons. An IconButton sets `padding:0` on itself and `flex:none` on the icon: the svg is a flex item, and without disabling shrinking the padding squeezes it into a narrow strip — changing `width` or adding `!important` cannot fix that. Use it for thin-stroke icons that vanish at `sm` (file-type / language glyphs). Defaults to false, so existing buttons are unchanged.
|
|
49
|
+
|
|
46
50
|
两者的 loading 语义一致:`onClick` 返回 Promise 就转圈(纯图标钮把图标位换成小圈,`aria-label` 不变、补 `aria-busy`),`onBusyChange` 把「在跑」报出去;受控 `loading` 与它取或 —— 别处刷新也能点亮同一颗按钮。转圈期间按钮自己的图标位(`icon` / `iconRight`)全部让位,只留转圈 + 文字。
|
|
47
51
|
|
|
48
52
|
> EN: Both share one loading semantics: a Promise-returning `onClick` spins (an icon button swaps its icon for the spinner, keeps `aria-label` and adds `aria-busy`), `onBusyChange` reports "running", and the controlled `loading` prop is OR-ed in — an external refresh lights the same button too. While spinning, the button’s own icon slots (`icon` / `iconRight`) all step aside: spinner plus label only.
|
|
@@ -59,6 +63,7 @@ Segmented:`items`(必填)支持单选 / 多选,`size`(默认 lg;三
|
|
|
59
63
|
|---|---|---|---|---|
|
|
60
64
|
| `variant` | `上表 18 档(primary/secondary/danger/success/warning/info/outline/danger-outline/ghost/link + 扩展 purple/pink/cyan/teal/orange/indigo/rose/slate)` | 'outline' | 外观档 | Appearance tier |
|
|
61
65
|
| `size` | `'sm' \| 'md' \| 'lg'` | 'lg' | 高度档(24 / 28 / 32,有边无边同高) | Height tier (24 / 28 / 32, same height with or without border) |
|
|
66
|
+
| `dense` | `boolean` | false | 紧凑图标档:高度不变,图标放大、内距收紧(Button / IconButton 同名同义) | Compact-icon tier: same height, larger icon and tighter padding (same prop on Button and IconButton) |
|
|
62
67
|
| `icon` | `ReactNode` | — | 前置图标(官方图标组件 / 字符串均可) | Leading icon (official icon component or string) |
|
|
63
68
|
| `iconRight` | `ReactNode` | — | 后置图标(展开箭头、外链) | Trailing icon (expand arrow, external link) |
|
|
64
69
|
| `color` | `string` | — | 自定义主色:实心档作底色,线框 / 无底档作主色 | Custom main color: background for solid tiers, main color for outline/bare |
|
|
@@ -360,6 +365,10 @@ h(Surface, { tone: 'sunken', border: 'dashed', padding: 'lg' }, '还没做的东
|
|
|
360
365
|
|
|
361
366
|
> EN: The header centers vertically as a whole; drop a bare element into `action` and it centers. Colors come from tokens, so light/dark follows the host.
|
|
362
367
|
|
|
368
|
+
`hoverable` 开启后**整卡**悬停变色、同时把光标改成 `pointer`,统一吃 `--dshpc-card-hover`(亮 7% 蓝 / 暗 13% 蓝),默认关;`filled` 切底色,默认有底(Card = surface-1,StripCard = accent 浅底),`false` 时底透明但边框仍在。两根轴都只改 `--surface-bg`,插件不用再自己写 `:hover` 规则;透明底 + hover 也照常变色。
|
|
369
|
+
|
|
370
|
+
> EN: `hoverable` (default off) changes the **whole card** on hover and switches the cursor to `pointer`, using `--dshpc-card-hover` (7% blue light / 13% blue dark). `filled` (default on) controls the background — Card uses surface-1, StripCard the accent tint; `false` makes it transparent but keeps the border. Both axes only rewrite `--surface-bg`, so plugins no longer need their own `:hover` rules; a transparent card still changes color on hover.
|
|
371
|
+
|
|
363
372
|
| 参数 | 类型 | 默认 | 说明(zh) | 说明(en) |
|
|
364
373
|
|---|---|---|---|---|
|
|
365
374
|
| `accent` | `13 档 or 任意 CSS 色值(business/success/warning/danger/neutral + 扩展 purple/pink/cyan/teal/orange/indigo/rose/slate)` | 'business' | 档位映射到 design 的 token;也可传 '#f59e0b' 这类自定义值 | Tiers map to design tokens; also accepts custom values like '#f59e0b' |
|
|
@@ -369,6 +378,8 @@ h(Surface, { tone: 'sunken', border: 'dashed', padding: 'lg' }, '还没做的东
|
|
|
369
378
|
| `action` | `ReactNode` | — | 右上角操作区 | Top-right action area |
|
|
370
379
|
| `children` | `ReactNode` | — | 主内容,排版随你 | Main content, layout is yours |
|
|
371
380
|
| `panel` | `ReactNode` | — | 展开后才显示的额外内容 | Extra content shown only when expanded |
|
|
381
|
+
| `hoverable` | `boolean` | 'false' | 整卡 hover 变色 + 光标变 pointer,统一 --dshpc-card-hover;默认关 | Whole-card hover plus a pointer cursor, using --dshpc-card-hover; default off |
|
|
382
|
+
| `filled` | `boolean` | 'true' | 是否带底色;false 时底透明、边框仍在。默认有底(Card = surface-1 / StripCard = accent 浅底) | Background fill; false makes it transparent while keeping the border. Default on (Card = surface-1 / StripCard = accent tint) |
|
|
372
383
|
| `className / style` | `—` | — | 追加类 / 样式 | Append class / style |
|
|
373
384
|
|
|
374
385
|
```tsx id="ui-card-example"
|
|
@@ -377,6 +388,7 @@ import { Card, Chip, StripCard } from 'dsh-plugin-core/ui'
|
|
|
377
388
|
h(Card, {
|
|
378
389
|
accent: 'warning',
|
|
379
390
|
bar: { side: 'left', inset: 12, gap: 12 }, // 色条可放 left / right / top / bottom
|
|
391
|
+
hoverable: true, // 整卡 hover 变色(默认关)
|
|
380
392
|
action: h(Button, { size: 'sm', variant: 'ghost' }, '跳过'),
|
|
381
393
|
},
|
|
382
394
|
h('div', null, '标题'),
|
|
@@ -385,11 +397,59 @@ h(Card, {
|
|
|
385
397
|
|
|
386
398
|
h(StripCard, {
|
|
387
399
|
accent: 'success',
|
|
400
|
+
hoverable: true, filled: false, // 底色关掉 = 透明(默认有底)
|
|
388
401
|
expanded: open, onToggle: () => setOpen(!open),
|
|
389
402
|
panel: h('div', null, '展开详情'),
|
|
390
403
|
}, h('div', null, '标题 + 元信息'))
|
|
391
404
|
```
|
|
392
405
|
|
|
406
|
+
## ListRow(一行一条)
|
|
407
|
+
|
|
408
|
+
> EN: ListRow (one item per line)
|
|
409
|
+
|
|
410
|
+
一行代表一个可选 / 可点的对象(文件、任务、候选项):常态就带底色,鼠标移上去按 Button 灰色档加深一档;选中换成 slate 的极浅蓝灰单色背景,不描边、不画竖条。左主内容、右侧一组小操作,点右侧不连带触发整行。底 / 边 / 圆角复用 Surface,颜色全走 token,插件不写第二套皮肤。
|
|
411
|
+
|
|
412
|
+
> EN: One line per selectable / clickable object (file, task, candidate): a visible background at rest that darkens one step on hover using the same grey tokens as Button; selected rows swap to a single pale slate-blue-grey background — no border, no rail. Main content on the left, a group of small actions on the right that never trigger the row. Background / border / radius come from Surface and colors from tokens, so plugins write no second skin.
|
|
413
|
+
|
|
414
|
+
默认中号(min-height 28,与 Button md 同档),不设 size 轴;四边内距 8px 等距;默认字号 13px / 行高 20px 由核心给(`--dshpc-font-md` / `--dshpc-line-md`,与 Table 同档),调用方不必自己设、可用内联 style 覆盖。`bordered` 切带边 / 不带边,无边档仍是 1px 透明占位,两档同高。`selected` 只改外观,状态由调用方管。
|
|
415
|
+
|
|
416
|
+
> EN: One medium tier by default (min-height 28, same as Button md); no size axis; equal 8px padding on all four sides. The core sets a default 13px / 20px type scale (`--dshpc-font-md` / `--dshpc-line-md`, same as Table) — callers need not set it and can override it inline. `bordered` switches the edge on/off — the borderless tier keeps a transparent 1px placeholder so heights stay equal. `selected` only changes appearance; the caller owns the state.
|
|
417
|
+
|
|
418
|
+
`hoverable` 默认跟随「整行可点」(可点行开、纯展示行关,与 Table 的 hover 口径一致),可显式开关;hover / 选中色取 `--dshpc-active` / `--dshpc-listrow-selected-bg`(由 `--dshpc-slate` 派生,亮色 14% / 暗色 26% 两档,暗色不发黑)。整行不是 `<button>`:调用方要往里放 Button / IconButton,嵌 button 是非法 HTML;键盘走 Enter / Space,右侧操作槽挡住冒泡。
|
|
419
|
+
|
|
420
|
+
> EN: `hoverable` follows row clickability by default (on for clickable rows, off for static ones, matching Table), and can be forced on/off; hover / selected use `--dshpc-active` / `--dshpc-listrow-selected-bg` (derived from `--dshpc-slate`, 14% light / 26% dark, so the dark theme never goes black). The row is not a `<button>`: callers put Buttons inside it and nested buttons are invalid HTML. Keyboard uses Enter / Space; the actions slot stops propagation.
|
|
421
|
+
|
|
422
|
+
| 参数 | 类型 | 默认 | 说明(zh) | 说明(en) |
|
|
423
|
+
|---|---|---|---|---|
|
|
424
|
+
| `children` | `ReactNode` | — | 主内容:图标 / 名字 / 标签 / 左右分区自己排 | Main content: icons, labels, tags, left/right layout are yours |
|
|
425
|
+
| `actions` | `ReactNode` | — | 右侧操作槽;核心挡住事件冒泡,不连带整行 | Right-side actions; the core stops propagation so the row is not triggered |
|
|
426
|
+
| `onClick` | `() => void` | — | 给了才让整行可点(role=button / tabIndex / Enter·Space) | Only makes the row clickable when provided (role=button / tabIndex / Enter·Space) |
|
|
427
|
+
| `selected` | `boolean` | 'false' | 选中态:slate 极浅蓝灰单色背景(明暗各一档),不描边 / 不画竖条,压在 hover 之上 | Selected: a single pale slate-blue-grey background (separate light / dark tiers), no border or rail, above hover |
|
|
428
|
+
| `bordered` | `boolean` | 'false' | 带边 / 不带边,两档同高 | Bordered / borderless; both tiers share the same height |
|
|
429
|
+
| `hoverable` | `boolean` | 自动 | hover 灰态加深开关;默认跟随整行可点 | Grey-state hover switch; defaults to row clickability |
|
|
430
|
+
| `className / style` | `—` | — | 追加类 / 样式,只允许补布局 | Append class / style, layout patches only |
|
|
431
|
+
|
|
432
|
+
```tsx id="ui-listrow-example"
|
|
433
|
+
import { ListRow, Button, Chip } from 'dsh-plugin-core/ui'
|
|
434
|
+
|
|
435
|
+
// 一行一条:常态带底,点整行打开 / 选中,右侧小操作不连带触发整行
|
|
436
|
+
h(ListRow, {
|
|
437
|
+
onClick: () => openFile(entry),
|
|
438
|
+
selected: entry.path === currentPath,
|
|
439
|
+
actions: h(Button, {
|
|
440
|
+
size: 'sm', variant: 'ghost', onClick: () => removeEntry(entry),
|
|
441
|
+
}, '移除'),
|
|
442
|
+
},
|
|
443
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
444
|
+
h(Chip, null, 'TS'),
|
|
445
|
+
h('span', null, entry.name),
|
|
446
|
+
),
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
// 带边 / 关掉 hover / 纯展示(不可点)
|
|
450
|
+
h(ListRow, { bordered: true, hoverable: false }, '只读的一行')
|
|
451
|
+
```
|
|
452
|
+
|
|
393
453
|
## Chip
|
|
394
454
|
|
|
395
455
|
> EN: Chip
|
package/package.json
CHANGED