@greypan/web-ui 10.1.2 → 10.2.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.
- package/README.CN.md +41 -1547
- package/README.md +41 -1554
- package/dist/assets/overlay-motion.js +1 -1
- package/dist/assets/overlay-motion.js.map +1 -1
- package/dist/components/context-menu/index.js +3 -3
- package/dist/components/context-menu/index.js.map +1 -1
- package/dist/components/image-preview/style.js +1 -1
- package/dist/components/image-preview/style.js.map +1 -1
- package/dist/components/theme/style.js +1 -1
- package/dist/components/theme/style.js.map +1 -1
- package/dist/components/toast/style.js +1 -1
- package/dist/components/toast/style.js.map +1 -1
- package/package.json +1 -1
package/README.CN.md
CHANGED
|
@@ -303,1552 +303,46 @@ dropdown、tooltip)不需要它。
|
|
|
303
303
|
|
|
304
304
|
## 所有组件
|
|
305
305
|
|
|
306
|
+
每个组件都有自己的文档页,API 参考、行为说明和可交互示例都在 [`docs/`](docs/) 下。下面的表只是索引。
|
|
307
|
+
|
|
306
308
|
| 分类 | 组件 |
|
|
307
309
|
| --------------------- | --------------------------------------------------------- |
|
|
308
|
-
| **表单控件** | [`<web-ui-input>`](
|
|
309
|
-
| | [`<web-ui-textarea>`](
|
|
310
|
-
| | [`<web-ui-editable-text>`](
|
|
311
|
-
| | [`<web-ui-input-number>`](
|
|
312
|
-
| | [`<web-ui-select>`](
|
|
313
|
-
| | [`<web-ui-autocomplete>`](
|
|
314
|
-
| | [`<web-ui-slider>`](
|
|
315
|
-
| | [`<web-ui-checkbox>`](
|
|
316
|
-
| | [`<web-ui-radio>`](
|
|
317
|
-
| | [`<web-ui-switch>`](
|
|
318
|
-
| | [`<web-ui-segmented>`](
|
|
319
|
-
| | [`<web-ui-checkbox-group>`](
|
|
320
|
-
| | [`<web-ui-radio-group>`](
|
|
321
|
-
| **按钮** | [`<web-ui-button>`](
|
|
322
|
-
| | [`<web-ui-button-group>`](
|
|
323
|
-
| **浮层 / 模态** | [`<web-ui-dialog>`](
|
|
324
|
-
| | [`<web-ui-drawer>`](
|
|
325
|
-
| | [`imagePreview()`](
|
|
326
|
-
| **文档流 Disclosure** | [`<web-ui-collapse>`](
|
|
327
|
-
| **浮动** | [`<web-ui-popover>`](
|
|
328
|
-
| | [`<web-ui-tooltip>`](
|
|
329
|
-
| | [`<web-ui-context-menu>`](
|
|
330
|
-
| **菜单** | [`<web-ui-dropdown>`](
|
|
331
|
-
| | [`<web-ui-dropdown-item>`](
|
|
332
|
-
| | [`<web-ui-dropdown-divider>`](
|
|
333
|
-
| | [`<web-ui-dropdown-header>`](
|
|
334
|
-
| **数据展示** | [`<web-ui-avatar>`](
|
|
335
|
-
| | [`<web-ui-badge>`](
|
|
336
|
-
| | [`<web-ui-empty>`](
|
|
337
|
-
| | [`<web-ui-middle-ellipsis>`](
|
|
338
|
-
| | [`<web-ui-icon>`](
|
|
339
|
-
| | [`<web-ui-spinner>`](
|
|
340
|
-
| **布局与工具** | [`<web-ui-layout>`](
|
|
341
|
-
| | [`<web-ui-back-top>`](
|
|
342
|
-
| | [`<web-ui-svg-draw-lines>`](
|
|
343
|
-
| | [`<web-ui-theme>`](
|
|
344
|
-
| **通知** | [`<web-ui-toast>`](
|
|
345
|
-
| **子项** | [`<web-ui-option>`](
|
|
346
|
-
| | [`<web-ui-segmented-trigger>`](
|
|
347
|
-
|
|
348
|
-
## API 参考
|
|
349
|
-
|
|
350
|
-
### 表单控件
|
|
351
|
-
|
|
352
|
-
表单控件声明 `static formAssociated = true`,集成原生 `<form>`:通过 `FormData` 提交值,实现 `formResetCallback()` / `formDisabledCallback()`。
|
|
353
|
-
|
|
354
|
-
#### `<web-ui-input>`
|
|
355
|
-
|
|
356
|
-
文本输入框,支持清除按钮和前后缀插槽。
|
|
357
|
-
|
|
358
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
359
|
-
| ------------- | --------- | -------- | --------------------------------------------------------------------- |
|
|
360
|
-
| `value` | `string` | `''` | 输入值 |
|
|
361
|
-
| `type` | `string` | `'text'` | HTML input 类型 |
|
|
362
|
-
| `placeholder` | `string` | `''` | 占位文本 |
|
|
363
|
-
| `name` | `string` | `''` | 表单字段名 |
|
|
364
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
365
|
-
| `readonly` | `boolean` | `false` | 只读状态 |
|
|
366
|
-
| `required` | `boolean` | `false` | 必填校验 |
|
|
367
|
-
| `clearable` | `boolean` | `false` | 显示清除按钮 |
|
|
368
|
-
| `full` | `boolean` | `false` | 全宽 |
|
|
369
|
-
| `borderless` | `boolean` | `false` | ghost 形态:移除边框、背景与阴影;保留 padding、高度度量与 focus ring |
|
|
370
|
-
| `aria-label` | `string` | — | 无障碍标签 |
|
|
371
|
-
|
|
372
|
-
**事件:** `input`, `change`, `focus`, `blur`
|
|
373
|
-
|
|
374
|
-
**方法:** `focus()`, `blur()`, `select()`,委托到 shadow 内的原生 input;宿主自身不可聚焦。`disabled` 时 `focus()` 与 `select()` 不产生效果,`readonly` 仍允许聚焦和选中。
|
|
375
|
-
|
|
376
|
-
**插槽:** `prefix`, `default`, `suffix`
|
|
377
|
-
|
|
378
|
-
**CSS 自定义属性:**
|
|
379
|
-
|
|
380
|
-
| 属性 | 默认值 | 说明 |
|
|
381
|
-
| ------------------------- | -------------------------------- | ------------ |
|
|
382
|
-
| `--wui-input-clear-color` | `var(--wui-color-text-tertiary)` | 清除按钮颜色 |
|
|
383
|
-
|
|
384
|
-
#### `<web-ui-textarea>`
|
|
385
|
-
|
|
386
|
-
多行文本输入框,支持自动调整高度。
|
|
387
|
-
|
|
388
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
389
|
-
| ----------------- | --------- | ------- | ----------------------------------------------------------------------- |
|
|
390
|
-
| `value` | `string` | `''` | 输入值 |
|
|
391
|
-
| `placeholder` | `string` | `''` | 占位文本 |
|
|
392
|
-
| `rows` | `number` | `3` | 显示行数 |
|
|
393
|
-
| `name` | `string` | `''` | 表单字段名 |
|
|
394
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
395
|
-
| `readonly` | `boolean` | `false` | 只读状态 |
|
|
396
|
-
| `required` | `boolean` | `false` | 必填校验 |
|
|
397
|
-
| `clearable` | `boolean` | `false` | 显示清除按钮 |
|
|
398
|
-
| `full` | `boolean` | `false` | 全宽 |
|
|
399
|
-
| `borderless` | `boolean` | `false` | ghost 形态:移除边框、背景与阴影;保留 padding、高度度量与 focus ring |
|
|
400
|
-
| `autosize` | `boolean` | `false` | 自动调整高度;同时强制 `resize: none`,原生拖拽柄会与脚本接管的高度冲突 |
|
|
401
|
-
| `max-height` | `number` | `0` | 自动高度上限(px),`0` 表示不限制 |
|
|
402
|
-
| `minlength` | `number` | — | 最小长度校验 |
|
|
403
|
-
| `maxlength` | `number` | — | 最大长度校验 |
|
|
404
|
-
| `aria-label` | `string` | — | 无障碍标签 |
|
|
405
|
-
| `aria-labelledby` | `string` | — | 无障碍标签引用 |
|
|
406
|
-
|
|
407
|
-
**事件:** `input`, `change`, `focus`, `blur`
|
|
408
|
-
|
|
409
|
-
**方法:** `focus()`, `blur()`, `select()`,委托到 shadow 内的原生 textarea。`disabled` 时 `focus()` 与 `select()` 不产生效果,`readonly` 仍允许聚焦和选中。
|
|
410
|
-
|
|
411
|
-
**插槽:** `prefix`, `suffix`
|
|
412
|
-
|
|
413
|
-
**CSS 自定义属性:**
|
|
414
|
-
|
|
415
|
-
| 属性 | 默认值 | 说明 |
|
|
416
|
-
| ---------------------------- | -------------------------------- | ------------ |
|
|
417
|
-
| `--wui-textarea-width` | `200px` | 文本域宽度 |
|
|
418
|
-
| `--wui-textarea-clear-color` | `var(--wui-color-text-tertiary)` | 清除按钮颜色 |
|
|
419
|
-
|
|
420
|
-
#### `<web-ui-editable-text>`
|
|
421
|
-
|
|
422
|
-
行内纯文本编辑器:点击文字就地编辑,`Enter` 与 `blur` 提交,`Escape` 取消。文本层与编辑层共用一个盒,进入编辑态不会移动任何一个像素。
|
|
423
|
-
|
|
424
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
425
|
-
| ------------------- | --------- | ------- | --------------------------------------------------------------------------------------------------------- |
|
|
426
|
-
| `value` | `string` | `''` | 当前值;首次连接时捕获声明式初值作为 `form.reset()` 默认,连接后修改 attribute 或 property 均不更新该初值 |
|
|
427
|
-
| `placeholder` | `string` | `''` | 值为空时显示的占位文本 |
|
|
428
|
-
| `name` | `string` | `''` | 表单字段名 |
|
|
429
|
-
| `disabled` | `boolean` | `false` | 禁用状态;只影响行为,不做视觉置灰 |
|
|
430
|
-
| `readonly` | `boolean` | `false` | 只读状态;仍可聚焦、全选和复制,但不能编辑或提交 `change` |
|
|
431
|
-
| `commit-on-unmount` | `boolean` | `false` | 卸载时按提交语义结束编辑态;见下文 |
|
|
432
|
-
| `aria-label` | `string` | — | 无障碍标签 |
|
|
433
|
-
|
|
434
|
-
**事件:** `input`(每次输入)、`change`(提交)、`cancel`(取消;与原生 `<dialog>` 的 `cancel` 同名,不冒泡、不组合,只在组件本身派发)。React 没有覆盖 `cancel` 的合成事件,须用 `addEventListener('cancel', ...)` 监听
|
|
435
|
-
|
|
436
|
-
**方法:** `focus()`, `blur()`, `select()`
|
|
437
|
-
|
|
438
|
-
点击时光标落在点击处;键盘聚焦时落在文本末尾。`Enter` 与 `blur` 均提交草稿并恰好派发一次 `change`:`Enter` 不插入换行,并把焦点交还宿主;`blur` 不干预焦点,焦点留在用户移往的位置。只有 `Escape` 取消:值回到进入编辑时的状态,派发 `cancel` 而不派发 `change`,焦点交还宿主,且按键由编辑层消费,外层浮层(抽屉、菜单)不会因同一次按键关闭。`cancel` 不冒泡也不组合,只在组件本身派发:组件被投映在浮层 shadow 内时(如 drawer 标题),它不会触达浮层的原生 `cancel` 关闭管线,监听一律挂在组件本身。值里已有的换行仍按多行渲染,只是不能再靠输入 `Enter` 增加换行。空值继续显示 placeholder;编辑层始终按自身内容撑高,因此空草稿或纯空格草稿在宿主自身塌缩的场合(flex 项 `min-width: 0`、表格单元格)也仍有承接光标的位置。
|
|
439
|
-
|
|
440
|
-
`select()` 进入编辑态并全选内容;已在编辑态时只重新全选。`disabled` 时与 `focus()` 一样不产生效果。`readonly` 时仍可聚焦和全选,但输入被拒绝,退出编辑态也不派发 `change`。
|
|
441
|
-
|
|
442
|
-
**编辑到一半被卸载。** 正在编辑的元素被从 DOM 移除时会发生什么,取决于引擎,这个差异正是本属性存在的理由:
|
|
443
|
-
|
|
444
|
-
- **Chromium** 会在编辑层上派发 `blur`,因此纯粹的 `value` 提交今天就已经发生,默认 `false` 不改变这一点。`blur` 与它触发的 `change` 都在卸载回调跑完**之后**同步派发,也就是说提交落在「消费方针对这次移除已经更新完状态」之后。
|
|
445
|
-
- **WebKit(Safari、iOS)** 完全不派发 `blur`。默认 `false` 下什么都不提交,草稿随元素一起丢弃。
|
|
446
|
-
- **Gecko(Firefox)未验证**:实测所用机器上没有任何 Firefox 能启动,因此这里不做任何断言。把 Gecko 当作未知,而不是默认它与上面两列之一相同;需要靠卸载提交来保住草稿时,请开启本属性。
|
|
447
|
-
|
|
448
|
-
如果你的代码依赖「行被回收时草稿不丢」,请**开启 `commit-on-unmount`**:在 Safari 与 iOS 上它是唯一能保住草稿的手段,在 Chromium 上它把提交提前到移除自带的 `blur` 之前。两个引擎下,卸载都按提交语义收尾一段开着的编辑会话——草稿成为新值、退出编辑、恰好派发一次 `change`,走的是与 `Enter`、`blur` 同一份实现。结论在 Chromium 与 WebKit 上实测过;浏览器测试套件只跑 Chromium,WebKit 的结果来自一次一次性跨引擎取证、不在 CI 里,Gecko 则完全没有测。
|
|
449
|
-
|
|
450
|
-
`Enter` 与 `blur` 由用户动作触发,一律算提交意图;卸载没有这种意图。因此只有草稿与进入编辑时的值确实不同时才提交——什么都没改的会话不会产生空提交。不在编辑态时没有可提交的内容;`disabled` 或 `readonly` 时与 `blur` 路径一致,都不派发提交事件。但两者对「已经有草稿」的处置不同:`disabled` 下草稿留在组件上,`readonly` 下草稿是真的丢掉。`readonly` 阻止不了先前输入的字,而卸载时已没有 blur 可以兜底提交——WebKit 上连 blur 都没有。可达路径是:可编辑态进入 → 起草稿 → 期间翻 readonly → 卸载。这里不按取消处理——取消的契约是恢复进入编辑时的值,而卸载既没有焦点要交也没有宿主接收结果;用户看到的是「我打的字没了」,既没有 `change` 也没有 `cancel`。
|
|
451
|
-
|
|
452
|
-
提交发生在卸载回调里,`change` 仍沿组合路径向上派发,因此挂在组件自身或任一祖先上的监听都会执行,**包括自身也正在被卸载的祖先**:派发不要求祖先此刻还在文档里。因此监听器不能假定组件仍在文档中;结果需要活过这次移除时,用 `queueMicrotask` 之类的方式显式转交给宿主树之外的状态。注意这条可达性对「由 React 自身 render 驱动的卸载」并不成立:那种移除走不到 React 的事件委托根,`onChange` 不会触发,只能改为从元素上读值——与上文 `cancel` 的告示同源。在祖先上设 `display: none` 不算移除——两个引擎下组件都仍在文档中、不派发任何事件,那种场合需要消费方主动结束编辑。
|
|
453
|
-
|
|
454
|
-
宿主是行内级盒子:未设宽度时随内容伸缩,折行后高度按行数增长。字体、颜色、文本对齐与空白处理全部继承外部上下文,因此编辑前它就是一段普通文字。
|
|
455
|
-
|
|
456
|
-
光标颜色默认跟随共享语义 token `--wui-color-accent`;在宿主或任意祖先上设置该 token 即可整体改色。两层对长无空格串(连续字母数字)按同一组断行点折行:超出盒宽的长串在盒内断开,文字态不再溢出宿主盒。
|
|
457
|
-
|
|
458
|
-
**CSS 自定义属性:**
|
|
459
|
-
|
|
460
|
-
| 属性 | 默认值 | 说明 |
|
|
461
|
-
| ----------------------------------- | ---------- | ---------------------------------------------------------------- |
|
|
462
|
-
| `--wui-editable-text-white-space` | `pre-wrap` | 两层的空白处理方式 |
|
|
463
|
-
| `--wui-editable-text-overflow-wrap` | `anywhere` | 两层的断词处理方式;默认让长无空格串在盒内断开,与原生编辑层一致 |
|
|
464
|
-
|
|
465
|
-
共用盒子带来两个约束:`line-height` 需不小于 `1`,更紧凑时原生编辑层内容会高出自身盒子,文字被顶偏 1px;`nowrap` 加固定宽度时,超出盒宽的文案在文字态溢出显示、在编辑态于盒内滚动。
|
|
466
|
-
|
|
467
|
-
**交互变更:** `blur` 恢复提交:`Enter` 与 `blur` 均提交草稿,只有 `Escape` 取消,值回到进入编辑时的状态。`Enter` 仍不插入换行。`cancel` 事件不再冒泡:此前冒泡的自定义 `cancel` 会经 slot 进入外层浮层 shadow 内的 `<dialog>`(`composed` 与否都会),被当成关闭请求一并关掉(issue #159);监听请挂在组件本身。
|
|
468
|
-
|
|
469
|
-
#### `<web-ui-input-number>`
|
|
470
|
-
|
|
471
|
-
数字输入框,支持步进按钮和键盘操作。
|
|
472
|
-
|
|
473
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
474
|
-
| ------------- | --------- | ---------- | ---------- |
|
|
475
|
-
| `value` | `number` | `0` | 当前值 |
|
|
476
|
-
| `min` | `number` | `0` | 最小值 |
|
|
477
|
-
| `max` | `number` | `Infinity` | 最大值 |
|
|
478
|
-
| `step` | `number` | `1` | 步进值 |
|
|
479
|
-
| `precision` | `number` | `0` | 小数精度 |
|
|
480
|
-
| `placeholder` | `string` | `''` | 占位文本 |
|
|
481
|
-
| `name` | `string` | `''` | 表单字段名 |
|
|
482
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
483
|
-
| `readonly` | `boolean` | `false` | 只读状态 |
|
|
484
|
-
| `required` | `boolean` | `false` | 必填校验 |
|
|
485
|
-
|
|
486
|
-
**事件:** `input`, `change`
|
|
487
|
-
|
|
488
|
-
ArrowUp/ArrowDown 键增减数值。空输入或 `-` 在提交时被忽略,值保持在最后一个有效数字。
|
|
489
|
-
|
|
490
|
-
#### `<web-ui-select>`
|
|
491
|
-
|
|
492
|
-
选择器下拉框,支持键盘导航和 Portal。
|
|
493
|
-
|
|
494
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
495
|
-
| ------------------ | ---------------------------------- | ------- | ---------------------------------- |
|
|
496
|
-
| `value` | `string` | `''` | 选中值 |
|
|
497
|
-
| `placeholder` | `string` | `''` | 占位文本 |
|
|
498
|
-
| `name` | `string` | `''` | 表单字段名 |
|
|
499
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
500
|
-
| `required` | `boolean` | `false` | 必填校验 |
|
|
501
|
-
| `portal` | `boolean` | `false` | 在 theme-owned overlay root 中渲染 |
|
|
502
|
-
| `no-scroll-lock` | `boolean` | `false` | 打开时不锁定页面滚动 |
|
|
503
|
-
| `overlayContainer` | `HTMLElement \| () => HTMLElement` | — | 显式 Portal 容器 |
|
|
504
|
-
|
|
505
|
-
**事件:** `input`, `change`, `open-change` (`CustomEvent<{ open: boolean }>`)
|
|
506
|
-
|
|
507
|
-
**插槽:** `default`(投影 `<web-ui-option>` 元素)、`trigger`(自定义触发区域内容,替换默认 label 和箭头)
|
|
508
|
-
|
|
509
|
-
子 `<web-ui-option>` 通过 `option-register` / `option-unregister` 注册。支持 ArrowDown/ArrowUp/Enter/Escape 键盘导航。
|
|
510
|
-
|
|
511
|
-
**CSS 自定义属性:**
|
|
512
|
-
|
|
513
|
-
| 属性 | 默认值 | 说明 |
|
|
514
|
-
| ------------------------- | ---------------------------------- | --------------------- |
|
|
515
|
-
| `--wui-select-max-width` | `500px` | 下拉框最大宽度 |
|
|
516
|
-
| `--wui-select-max-height` | `200px` | 下拉滚动区域最大高度 |
|
|
517
|
-
| `--wui-overlay-min-width` | `200px` | 下拉框最小宽度 |
|
|
518
|
-
| `--wui-select-bg-hover` | `--wui-color-surface-glass-hover` | Trigger 悬停背景 |
|
|
519
|
-
| `--wui-select-bg-active` | `--wui-color-surface-glass-active` | Trigger 按下/打开背景 |
|
|
520
|
-
|
|
521
|
-
Portal 面板创建时会镜像 host 上解析后的这些变量;更新 host 后需重新打开 Portal 面板才会生效。
|
|
522
|
-
|
|
523
|
-
#### `<web-ui-autocomplete>`
|
|
524
|
-
|
|
525
|
-
可输入并过滤候选的单值选择器。
|
|
526
|
-
|
|
527
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
528
|
-
| -------------------- | ---------------------------------- | ------------ | --------------------------------------------------------------------- |
|
|
529
|
-
| `value` | `string` | `''` | 当前输入文本(表单值) |
|
|
530
|
-
| `selected-value` | `string` | `''` | 输入文本精确匹配 label 的非禁用 option 的 value(派生,只读) |
|
|
531
|
-
| `placeholder` | `string` | `''` | 占位文本 |
|
|
532
|
-
| `borderless` | `boolean` | `false` | ghost 形态:移除边框、背景与阴影;保留 padding、高度度量与 focus ring |
|
|
533
|
-
| `filter` | `'none' \| 'prefix' \| 'contains'` | `'contains'` | 候选过滤模式(按 option label 匹配) |
|
|
534
|
-
| `name` | `string` | `''` | 表单字段名 |
|
|
535
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
536
|
-
| `readonly` | `boolean` | `false` | 只读状态(不可输入、不可展开下拉) |
|
|
537
|
-
| `required` | `boolean` | `false` | 必填校验 |
|
|
538
|
-
| `allow-custom-value` | `boolean` | `false` | 允许 Enter 提交不在候选中的 custom value |
|
|
539
|
-
| `portal` | `boolean` | `false` | 在 theme-owned overlay root 中渲染 |
|
|
540
|
-
| `no-scroll-lock` | `boolean` | `false` | 打开时不锁定页面滚动 |
|
|
541
|
-
| `overlayContainer` | `HTMLElement \| () => HTMLElement` | — | 显式 Portal 容器 |
|
|
542
|
-
| `aria-label` | `string` | — | 无障碍名称 |
|
|
543
|
-
| `aria-labelledby` | `string` | — | 无障碍名称引用 |
|
|
544
|
-
|
|
545
|
-
**事件:** `input`, `change`, `focus`, `blur`, `open-change` (`CustomEvent<{ open: boolean }>`)
|
|
546
|
-
|
|
547
|
-
**方法:** `focus()`, `blur()`
|
|
548
|
-
|
|
549
|
-
**插槽:** `default`(投影 `<web-ui-option>` 元素)、`trigger`(自定义触发器内容——替换默认输入框)
|
|
550
|
-
|
|
551
|
-
键入时按 label 过滤候选(`contains` 或 `prefix`,`none` 关闭过滤)。没有匹配项时下拉面板保持关闭;删除字符后重新出现匹配项时,面板会再次打开。选择 option 时文本回填为该项 label,`selected-value` 暴露该项的 value;`change` 在选择提交时触发。支持 ArrowDown/ArrowUp/Enter/Escape 键盘导航。
|
|
552
|
-
|
|
553
|
-
**触发器:** 默认触发器是 shadow 内的 `web-ui-input`。把任意可编辑组件放进 `trigger` slot 即可替换它——包装 div 继续承载 combobox ARIA,并以 `data-custom-trigger` 标记当前使用自定义触发器,浮层始终以触发器元素为锚点。组件的 `focus()` / `blur()` 委托到当前生效的触发器:`web-ui-input` 与 `web-ui-textarea` 会把焦点落到内部原生控件;自定义触发器没有自己的 focus 重定向时,按宿主自身聚焦。
|
|
554
|
-
|
|
555
|
-
```html
|
|
556
|
-
<web-ui-autocomplete placeholder="描述问题">
|
|
557
|
-
<web-ui-textarea slot="trigger" rows="3"></web-ui-textarea>
|
|
558
|
-
<web-ui-option value="bug" label="缺陷"></web-ui-option>
|
|
559
|
-
<web-ui-option value="feature" label="需求"></web-ui-option>
|
|
560
|
-
</web-ui-autocomplete>
|
|
561
|
-
```
|
|
562
|
-
|
|
563
|
-
自定义触发器与默认触发器共用同一份契约:字符串 `value` 承载文本、可聚焦,并派发组件委托监听的事件(`input`、`click`、`focus`、`blur`)。`web-ui-input` 与 `web-ui-textarea` 开箱即用;自定义元素只要暴露字符串 `value` property 即可接入。包装 div 不占 tab 位(`tabindex="-1"`):顺序焦点归触发器自身,自定义触发器必须可聚焦,键盘用户才能到达 combobox。
|
|
564
|
-
|
|
565
|
-
多行触发器(可编辑元素为 `<textarea>`)保留 Enter 换行语义:Enter 不会选中高亮项,也不会提交 custom value,关闭面板用 Escape 或 blur。选择 option 仍会把该项 label 回写到触发器。单行自定义触发器保持默认的 Enter 语义。面板打开时 ArrowUp/ArrowDown 适用同一例外:方向键移动文本光标而不导航候选,该状态下键盘无法导航 option——用指针点击选择。面板关闭时 ArrowDown/ArrowUp 仍可打开面板。
|
|
566
|
-
|
|
567
|
-
启用 `allow-custom-value` 后,无匹配且无活动 option 时,Enter 会把当前输入原文作为 custom value 提交并保持面板关闭;`change` 会触发,`selected-value` 保持为空。组件不会自动创建 option,也不会 trim 原文。命中禁用 option 的文本不会绕过禁用语义,也不会派生为已选 option。
|
|
568
|
-
|
|
569
|
-
**CSS 自定义属性:**
|
|
570
|
-
|
|
571
|
-
| 属性 | 默认值 | 说明 |
|
|
572
|
-
| ------------------------------- | ------- | -------------------- |
|
|
573
|
-
| `--wui-autocomplete-max-width` | `500px` | 下拉框最大宽度 |
|
|
574
|
-
| `--wui-autocomplete-max-height` | `200px` | 下拉滚动区域最大高度 |
|
|
575
|
-
| `--wui-overlay-min-width` | `200px` | 下拉框最小宽度 |
|
|
576
|
-
|
|
577
|
-
Portal 面板创建时会镜像 host 上解析后的这些变量;更新 host 后需重新打开 Portal 面板才会生效。
|
|
578
|
-
|
|
579
|
-
#### `<web-ui-slider>`
|
|
580
|
-
|
|
581
|
-
范围滑块,支持刻度标记和垂直方向。
|
|
582
|
-
|
|
583
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
584
|
-
| ---------- | --------- | ------- | ------------ |
|
|
585
|
-
| `value` | `number` | `0` | 当前值 |
|
|
586
|
-
| `min` | `number` | `0` | 最小值 |
|
|
587
|
-
| `max` | `number` | `100` | 最大值 |
|
|
588
|
-
| `step` | `number` | `1` | 步进值 |
|
|
589
|
-
| `name` | `string` | `''` | 表单字段名 |
|
|
590
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
591
|
-
| `required` | `boolean` | `false` | 必填校验 |
|
|
592
|
-
| `marks` | `boolean` | `false` | 显示刻度标记 |
|
|
593
|
-
| `vertical` | `boolean` | `false` | 垂直方向 |
|
|
594
|
-
|
|
595
|
-
**事件:** `input`(拖拽中)、`change`(松开或键盘确认)
|
|
596
|
-
|
|
597
|
-
**方法:** `focus()`, `blur()`
|
|
598
|
-
|
|
599
|
-
支持 ArrowLeft/Right/Up/Down、Home/End、PageUp/PageDown 键盘导航。使用 pointer capture 处理鼠标、触控笔和触摸交互;组件 host 与触控轨道均声明 `touch-action: none`,拖拽激活期间同时阻止 `touchmove` 默认滚动,避免 iOS Safari 在横/纵拖拽过程中接管手势。
|
|
600
|
-
|
|
601
|
-
**CSS 自定义属性:**
|
|
602
|
-
|
|
603
|
-
| 属性 | 默认值 | 说明 |
|
|
604
|
-
| ------------------------------ | ----------------------------------- | ------------ |
|
|
605
|
-
| `--wui-slider-width` | `200px` | 滑块宽度 |
|
|
606
|
-
| `--wui-slider-vertical-height` | `200px` | 垂直滑块高度 |
|
|
607
|
-
| `--wui-slider-height` | `var(--wui-slider-track-size, 6px)` | 轨道厚度 |
|
|
608
|
-
| `--wui-slider-track-size` | `6px` | 轨道尺寸 |
|
|
609
|
-
| `--wui-slider-thumb-width` | `24px` | 滑块宽度 |
|
|
610
|
-
| `--wui-slider-thumb-height` | `20px` | 滑块高度 |
|
|
611
|
-
| `--wui-slider-marks-inset` | `0` | 刻度内缩 |
|
|
612
|
-
|
|
613
|
-
#### `<web-ui-checkbox>`
|
|
614
|
-
|
|
615
|
-
单个复选框。
|
|
616
|
-
|
|
617
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
618
|
-
| ---------- | --------- | ------- | ---------- |
|
|
619
|
-
| `checked` | `boolean` | `false` | 选中状态 |
|
|
620
|
-
| `value` | `string` | `''` | 表单提交值 |
|
|
621
|
-
| `name` | `string` | `''` | 表单字段名 |
|
|
622
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
623
|
-
| `required` | `boolean` | `false` | 必填校验 |
|
|
624
|
-
|
|
625
|
-
**事件:** `input`, `change`
|
|
626
|
-
|
|
627
|
-
**插槽:** `default`(标签文本)
|
|
628
|
-
|
|
629
|
-
使用 `role="checkbox"` 和 `aria-checked`。Enter/Space 键盘切换。
|
|
630
|
-
|
|
631
|
-
**布局:** 宿主是 inline-flex 盒,高度由内容撑开、不继承页面行高,因此不会在指示器上下留出多余缝隙;`--wui-selection-control-size`(`18px`)决定指示器宽高,宿主与相邻文字的对齐固定为 `vertical-align: middle`。`<web-ui-radio>` 共用同一套契约。指示器与标签之间的 10px 归标签所有:default slot 没画出东西时(空着,或里面只放了一个视觉隐藏的无障碍名,例如 `.sr-only` 的 span)这段间距一并收起,宿主宽度就等于指示器。空不空按标签盒子的渲染宽度判定,而不是按 slot 被指派了节点判定——无障碍名正是「有指派、不画盒」的那一种;也不能用 `display: none` 收掉标签,那会把这个名字从无障碍树里拿掉。
|
|
632
|
-
|
|
633
|
-
**选中动画:** 对勾是 `@/icons` 里的描边资产,由嵌套的 `<web-ui-icon>` 渲染在 `<web-ui-svg-draw-lines>` 内,并带 `no-autoplay`,所以挂载时就已勾选的控件显示静态勾。勾选时线条从左侧尖端起按恒定笔速画出,时长取 `--wui-duration-trigger`(默认 160ms),在切换当下从已生效的主题解析,和指示器底色那条 transition 落在同一拍;取消时沿同一条路径收回到空白,而不是只淡出;主题范围为 `motion="reduced"` 时两者都跳过,两个状态直接切换。描边颜色是 `--wui-color-on-control`,经 `<web-ui-icon>` 的 `--wui-icon-color` 传下去,因为勾选后它背后就是 `--wui-color-accent`。这个结构带来两条约束:资产必须保持描边(`fill: none` + `stroke: currentColor`),换成实心图标 dash 动画照跑却什么都不画,勾会直接出现;并且它按 `<web-ui-icon>` 自己的默认 18px 渲染,宿主够不到图标 shadow root 里的 `<svg>`,因此不随 `--wui-selection-control-size` 放大。
|
|
634
|
-
|
|
635
|
-
**未激活态:** 未选中指示器的底色取 `--wui-color-surface-control`(与中性按钮同一档控件底),深色模式下也能和 `--wui-color-page` 分辨开。触发区内任意位置(指示器、间距或右侧 slot 标签)被 hover 时,该底色再叠 6% 状态层。hover 只在 `(hover: hover) and (pointer: fine)` 设备上生效;没有按下态,已选中和禁用态保持各自底色。`<web-ui-radio>` 共用同一套状态。
|
|
636
|
-
|
|
637
|
-
#### `<web-ui-radio>`
|
|
638
|
-
|
|
639
|
-
单个单选按钮。
|
|
640
|
-
|
|
641
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
642
|
-
| ---------- | --------- | ------- | ---------- |
|
|
643
|
-
| `checked` | `boolean` | `false` | 选中状态 |
|
|
644
|
-
| `value` | `string` | `''` | 表单提交值 |
|
|
645
|
-
| `name` | `string` | `''` | 表单字段名 |
|
|
646
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
647
|
-
| `required` | `boolean` | `false` | 必填校验 |
|
|
648
|
-
|
|
649
|
-
**事件:** `input`, `change`
|
|
650
|
-
|
|
651
|
-
**插槽:** `default`(标签文本)
|
|
652
|
-
|
|
653
|
-
**布局:** 与 `<web-ui-checkbox>` 共用同一套选择控件盒契约,包括「标签没有画出东西时收起间距」这一条——宿主高度由内容撑开、不继承页面行高,指示器宽高走 `--wui-selection-control-size`,与相邻文字按 `vertical-align: middle` 对齐。
|
|
654
|
-
|
|
655
|
-
#### `<web-ui-switch>`
|
|
656
|
-
|
|
657
|
-
开关切换。
|
|
658
|
-
|
|
659
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
660
|
-
| ---------- | --------- | ------- | ------------- |
|
|
661
|
-
| `checked` | `boolean` | `false` | 打开/关闭状态 |
|
|
662
|
-
| `value` | `string` | `''` | 表单提交值 |
|
|
663
|
-
| `name` | `string` | `''` | 表单字段名 |
|
|
664
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
665
|
-
| `required` | `boolean` | `false` | 必填校验 |
|
|
666
|
-
| `loading` | `boolean` | `false` | 加载状态 |
|
|
667
|
-
|
|
668
|
-
**事件:** `input`, `change`
|
|
669
|
-
|
|
670
|
-
使用 `role="switch"` 和 `aria-checked`。Pointer 事件管理按下状态。
|
|
671
|
-
|
|
672
|
-
#### `<web-ui-segmented>`
|
|
673
|
-
|
|
674
|
-
分段控制——单选按钮组。
|
|
675
|
-
|
|
676
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
677
|
-
| ---------- | ------------------- | ------- | ---------------------- |
|
|
678
|
-
| `value` | `string` | `''` | 当前选中值 |
|
|
679
|
-
| `name` | `string` | `''` | 表单字段名 |
|
|
680
|
-
| `disabled` | `boolean` | `false` | 禁用全部 trigger |
|
|
681
|
-
| `required` | `boolean` | `false` | 必填校验 |
|
|
682
|
-
| `variant` | `inset` \| `raised` | `inset` | 轨道与静止指示器的材质 |
|
|
683
|
-
|
|
684
|
-
**事件:** `input`, `change`
|
|
685
|
-
|
|
686
|
-
**插槽:** `default`(投影 `<web-ui-segmented-trigger>` 元素)
|
|
687
|
-
|
|
688
|
-
**变体:** `inset`(默认,「凹」)是不透明实体轨道(`--wui-color-surface`)——1px 描边环与投影全程常驻,静止指示器是 `--wui-color-surface-segmented` 纯灰底,视觉上嵌入轨道。浅色下该 token 与 `--wui-color-page` 同值,轨道与页面齐平,凹感只由常驻环与投影承担;深色下它是「比页面高一档」的 elevation,轨道依旧浮离页面。`raised`(「凸」)是经典形态:flat 灰轨道(`--wui-color-surface-segmented`,无环无投影),静止指示器是 `--wui-color-surface-selected` 实体底加柔投影,浮起于轨道之上。两个变体的按压/拖拽态一致——指示器转透明玻璃(backdrop blur、80ms 淡入的描边环、1.5x 放大),松开淡回各自静止面。非法值回退 `inset`。
|
|
689
|
-
|
|
690
|
-
与原生 `<form>` 集成(通过 `ElementInternals`)。
|
|
691
|
-
|
|
692
|
-
根据 `value` 同步子 trigger 的 `checked` 状态。`disabled` 提供继承的有效禁用状态,不改写 trigger 自身的 `disabled` 属性。直接设 `value` 不派发事件。
|
|
693
|
-
|
|
694
|
-
按住当前选中项横向拖拽可滑动指示器,松手吸附到最近选项(快速抛掷按速度切换)。trigger 宽度不等时,拖拽中的指示器取「离自身中心最近的那一项」的宽度——即预览松手会落到的那一项,而不是保持按下那一项的宽度。触摸面声明 `touch-action: none`,拖拽激活期间阻止 `touchmove` 默认滚动,避免 iOS Safari 中断手势。
|
|
695
|
-
|
|
696
|
-
**按压态:** 轨道在所有状态下都是不透明实体面(`--wui-color-surface`,浅色即 page 同色、无 backdrop filter),1px 描边环与投影全程不变;转为玻璃的是指示器这一层。静止态指示器是 `--wui-color-surface-segmented` 实色底、玻璃输出全部关闭;按压/拖拽态获得 backdrop blur、随 80ms 过渡淡入的玻璃描边环与 1.5x 放大,底色全透明:指示器的 blur 采样面整个落在轨道之内,此前的玻璃轨道上全透明指示器在均匀深底与相邻轨道仅差 +0.5 阶,条纹底实测指示器内部灰度 std 1.89(背景渗透);轨道实体化后同一采样 std 归零,按压态本体的可感知性由实色轨道与放大/环/投影共同承担,不再需要 tint 打底或压制轨道边缘装饰。底色不淡入——按下第一帧即落定;松开时指示器经 `--wui-duration-press` 淡回实色灰底。
|
|
697
|
-
|
|
698
|
-
**文字色:** 选中与未选中项文字一律保持 `--wui-color-text-secondary`——`inset`/`raised` 两个变体一致,静止态与按压/拖拽态一致。选中态只由指示器承担,任何标签都不着 `--wui-color-accent`。
|
|
699
|
-
|
|
700
|
-
#### `<web-ui-checkbox-group>`
|
|
701
|
-
|
|
702
|
-
多选复选框组。
|
|
703
|
-
|
|
704
|
-
##### 属性
|
|
705
|
-
|
|
706
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
707
|
-
| ----------- | ---------------------------- | ------------ | -------------- |
|
|
708
|
-
| `value` | `string[]` | `[]` | 已选项的值数组 |
|
|
709
|
-
| `name` | `string` | `''` | 表单字段名 |
|
|
710
|
-
| `disabled` | `boolean` | `false` | 禁用全部子项 |
|
|
711
|
-
| `required` | `boolean` | `false` | 必填校验 |
|
|
712
|
-
| `direction` | `'horizontal' \| 'vertical'` | `'vertical'` | 布局方向 |
|
|
713
|
-
|
|
714
|
-
**事件:** `input`, `change`
|
|
715
|
-
|
|
716
|
-
**插槽:** `default`(投影 `<web-ui-checkbox>` 元素)
|
|
717
|
-
|
|
718
|
-
同步子 checkbox 的 `checked` 状态。`disabled` 提供继承的有效禁用状态,不改写子项自身的 `disabled` 属性。监听子项 `change` 事件。`direction` 传入非法值时回退为 `vertical`。
|
|
719
|
-
|
|
720
|
-
##### Token
|
|
721
|
-
|
|
722
|
-
| Token | 默认值 | 说明 |
|
|
723
|
-
| -------------------------- | -------------------- | ------------------------ |
|
|
724
|
-
| `--wui-checkbox-group-gap` | `var(--wui-space-2)` | 成员 checkbox 之间的间距 |
|
|
725
|
-
|
|
726
|
-
在 group 元素本身,或同一个 `<web-ui-theme>` 作用域内的祖先元素上设置。theme 宿主会为自己的子树声明该间距阶,写在 `<web-ui-theme>` 元素之外的覆盖到不了 group。它的默认值现在经 `--wui-space-2` 解析,所以在 theme 宿主上覆盖该级会连带改变成员间距;显式设置这个 token 仍然优先于两者。
|
|
727
|
-
|
|
728
|
-
```css
|
|
729
|
-
web-ui-checkbox-group {
|
|
730
|
-
--wui-checkbox-group-gap: 16px;
|
|
731
|
-
}
|
|
732
|
-
```
|
|
733
|
-
|
|
734
|
-
#### `<web-ui-radio-group>`
|
|
735
|
-
|
|
736
|
-
单选组。
|
|
737
|
-
|
|
738
|
-
##### 属性
|
|
739
|
-
|
|
740
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
741
|
-
| ----------- | ---------------------------- | ------------ | ------------------------ |
|
|
742
|
-
| `value` | `string` | `''` | 当前选中值 |
|
|
743
|
-
| `name` | `string` | `''` | 表单字段名(传递到子项) |
|
|
744
|
-
| `disabled` | `boolean` | `false` | 禁用全部子项 |
|
|
745
|
-
| `required` | `boolean` | `false` | 必填校验 |
|
|
746
|
-
| `direction` | `'horizontal' \| 'vertical'` | `'vertical'` | 布局方向 |
|
|
747
|
-
|
|
748
|
-
**事件:** `input`, `change`
|
|
749
|
-
|
|
750
|
-
**插槽:** `default`(投影 `<web-ui-radio>` 元素)
|
|
751
|
-
|
|
752
|
-
`disabled` 提供继承的有效禁用状态,不改写子项自身的 `disabled` 属性。`direction` 传入非法值时回退为 `vertical`。
|
|
753
|
-
|
|
754
|
-
##### Token
|
|
755
|
-
|
|
756
|
-
| Token | 默认值 | 说明 |
|
|
757
|
-
| ----------------------- | -------------------- | --------------------- |
|
|
758
|
-
| `--wui-radio-group-gap` | `var(--wui-space-2)` | 成员 radio 之间的间距 |
|
|
759
|
-
|
|
760
|
-
在 group 元素本身,或同一个 `<web-ui-theme>` 作用域内的祖先元素上设置。theme 宿主会为自己的子树声明该间距阶,写在 `<web-ui-theme>` 元素之外的覆盖到不了 group。它的默认值现在经 `--wui-space-2` 解析,所以在 theme 宿主上覆盖该级会连带改变成员间距;显式设置这个 token 仍然优先于两者。
|
|
761
|
-
|
|
762
|
-
```css
|
|
763
|
-
web-ui-radio-group {
|
|
764
|
-
--wui-radio-group-gap: 16px;
|
|
765
|
-
}
|
|
766
|
-
```
|
|
767
|
-
|
|
768
|
-
---
|
|
769
|
-
|
|
770
|
-
### 按钮
|
|
771
|
-
|
|
772
|
-
#### `<web-ui-button>`
|
|
773
|
-
|
|
774
|
-
样式化按钮,支持多种变体和加载状态。
|
|
775
|
-
|
|
776
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
777
|
-
| ------------ | ------------------------------------------------------------ | ---------- | ------------------------------------------------------------- |
|
|
778
|
-
| `variant` | `'primary' \| 'secondary' \| 'ghost' \| 'danger' \| 'glass'` | `'glass'` | 按钮变体 |
|
|
779
|
-
| `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | 内部按钮类型;非法值回退为 `button` |
|
|
780
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
781
|
-
| `loading` | `boolean` | `false` | 加载旋转动画;与 `icon` 组合时 spinner 替换图标内容 |
|
|
782
|
-
| `full` | `boolean` | `false` | 全宽 |
|
|
783
|
-
| `icon` | `boolean` | `false` | 纯图标模式 |
|
|
784
|
-
| `size` | `string` | `''` | 按钮高度(px);icon 模式下同时设为 min-width,默认保持正方形 |
|
|
785
|
-
| `aria-label` | `string` | — | 无障碍标签(委托给内部按钮) |
|
|
786
|
-
|
|
787
|
-
**事件:** 标准 `click`
|
|
788
|
-
|
|
789
|
-
**插槽:** `prefix`, `default`, `suffix`
|
|
790
|
-
|
|
791
|
-
`submit` 和 `reset` 在 composed `click` 事件完成派发后,通过宿主的 form owner 转发到其原生 `<form>`;对这次 click 调用 `preventDefault()` 会取消该动作。内部按钮自身仍没有 form owner,因此 `SubmitEvent.submitter` 为 `null`。按钮不向 `FormData` 贡献值。
|
|
792
|
-
|
|
793
|
-
禁用、加载和 `formDisabled` 状态(包括祖先 `fieldset` 导致的禁用)都会阻止 `click` 事件。
|
|
794
|
-
|
|
795
|
-
`icon` 与 `loading` 同时开启时,按钮只渲染 spinner,默认插槽图标不投影。默认 icon 几何保持正方形,`full` 或显式 width 除外。
|
|
796
|
-
|
|
797
|
-
**CSS 自定义属性:**
|
|
798
|
-
|
|
799
|
-
| 属性 | 默认值 | 说明 |
|
|
800
|
-
| ------------------------ | ------------- | ---------------------- |
|
|
801
|
-
| `--wui-button-width` | `max-content` | 按钮宽度 |
|
|
802
|
-
| `--wui-button-px` | `12px` | 水平内边距 |
|
|
803
|
-
| `--wui-button-gap` | `8px` | 前缀/默认/后缀插槽间距 |
|
|
804
|
-
| `--wui-button-color` | 随 variant | 按钮文字颜色 |
|
|
805
|
-
| `--wui-button-bg` | 随 variant | 按钮背景颜色 |
|
|
806
|
-
| `--wui-button-bg-hover` | 随 variant | 悬停背景颜色 |
|
|
807
|
-
| `--wui-button-bg-active` | 随 variant | 按下背景颜色 |
|
|
808
|
-
|
|
809
|
-
#### `<web-ui-button-group>`
|
|
810
|
-
|
|
811
|
-
按钮组,管理子按钮布局和方向。
|
|
812
|
-
|
|
813
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
814
|
-
| ----------- | ---------------------------- | -------------- | -------- |
|
|
815
|
-
| `direction` | `'horizontal' \| 'vertical'` | `'horizontal'` | 布局方向 |
|
|
816
|
-
|
|
817
|
-
**插槽:** `default`(投影 `<web-ui-button>` 元素)
|
|
818
|
-
|
|
819
|
-
以内部派生的视觉上下文控制按钮组方向,不改写子按钮属性。
|
|
820
|
-
|
|
821
|
-
**CSS 自定义属性:**
|
|
822
|
-
|
|
823
|
-
| 属性 | 默认值 | 说明 |
|
|
824
|
-
| ----------------------------------- | ------ | ---------------------------------------------------- |
|
|
825
|
-
| `--wui-button-group-divider-length` | `20px` | 相邻按钮之间分割线的长度;短边恒为 1px,不随该值变化 |
|
|
826
|
-
|
|
827
|
-
在按钮组宿主上设置。该 token 控制两个方向上的长边:横排改的是分割线高度,竖排改的是宽度。
|
|
828
|
-
|
|
829
|
-
---
|
|
830
|
-
|
|
831
|
-
### 浮层 / 模态
|
|
832
|
-
|
|
833
|
-
#### `<web-ui-dialog>`
|
|
834
|
-
|
|
835
|
-
模态对话框,使用原生 `<dialog>` 的 `showModal()`。
|
|
836
|
-
|
|
837
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
838
|
-
| ------------------- | --------- | ------- | ----------------------------------------------------------------------------------------------- |
|
|
839
|
-
| `open` | `boolean` | `false` | 对话框可见性 |
|
|
840
|
-
| `closable` | `boolean` | `false` | 显示内置关闭按钮 |
|
|
841
|
-
| `no-scroll-lock` | `boolean` | `false` | 打开时不锁定页面滚动 |
|
|
842
|
-
| `no-backdrop-close` | `boolean` | `false` | 禁止点击遮罩关闭 |
|
|
843
|
-
| `no-escape-close` | `boolean` | `false` | 禁止按 Escape 关闭 |
|
|
844
|
-
| `controlled` | `boolean` | `false` | Escape 与遮罩仅请求 `open=false`,由 Consumer 回写 `open` |
|
|
845
|
-
| `heading` | `string` | `''` | 标题文字;`header` 插槽为空时的 fallback(`headless` 模式下忽略) |
|
|
846
|
-
| `headless` | `boolean` | `false` | 只保留浮层行为;`default` 插槽渲染为卡片内唯一的滚动层,不再渲染内置 header 带、内容区与 footer |
|
|
847
|
-
| `dialog-label` | `string` | `''` | 内部原生 dialog 的 accessible name;`headless` 模式下必填 |
|
|
848
|
-
|
|
849
|
-
**事件:** `open-change` (`CustomEvent<{ open: boolean }>`)。启用 `controlled` 后,Escape 与遮罩点击仅请求 `open=false`,Consumer 写入 `open=false` 前对话框保持打开。程序化 API(`showModal()`/`close()`/直接赋值 `open`)不受影响,始终直通且不派发事件。原生 dialog 关闭(如表单 `method="dialog"`)会被恢复为受控打开状态并重新发出同一请求。
|
|
850
|
-
|
|
851
|
-
**插槽:** `header`, `default`, `footer`。`headless` 下只渲染 `default` 插槽。
|
|
852
|
-
|
|
853
|
-
`header` 槽是卡片的 chrome 带:不参与滚动,自带水平 padding(`--wui-dialog-inline-padding`),内容不会贴到卡片内沿。`heading` 属性是它的 fallback——槽一旦有分配元素就整条接管,否则非空的 `heading` 渲染出 `.wui-dialog-heading`;两者都为空时整条带隐藏。
|
|
854
|
-
|
|
855
|
-
`default` 槽在两种模式下都承载正文。默认模式下它渲染进 `.desc`(卡片的内容区),与 `header` 带、`footer` 并列。`headless` 下整张卡片变成一个滚动层:`default` 槽渲染进 `.wui-dialog-content`,`header` 带、`.desc` 与 `footer` 一概不渲染,`heading` 一并失效。`headless` 与 `<web-ui-drawer>` 是同一套机制——显式布尔属性,不是「往某个插槽里塞东西」的开关。
|
|
856
|
-
|
|
857
|
-
**accessible name:** 设置了 `dialog-label` 时,内部原生 `<dialog>` 直接取它的 `aria-label`;否则在默认模式且 `header` 带可见时,取指向该带的 `aria-labelledby="wui-dialog-heading"`。`headless` 没有可指向的内置标题,所以**名字必须由 Consumer 提供**——`dialog-label`,或自绘的可关联标题元素。
|
|
858
|
-
|
|
859
|
-
**方法:** `showModal()`, `close()`
|
|
860
|
-
|
|
861
|
-
使用原生 `<dialog>`,`@cancel` 阻止默认关闭行为。除非存在 `no-escape-close`,否则 Escape 调用 `close()`;除非存在 `no-backdrop-close`,否则点击遮罩关闭。启用 `controlled` 后,两者都只派发关闭请求而不自关闭。
|
|
862
|
-
|
|
863
|
-
启用 `closable` 后,默认模式与 `headless` 模式都会渲染内置关闭按钮,且与 Escape、遮罩点击走同一条关闭路径(`controlled` 对它同样生效)。两种模式的位置已统一:按钮都绝对定位到卡片右上角,距卡片两沿的偏移由 `--wui-dialog-close-top` / `--wui-dialog-close-right` 控制(默认都是 `20px`;drawer 关闭按钮仍为 `16px`)。只有 `headless` 会把整卡换掉;默认模式下 `header` 带会为按钮留出避让,长内容不会钻到按钮下面。图标与 drawer 关闭按钮同为 `ooui:close`。不启用 `closable` 时什么都不渲染——既没有按钮,也没有多余的包裹层。
|
|
864
|
-
|
|
865
|
-
> **Escape 归属**:Escape 由共享仲裁者统一判定,一次按键只关闭**最内层**的已打开浮层(popover、select、autocomplete、dropdown、context-menu、drawer、dialog 都参与)。例如在 drawer 内打开 select,第一次 Escape 只关 select,第二次才关 drawer。互不嵌套的并列浮层按打开顺序关闭最上层。正在播退场过渡的面板仍在场上,也仍由它接住 Escape——但只要还有别的浮层开着,那一次按键仍然归该层,「一次按键关一层」不因此改变。`image-preview` 同样参与:它的原生 `<dialog>` 会登记进同一个仲裁者,Escape 按层级判定;组件的 `cancel` handler 只是拦掉原生的瞬时关闭,把 top layer 保留到退场过渡结束。
|
|
866
|
-
|
|
867
|
-
**CSS 自定义属性:**
|
|
868
|
-
|
|
869
|
-
| 属性 | 默认值 | 说明 |
|
|
870
|
-
| --------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
871
|
-
| `--wui-dialog-width` | `360px` | 对话框宽度 |
|
|
872
|
-
| `--wui-dialog-max-height` | `90vh` / `min(90vh, 90dvh)`(dvh 引擎) | 内容区(`.desc`)的最大高度,不是整卡高度。两套 fallback:基础规则 `90vh`,`@supports (height: 100dvh)` 内 `min(90vh, 90dvh)`——不支持 dvh 的引擎只拿到 `90vh`。整卡换算见下方说明 |
|
|
873
|
-
| `--wui-dialog-close-top` | `20px` | `closable` 按钮距卡片顶部的偏移 |
|
|
874
|
-
| `--wui-dialog-close-right` | `20px` | `closable` 按钮距卡片右沿的偏移 |
|
|
875
|
-
| `--wui-dialog-overlay-bg` | `var(--wui-color-backdrop)` | 遮罩背景色 |
|
|
876
|
-
| `--wui-dialog-bg` | `var(--wui-color-surface-overlay)` | 玻璃卡片背景色,回退到 `rgb(248 248 248 / 0.92)` |
|
|
877
|
-
| `--wui-dialog-padding` | `20px 24px 24px` | 卡片的 block 内边距。inline 分量不再作用于卡片:水平内边距移到了各分区(`.header` / `.desc` / `.wui-dialog-footer`),见 `--wui-dialog-inline-padding` |
|
|
878
|
-
| `--wui-dialog-inline-padding` | `24px` | `header` 带、内容区(`.desc`)与 footer 的水平内边距。卡片自身没有 inline padding,内容区的滚动口(其 border box)因此与卡片内沿重合——滚动条贴边——而内容仍保留这个内缩。默认取自 `--wui-space-6` |
|
|
879
|
-
| `--wui-dialog-title-gap` | `16px` | 标题下方间距 |
|
|
880
|
-
| `--wui-dialog-desc-gap` | `24px` | 正文内容下方间距 |
|
|
881
|
-
| `--wui-dialog-desc-focus-padding` | `6px` | 内容区**block 轴**预留的 focus-ring 余量。`.desc` 既是滚动容器,又因为有一条轴必须非 `visible` 而成了裁剪盒,所以紧贴其上下沿的控件,ring 会被 padding box 裁掉;这个余量覆盖 `5px` 的 ring(`offset: 2` + `width: 3`)。横向的 ring 余量改由 `--wui-dialog-inline-padding` 提供(24px ≫ 5px),所以本 token 只作用于 block 轴——原来的 inline 分量不再生效。成对负 margin 使 chrome 高度保持不变 |
|
|
882
|
-
| `--wui-dialog-footer-gap` | `10px` / horizontal `12px` | Footer 按钮间距 |
|
|
883
|
-
| `--wui-dialog-footer-justify` | `flex-end`(默认)/ `center`(horizontal) | Footer `justify-content`;horizontal 模式下覆盖为 `flex-end` 可右对齐 |
|
|
884
|
-
| `--wui-dialog-scale-enter` | `1.1` | 进场缩放起点:由 `1.1` 收缩到 `1`,退场反向 |
|
|
885
|
-
|
|
886
|
-
> **高度语义**:本 token 限制的是内容区,不是整卡。`header` 带与 footer 仍由内容撑开,超出上限的部分在 `.desc` 内部滚动;整卡本身只由字面量 `100vh` / `100dvh` 兜底,接住「视口太矮、chrome 加内容区上限也放不下」的情况。要给**整卡**定上限的宿主仍需自行量出 chrome(卡片 block 内边距 + header 带 + 间距 + footer)并从 token 里减掉——Interweave 前端三个 dialog 分别减 142、142、158。不再需要这个数的是「让内容区滚起来」:改动前 `.desc` 既没有上限也没有 `overflow`,唯一能滚的是宿主自己撑出来的内层盒子,宿主想让它滚就必须自己算这个高度。
|
|
887
|
-
>
|
|
888
|
-
> **`headless` 模式没有 `.desc`**,所以该 token 在这一档不生效。`headless` 下的 `default` 插槽被包进 `.wui-dialog-content`,滚动发生在这一层;卡片在内容装得下时仍贴合内容,装不下时由同一条字面量 `100vh` / `100dvh` 兜底。内置关闭按钮在内容滚动时保持 `--wui-dialog-close-top` / `--wui-dialog-close-right` 给出的位置不变。
|
|
889
|
-
|
|
890
|
-
#### `<web-ui-drawer>`
|
|
891
|
-
|
|
892
|
-
侧边抽屉,使用原生 `<dialog>` 并自带关闭动画。非 headless 模式下抽屉渲染为四周留边的浮动圆角卡片(与 layout sidebar 的卡片语言一致),弹性拖拽的位移表现为边距变化而非缺口。
|
|
893
|
-
|
|
894
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
895
|
-
| ------------------- | ---------------------------------------- | --------- | ---------------------------------------------------- |
|
|
896
|
-
| `open` | `boolean` | `false` | 抽屉可见性 |
|
|
897
|
-
| `placement` | `'right' \| 'left' \| 'top' \| 'bottom'` | `'right'` | 滑入方向 |
|
|
898
|
-
| `heading` | `string` | `''` | 标题文字(无 header 插槽时显示) |
|
|
899
|
-
| `closable` | `boolean` | `false` | 显示关闭按钮 |
|
|
900
|
-
| `no-scroll-lock` | `boolean` | `false` | 打开时不锁定页面滚动 |
|
|
901
|
-
| `no-backdrop-close` | `boolean` | `false` | 禁止点击遮罩关闭 |
|
|
902
|
-
| `controlled` | `boolean` | `false` | 用户关闭仅请求 `open=false`,由 Consumer 回写 `open` |
|
|
903
|
-
| `headless` | `boolean` | `false` | 仅保留 overlay 行为,默认插槽不渲染内置抽屉 UI |
|
|
904
|
-
| `dialog-label` | `string` | `''` | 内部原生 dialog 的可访问名称;headless 模式必须提供 |
|
|
905
|
-
| `draggable` | `boolean` | `false` | 打开时在抽屉内缘显示 drag bar,支持拖拽关闭手势 |
|
|
906
|
-
|
|
907
|
-
**事件:** `open-change` (`CustomEvent<{ open: boolean }>`)。启用 `controlled` 后,Escape、遮罩、内置关闭按钮和拖拽松手仅请求 `open=false`;Consumer 写入 `open=false` 前抽屉保持打开。程序化 API(`show()`/`close()`/直接赋值 `open`)不受影响,始终直通且不派发事件。若原生 dialog 在请求等待期间关闭,组件会恢复其打开的 top layer 状态并发出同一关闭请求。嵌套 drawer 时,内层 `open-change` 会 composed 冒泡穿过外层根,按 `event.target` 区分。
|
|
908
|
-
|
|
909
|
-
**插槽:** `header`, `default`, `footer`;启用 `headless` 时仅渲染 `default` 插槽。
|
|
910
|
-
|
|
911
|
-
**方法:** `show()`, `close()`
|
|
912
|
-
|
|
913
|
-
`headless` 保留原生 dialog、遮罩、placement 动画、Escape/遮罩关闭行为和滚动锁定,但不渲染内置 glass 主体、header、关闭按钮或 footer;Consumer 负责完整定义默认插槽内容的样式,并且必须提供 `dialog-label`,确保原生 dialog 具有可访问名称。
|
|
914
|
-
|
|
915
|
-
关闭时保留原生 dialog 的 top layer,待退出过渡完成后调用 `dialog.close()`。Escape 始终走此关闭路径;`no-backdrop-close` 仅控制遮罩点击。
|
|
916
|
-
|
|
917
|
-
启用 `closable` 时,内置关闭按钮固定在 header 右上角(默认顶部 `16px`、右侧 `16px`),不会再拉伸到抽屉中间。顶部 `16px` 经 `--wui-space-4` 解析,因此与 drawer 的 header padding 同源;右侧 `16px` 是按钮自身到面板右边沿的偏移,它并不等于 header 的水平内边距(`20px`),因此保持字面量。
|
|
918
|
-
|
|
919
|
-
**拖拽关闭:** 启用 `draggable` 后,打开的抽屉在内缘显示灰色胶囊 drag bar(默认 4×56px,视觉中线距内缘 10px;`right` 在左缘、`left` 在右缘、`top` 在下缘、`bottom` 在上缘):
|
|
920
|
-
|
|
921
|
-
中线跟随 `--wui-drawer-content-padding` 的半值,并以「半个胶囊厚度 + 4px 呼吸间距」兜底:把该 padding 归零、让内容贴边铺满时,胶囊仍留可见内缩,既不会越出面板内缘,也不会贴死面板边缘。
|
|
922
|
-
|
|
923
|
-
胶囊是纯视觉件:`pointer-events: none`,本身不吃任何一次按下。真正被按下的是命中层。左右 placement 只有热区;上下 placement 有三层——拖拽热区、沿可抓取边缘的一条端头热边(宽度与胶囊等宽,厚度不小于 `--wui-drawer-drag-edge-size`),以及整块的 header(`placement=bottom`)或 footer(`placement=top`)。热边的高度取「胶囊外沿」与该 token 的较大者,按构造覆盖胶囊所在的带,所以在四个 placement 上,按住胶囊能开始拖拽、悬停胶囊会点亮它。
|
|
924
|
-
|
|
925
|
-
让开 header(`placement=bottom`)或 footer(`placement=top`)的是热区本身,因此那一节不会把胶囊推离面板边缘;而那一节改由**自己**接拖拽,不再整块按不动:在它里面任意一点起手都能开始拖拽,标题文字同样在内。控件的让开是**逐个控件、在事件层**做的,不是把命中面几何切开——按下路径上只要出现可点元素(原生 `button` / `input` / `select` / `textarea` / `label` / `a[href]` / `summary`,任何 `contenteditable`,交互语义 `role`(`button`、`checkbox`、`switch`、`tab`、`menuitem`、`option`、`slider` 等),以及任何显式 `tabindex` 且不为 `-1`),这一次按下就交给控件、不起拖拽。自绘控件若本身不是原生可交互元素,需要带上其中之一才会被认出来;补 `role` 或 `tabindex` 就是入口,而它本身也是无障碍上该做的事。几何切割是被有意否掉的:它必须测量每个控件的位置,控件一动就失效,而且只是把 A6 那条缺陷换了个位置。代价是明说的:内置关闭按钮贴着面板右缘、在胶囊跨度之外,仍可点击;落在胶囊水平跨度内的控件,其顶部到热边高度之间仍被拖拽面盖住。
|
|
926
|
-
|
|
927
|
-
「把手占位」自带一条推论:落在这条跨度内、顶边又落在热边高度(`--wui-drawer-drag-edge-size`,默认 20px;胶囊外沿更厚时以胶囊为准)之内的控件,上部会被拖拽面盖住——把手就画在那里,起手必须从把手所在的位置开始。抬 `z-index` 没用(Chromium 里热边始终压在 slotted 内容之上),量得着的杠杆是 `--wui-drawer-drag-bar-length`(同时决定胶囊长度与热边宽度)与 `--wui-drawer-drag-edge-size`(热边厚度)。
|
|
928
|
-
|
|
929
|
-
- 拖拽实时跟手,遮罩透明度按比例淡出。
|
|
930
|
-
- 遮罩点击关闭只认**轻点链路**:按下起点在遮罩上、且按-放位移在轻点量级内的 click 才关闭。浏览器对「按下 → 拖动 → 松手」生成的 click 落在起点与松手点的共同祖先(dialog)上——从面板内容或遮罩上开始拖拽、松手落在遮罩时,click 的 target 同样是 dialog;组件在 `pointerdown` 记录起点与坐标做回溯校验,这类拖拽松手一律弹回。`detail` 为 0 的 click(键盘/程序化来源)不消费指针记录。
|
|
931
|
-
- 松手时自抓取瞬间起的**净位移**超过抽屉尺寸的一半(下限 10px)或快速甩动即关闭,否则弹回打开位;方向随 placement 适配。判定的每一环都与 Base UI `useSwipeDismiss` 对齐:
|
|
932
|
-
- 位移零点与判定时钟都在**首个 `pointermove`** 处重置,用来吸收「按下 → 首个 move」之间的空隙;该次 move 的整程位移被整体丢弃,因此一次手势至少要两个 move 才可能累积出位移。
|
|
933
|
-
- 甩动看的是**整段手势**的平均速度(净位移 ÷ 手势时长,分母下限 50ms)**达到** 500px/s,不是释放瞬间的瞬时速度。滑窗只描述最后一小段轨迹;在整段平均速度下,「先往边缘拖出、再快速扫回」不再能凑成甩动。时长为 0 时速度取 0 而不是按 50ms 兜底,因此测不到时长的手势永远不会被读成甩动。
|
|
934
|
-
- 净位移没有朝闭合方向的松手一律弹回打开位。
|
|
935
|
-
- 距离判据同样用净位移:从弹回途中抓取后不再拖动就松手,不会被当成「已经拖过一半」。
|
|
936
|
-
- 自「方向确认点」起反向回撤满 10px 即取消本次甩动,因此拖出后回扫不会关闭。位移重新越过距离阈值会清除该标记,所以「已经拖过一半」不受小幅回撤影响。上游有同一个守卫(`cancelledSwipe`),但在 drawer 这条路径上永远走不到——`DrawerViewport.onRelease` 必然返回一个决定。
|
|
937
|
-
- 朝打开方向的拖拽按平方根压缩(`sign(d) · |d| ** 0.5`),对齐 `applyDirectionalDamping`。阻尼作用于本次手势的**增量**、叠加在抓取瞬间的位移之上,因此自限幅而不需要固定的位移上限。
|
|
938
|
-
- 释放速度只决定收尾过渡的时长(180–420ms);收尾本身是一次交还 CSS 的 `transform` 过渡,不再创建 WAAPI 动画。
|
|
939
|
-
- 启用 `controlled` 后,超过阈值松手仅派发 `open-change(false)`;抽屉在闭合位短暂等待(120ms 回写窗口),Consumer 拒绝或超时未回写时弹回打开位。
|
|
940
|
-
- 不支持拖拽打开——关闭态的抽屉在原生 dialog 之外没有任何渲染物。
|
|
941
|
-
- `prefers-reduced-motion` 下松手即时到位,不播放收尾过渡。
|
|
942
|
-
|
|
943
|
-
**CSS 自定义属性:**
|
|
944
|
-
|
|
945
|
-
| 属性 | 默认值 | 说明 |
|
|
946
|
-
| --------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
947
|
-
| `--wui-drawer-width` | `320px` | 抽屉宽度 |
|
|
948
|
-
| `--wui-drawer-height` | `300px` | 抽屉高度(上/下) |
|
|
949
|
-
| `--wui-drawer-bg` | `var(--wui-color-surface-overlay)` | 抽屉背景色 |
|
|
950
|
-
| `--wui-drawer-radius` | `var(--wui-radius-overlay, 28px)` | 浮动卡片圆角(非 headless) |
|
|
951
|
-
| `--wui-drawer-inset` | `8px` | 浮动卡片视口留边(非 headless);置 `0` 为贴边几何 |
|
|
952
|
-
| `--wui-drawer-nested-peek-base` | `43.2px` | 嵌套堆叠露边基准 `A`(`width <= 640px` 时为 `28.8px`);堆叠总宽按 `A · ln(n)` 增长,单层因此完全不动,每多一层新增的露边递减。置 `0` 关闭露边。详见下方说明 |
|
|
953
|
-
| `--wui-drawer-overlay-bg` | `rgb(0 0 0 / 0.12)` | 遮罩背景色 |
|
|
954
|
-
| `--wui-drawer-drag-zone-size` | `20px` | Drag-to-close 命中热区厚度(draggable) |
|
|
955
|
-
| `--wui-drawer-drag-zone-inset` | `var(--wui-space-1, 4px)` | 拖拽热区与它避让的那一节(`placement=bottom` 的 header / `placement=top` 的 footer)之间的呼吸间距;该节不存在时为 0 |
|
|
956
|
-
| `--wui-drawer-drag-bar-thickness` | `4px` | Drag bar 胶囊厚度(短轴) |
|
|
957
|
-
| `--wui-drawer-drag-bar-length` | `56px` | Drag bar 胶囊长度(沿抽屉边缘) |
|
|
958
|
-
| `--wui-drawer-drag-edge-size` | `20px` | 胶囊命中带沿可抓取边缘的厚度(仅上下 placement)。以胶囊外沿兜底,胶囊所在的带恒被覆盖;默认值与 `--wui-drawer-drag-zone-size` 同值,于是把手不会比旁边的带子更难按 |
|
|
959
|
-
| `--wui-drawer-header-padding` | `16px 20px` | Header 区域 padding |
|
|
960
|
-
| `--wui-drawer-close-top` | `16px` | 内置关闭按钮相对 header 顶部的偏移 |
|
|
961
|
-
| `--wui-drawer-close-right` | `16px` | 内置关闭按钮相对抽屉右缘的偏移 |
|
|
962
|
-
| `--wui-drawer-content-padding` | `20px` | 内容区 padding;同时驱动 drag bar 视觉中线(取半值,不低于半个胶囊厚度 + 4px) |
|
|
963
|
-
| `--wui-drawer-footer-padding` | `16px 20px` | Footer 区域 padding |
|
|
964
|
-
|
|
965
|
-
`--wui-drawer-nested-peek-base` 不需要消费方声明:默认值 `43.2px` 是组件用 `CSS.registerProperty` 注册该属性时的 `initialValue`(`components/drawer/index.ts`),窄视口的 `28.8px` 基准来自 `components/drawer/style.css` 的 `@media (width <= 640px)` 规则——这两处就是内置基准的所在地。在宿主或任意祖先上设置该 token 可同时覆盖两者,在任何视口宽度下都生效。
|
|
966
|
-
|
|
967
|
-
按内置基准,四层 `320px` 等宽堆叠逐层露边在桌面端为 `29.94px` / `17.52px` / `12.43px`、窄视口为 `19.96px` / `11.68px` / `8.29px`,对应总堆叠宽度 `59.89px` 与 `39.93px`。单层始终不动,因为 `ln(1)` 为 `0`。
|
|
968
|
-
|
|
969
|
-
#### `imagePreview()`
|
|
970
|
-
|
|
971
|
-
命令式图片预览,没有声明式标签契约:只能通过 `imagePreview()` 打开,并用返回的句柄控制。内部使用原生 `<dialog>` 的 `showModal()`,默认挂载到最近的 `web-ui-theme` theme-owned overlay root(无可用 theme-owned root 时回退到全局 fallback overlay root)。
|
|
972
|
-
|
|
973
|
-
它不复用 `<web-ui-dialog>` 组件:预览需要自身铺满视口的 dialog(全视口遮罩由 dialog 内独立遮罩层渲染,dialog 元素自身不做 opacity 过渡,玻璃控件的 backdrop blur 全程连续)与自有指针交互,与 dialog 组件的玻璃卡片 / 插槽契约不同源。两者共享 `native-dialog-presence` 与 `scroll-lock` 两个底层插件,`noScrollLock` / `noBackdropClose` 的命名与语义也对齐同名属性。
|
|
974
|
-
|
|
975
|
-
```ts
|
|
976
|
-
import { imagePreview } from '@greypan/web-ui'
|
|
977
|
-
|
|
978
|
-
const preview = imagePreview({
|
|
979
|
-
images: [{ src: '/a.jpg', alt: '图 A' }, { src: '/b.jpg' }],
|
|
980
|
-
index: 0,
|
|
981
|
-
nav: true,
|
|
982
|
-
toolbar: true,
|
|
983
|
-
closable: true,
|
|
984
|
-
indicator: true
|
|
985
|
-
})
|
|
986
|
-
|
|
987
|
-
preview.next()
|
|
988
|
-
preview.zoomIn()
|
|
989
|
-
|
|
990
|
-
await preview.closed
|
|
991
|
-
```
|
|
992
|
-
|
|
993
|
-
**选项:**
|
|
994
|
-
|
|
995
|
-
| 选项 | 类型 | 默认值 | 说明 |
|
|
996
|
-
| ----------------- | -------------------- | ------- | ---------------------------------------------------- |
|
|
997
|
-
| `images` | `ImagePreviewItem[]` | — | 图片列表,至少一项,否则抛错 |
|
|
998
|
-
| `index` | `number` | `0` | 初始索引,越界时钳制到有效区间 |
|
|
999
|
-
| `loop` | `boolean` | `true` | 首尾循环切换 |
|
|
1000
|
-
| `target` | `Element` | — | 用于解析最近 theme-owned overlay root 的触发元素 |
|
|
1001
|
-
| `container` | `HTMLElement` | — | 显式挂载容器,优先级高于主题作用域 |
|
|
1002
|
-
| `nav` | `boolean` | `false` | 展示上一张 / 下一张按钮;仅图片多于一张时渲染 |
|
|
1003
|
-
| `toolbar` | `boolean` | `false` | 展示缩放工具条(缩小 / 倍率 / 放大 / 重置) |
|
|
1004
|
-
| `closable` | `boolean` | `false` | 展示关闭按钮 |
|
|
1005
|
-
| `indicator` | `boolean` | `false` | 展示「当前 / 总数」指示器,并用 `aria-live` 宣告位置 |
|
|
1006
|
-
| `swipe` | `boolean` | `false` | 1x 下允许左右滑动切换图片;生效期间横向轴归它 |
|
|
1007
|
-
| `noScrollLock` | `boolean` | `false` | 打开期间不锁定页面滚动 |
|
|
1008
|
-
| `noBackdropClose` | `boolean` | `false` | 点击图片以外的空白区域不关闭 |
|
|
1009
|
-
|
|
1010
|
-
`ImagePreviewItem` 为 `{ src: string; alt?: string }`;`alt` 缺省为空字符串。
|
|
1011
|
-
|
|
1012
|
-
展示类选项默认全关:不传任何选项时只渲染图片本身,导航、工具条、关闭按钮与指示器都需要显式开启。
|
|
1013
|
-
|
|
1014
|
-
**返回句柄:**
|
|
1015
|
-
|
|
1016
|
-
| 成员 | 类型 | 说明 |
|
|
1017
|
-
| ---------------------------------------- | ----------------------------- | -------------------------------- |
|
|
1018
|
-
| `index` | `number` | 当前索引,关闭后保留最后一次的值 |
|
|
1019
|
-
| `scale` | `number` | 当前缩放倍率,区间 `[1, 4]` |
|
|
1020
|
-
| `images` | `readonly ImagePreviewItem[]` | 归一化后的图片列表 |
|
|
1021
|
-
| `closed` | `Promise<void>` | 退场结束且宿主移除后兑现 |
|
|
1022
|
-
| `next()` / `prev()` | `() => void` | 相对切换 |
|
|
1023
|
-
| `goTo(index)` | `(index: number) => void` | 跳转;`loop` 关闭时在边界钳制 |
|
|
1024
|
-
| `zoomIn()` / `zoomOut()` / `resetZoom()` | `() => void` | 缩放控制 |
|
|
1025
|
-
| `close()` | `() => void` | 关闭并播放退场动画 |
|
|
1026
|
-
|
|
1027
|
-
**交互:** 方向键始终可切换图片,与 `nav` 无关;`+` / `-` 键和滚轮缩放,`0` 重置。缩放带锚点:滚轮固定光标下的那一点,双指捏合固定两指中点,工具条按钮、键盘快捷键与双击则以视口中心为中心放大。按住鼠标拖拽或单指按住拖拽都能平移图片,方向不限,且与倍率无关:放大后是查看被裁切的边缘,1x 是在视口内移动图片。两种情形共用同一个边界——图片与舞台尺寸差的一半,因此图片永远不会被拖出视口;拖到边界后继续拖是钳制而非回弹。双击图片在 1x 与 2x 之间切换。
|
|
1028
|
-
|
|
1029
|
-
双指缩放始终开启且不提供选项:舞台本身就持有指针交互,没有会冲突的手势。第一根手指照常负责平移 / 滑动,第二根手指落下才进入捏合,并中止进行中的平移或滑动。捏合不会关闭浮层;混合输入事后可能补发的兼容 `click` 会被吞掉,而不是当作遮罩点击。
|
|
1030
|
-
|
|
1031
|
-
点击图片以外的空白区域或按 Escape 关闭,原生 dialog 始终暴露 `图片预览` 这一可访问名称。`swipe` 开启且图片多于一张时,图片排布在单条轮播轨道上:当前图与相邻图各占等宽等高的 slide,舞台裁切溢出,整条轨道随手指平移;松手时越过距离阈值(舞台宽度的 15%,取整)滑入相邻图,位移未达阈值但快速轻扫的采样速度达到 320px/s 以上、或位移至少 16px 且速度(窗口采样或整段平均)达到 160px/s 以上的轻扫同样提交,否则弹回;`loop` 开启时首尾环绕的相邻图恒在当前图两侧,`loop` 关闭时边界方向回弹不越界。该手势生效期间接管整个拖拽:1x 下横向拖拽用于切图,纵向位移被完全忽略(不驱平移、不跟手)。放大后拖拽一律平移(纵横都跟),不再切换图片。由于 1x 也能平移,`resetZoom()` 在「已放大」和「已位移」两种情况下都可用——工具条里的重置按钮对已平移的 1x 图片同样可点。
|
|
1032
|
-
|
|
1033
|
-
关闭请求不会立刻销毁原生 dialog:它保持在 top layer,等退场过渡结束后才调用 `dialog.close()`,随后宿主从 DOM 移除并兑现 `closed`。打开期间锁定页面滚动,除非 `noScrollLock` 为 `true`。
|
|
1034
|
-
|
|
1035
|
-
**CSS 自定义属性:**
|
|
1036
|
-
|
|
1037
|
-
| 属性 | 默认值 | 说明 |
|
|
1038
|
-
| -------------------------------- | -------------------------------- | ------------------------ |
|
|
1039
|
-
| `--wui-image-preview-overlay-bg` | `rgb(0 0 0 / 0.72)` | 全视口遮罩背景色 |
|
|
1040
|
-
| `--wui-image-preview-edge-gap` | `20px` | 控件到视口边缘的可见距离 |
|
|
1041
|
-
| `--wui-duration-swipe-settle` | `220ms` | 轮播滑入/回位的过渡时长 |
|
|
1042
|
-
| `--wui-ease-swipe` | `cubic-bezier(0.32, 0.72, 0, 1)` | 轮播滑入/回位的缓动曲线 |
|
|
1043
|
-
|
|
1044
|
-
---
|
|
1045
|
-
|
|
1046
|
-
### 文档流展开收起
|
|
1047
|
-
|
|
1048
|
-
#### `<web-ui-collapse>`
|
|
1049
|
-
|
|
1050
|
-
文档流内的展开收起容器,带高度(或宽度)过渡动画。单元素双插槽;无 portal、无滚动锁定、无焦点管理。
|
|
1051
|
-
|
|
1052
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1053
|
-
| -------------- | --------- | ------- | ------------------------------------------------------------------------------------ |
|
|
1054
|
-
| `open` | `boolean` | `false` | 展开状态;交互时自管理,`open-change` 仅用户来源时派发 |
|
|
1055
|
-
| `disabled` | `boolean` | `false` | 忽略 trigger 点击并在 trigger 元素上设 `aria-disabled`;已展开内容保持现状 |
|
|
1056
|
-
| `horizontal` | `boolean` | `false` | 沿宽度而非高度动画 |
|
|
1057
|
-
| `keep-mounted` | `boolean` | `false` | 关闭稳态以 `inert` 保留在 0fr 轨道内(滚动位置与布局可测量),而非内部容器 `hidden` |
|
|
1058
|
-
| `peek` | `string` | — | 关闭稳态露出的尺寸(CSS 长度,如 `120px`),沿动画轴生效;末端自带自动长度的边缘渐隐 |
|
|
1059
|
-
|
|
1060
|
-
**事件:** `open-change` (`CustomEvent<{ open: boolean }>`)。仅用户来源的切换(trigger 点击)派发;程序化写入(`open`、`show()`、`close()`、`toggle()`)不派发。嵌套时内层 `open-change` 会冒泡穿过外层根(composed 事件),按 `event.target` 区分。
|
|
1061
|
-
|
|
1062
|
-
**插槽:** `default`(trigger)+ `content`(折叠内容)
|
|
1063
|
-
|
|
1064
|
-
**方法:** `show()`、`close()`、`toggle()`
|
|
1065
|
-
|
|
1066
|
-
```html
|
|
1067
|
-
<web-ui-collapse>
|
|
1068
|
-
<button type="button">点击切换</button>
|
|
1069
|
-
<div slot="content">折叠内容</div>
|
|
1070
|
-
</web-ui-collapse>
|
|
1071
|
-
```
|
|
1072
|
-
|
|
1073
|
-
初始即带 `open` attribute 时直接落稳态,不播动画。支持嵌套 collapse。
|
|
1074
|
-
|
|
1075
|
-
**trigger 语义:** 交互语义完全来自 default slot 放入的元素——原生 `<button>`、`<web-ui-button>` 或其他可交互元素原生提供 Enter/Space 激活与焦点。collapse 把 `aria-expanded` / `aria-controls`(指向内容轨道)与 `aria-disabled` 回写到 trigger slot 的首个 assigned element。trigger 请使用可交互元素:纯 `<span>` 可点击但没有键盘/焦点语义。
|
|
1076
|
-
|
|
1077
|
-
**关闭稳态语义:** Consumer的 light DOM 永不移动或卸载。默认关闭稳态在内部内容容器上设 `hidden`;`keep-mounted` 时内部容器标记 `inert`,保留在收起轨道内可测量。
|
|
1078
|
-
|
|
1079
|
-
**`peek`(只露一部分):** 设置 `peek` 后关闭稳态不再收拢到 0,而是沿动画轴露出内容的头部 `peek` 长度(`horizontal` 时改为宽度):轨道保持在内容高度,裁剪长度落在内部容器上。其语义等同 `keep-mounted`——内容保留挂载但被 `inert` 阻断,被裁掉的部分不可聚焦、不可点击。内容本身不足 `peek` 时按内容实际尺寸收起,不留空白。由于固定长度与自适应高度无法由 CSS 插值,`peek` ↔ 展开这两个方向改为读出像素后以显式长度驱动(每次开合测量一次);其余动画路径仍是零测量的 grid `fr` 过渡。
|
|
1080
|
-
|
|
1081
|
-
```html
|
|
1082
|
-
<web-ui-collapse peek="120px">
|
|
1083
|
-
<button type="button">点击切换</button>
|
|
1084
|
-
<div slot="content">很长的内容——关闭时只露出前 120px</div>
|
|
1085
|
-
</web-ui-collapse>
|
|
1086
|
-
```
|
|
1087
|
-
|
|
1088
|
-
**边缘渐隐:** 露出区域的裁剪边缘自带一段 alpha 渐变(`horizontal` 时改为右边),让被裁掉的部分柔和过渡到背景而不是硬切。渐变长度由 `peek` 推导,不需要也没有第二个属性可调——需要时用 CSS 变量微调:
|
|
1089
|
-
|
|
1090
|
-
| CSS 变量 | 默认值 | 说明 |
|
|
1091
|
-
| -------------------------------- | ------------- | ---------------------------------------------- |
|
|
1092
|
-
| `--wui-collapse-peek-edge-ratio` | `0.4` | 渐变长度占 `peek` 的比例 |
|
|
1093
|
-
| `--wui-collapse-peek-edge-max` | `112px` | 长度上限,避免大 `peek` 算出过长的虚化带 |
|
|
1094
|
-
| `--wui-collapse-peek-edge` | — | 显式指定长度,优先于推导值 |
|
|
1095
|
-
| `--wui-collapse-peek-edge-color` | `transparent` | 渐变末端颜色(需带 alpha,mask 按 alpha 解析) |
|
|
1096
|
-
| `--wui-collapse-peek-edge-blur` | `4px` | 同一条渐变带上背景模糊的半径 |
|
|
1097
|
-
|
|
1098
|
-
渐变带由两层组成,配对方式与 layout header 的晕染一致:上面那条 alpha 渐隐之外,同一条带上再叠一层真实的 `backdrop-filter` 模糊,被裁掉的内容因此是被**糊掉**的,而不只是被调成半透明。模糊强度由本盒自己的 `mask-image` 调制——从带子起点为 0 升到后半段满强度、再在裁剪边缘前回到 0:起点不为 0 会在「突然开始模糊」处留一条接缝,末端不为 0 则会一路糊到裁剪边界。展开态活动长度为 0,各色标塌缩到 100% 而末位色标是全透明,于是这一层什么都不画。
|
|
1099
|
-
|
|
1100
|
-
比例设为 `0` 即关闭渐隐。推导是纯 CSS 的 `calc(peek * ratio)`:`peek` 用 rem 时渐变长度自动跟随根字号缩放,JS 侧不需要做任何单位解析。已知限制:`peek` 取百分比时推导结果不是长度、无法被注册属性插值,会静默回落为「无渐隐」,此时请用 `--wui-collapse-peek-edge` 显式指定长度。
|
|
1101
|
-
|
|
1102
|
-
渐变是四段「先陡后缓」而非线性:主体全黑延伸到渐变带起点后,前 30% 距离就降到 60% alpha,65% 处 26%,末端落到边缘颜色。线性渐变的 alpha 在带子前 40% 几乎贴着 1.0,人眼感知到的虚化远短于声明长度——把下降前置后,带子从起点就明显发虚,同样长度的感知长度接近翻倍。
|
|
1103
|
-
|
|
1104
|
-
渐变带位置用百分比声明、相对容器的**当前渲染高度**(即 `max-height` 动画的那个高度),因此收起动画期间渐变带逐帧跟随裁剪边缘移动,`mask-image` 字符串本身无需插值。渐变带长度由注册的 `<length>` 自定义属性驱动,随开合动画平滑淡入淡出,而不是等收起动画落稳态才突然出现。
|
|
1105
|
-
|
|
1106
|
-
```html
|
|
1107
|
-
<!-- 调强渐隐:比例 0.5 → 120px peek 配 60px 渐隐 -->
|
|
1108
|
-
<web-ui-collapse peek="120px" style="--wui-collapse-peek-edge-ratio: 0.5">
|
|
1109
|
-
<button type="button">点击切换</button>
|
|
1110
|
-
<div slot="content">很长的内容——120px 露出区域的底部渐隐更强</div>
|
|
1111
|
-
</web-ui-collapse>
|
|
1112
|
-
```
|
|
1113
|
-
|
|
1114
|
-
**已知限制:** `horizontal` 动画期间内容随宽度变化 reflow。
|
|
1115
|
-
|
|
1116
|
-
---
|
|
1117
|
-
|
|
1118
|
-
### 浮动
|
|
1119
|
-
|
|
1120
|
-
#### `<web-ui-popover>`
|
|
1121
|
-
|
|
1122
|
-
锚定触发元素的弹出层。
|
|
1123
|
-
|
|
1124
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1125
|
-
| ------------------ | ---------------------------------- | ---------- | ---------------------------------- |
|
|
1126
|
-
| `open` | `boolean` | `false` | 弹出层可见性 |
|
|
1127
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
1128
|
-
| `placement` | `Placement` | `'bottom'` | Floating UI 位置 |
|
|
1129
|
-
| `trigger` | `'click' \| 'hover' \| 'manual'` | `'click'` | 触发方式 |
|
|
1130
|
-
| `offset` | `number` | `8` | 与锚点距离 |
|
|
1131
|
-
| `portal` | `boolean` | `false` | 在 theme-owned overlay root 中渲染 |
|
|
1132
|
-
| `overlayContainer` | `HTMLElement \| () => HTMLElement` | — | 显式 Portal 容器 |
|
|
1133
|
-
|
|
1134
|
-
**事件:** `open-change` (`CustomEvent<{ open: boolean }>`)
|
|
1135
|
-
|
|
1136
|
-
**插槽:** `trigger`, `default`
|
|
1137
|
-
|
|
1138
|
-
**方法:** `show()`, `close()`, `toggle()`
|
|
1139
|
-
|
|
1140
|
-
Hover 模式使用 `pointerenter`/`pointerleave` 加延迟控制。Click 模式点击切换。Manual 模式仅响应命令式调用。
|
|
1141
|
-
|
|
1142
|
-
**trigger ARIA:** 开合状态回写到 trigger 元素:`aria-expanded` 与 `aria-controls`(指向面板)设在 `trigger` slot 的首个 assigned element 上(包装结构上的同名属性保留兼容)。trigger slot 请放入可交互元素(原生 button、`web-ui-button`)。
|
|
1143
|
-
|
|
1144
|
-
#### `<web-ui-tooltip>`
|
|
1145
|
-
|
|
1146
|
-
工具提示,支持指针和焦点触发。
|
|
1147
|
-
|
|
1148
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1149
|
-
| ------------------ | ---------------------------------- | ------- | ---------------------------------- |
|
|
1150
|
-
| `placement` | `Placement` | `'top'` | Floating UI 位置 |
|
|
1151
|
-
| `content` | `string` | `''` | 提示文本(替代插槽) |
|
|
1152
|
-
| `open` | `boolean` | `false` | 可见性 |
|
|
1153
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
1154
|
-
| `show-delay` | `number` | `200` | 显示延迟(毫秒) |
|
|
1155
|
-
| `hide-delay` | `number` | `100` | 隐藏延迟(毫秒) |
|
|
1156
|
-
| `offset` | `number` | `6` | 与触发器的距离 |
|
|
1157
|
-
| `portal` | `boolean` | `false` | 在 theme-owned overlay root 中渲染 |
|
|
1158
|
-
| `overlayContainer` | `HTMLElement \| () => HTMLElement` | — | 显式 Portal 容器 |
|
|
1159
|
-
|
|
1160
|
-
**事件:** `open-change` (`CustomEvent<{ open: boolean }>`)
|
|
1161
|
-
|
|
1162
|
-
**插槽:** `default`(触发器)、`content`(提示面板)
|
|
1163
|
-
|
|
1164
|
-
`open` 是自管理的可见性属性。指针/焦点触发会更新它,直接赋值 property 也会同步本地或 Portal 面板;`open-change` 仅用户来源的开关时派发。第一个 Tooltip 显示后,相邻 Tooltip 会立即切换;其余 pointer/focus 触发使用延迟计时器。
|
|
1165
|
-
|
|
1166
|
-
**CSS 自定义属性:**
|
|
1167
|
-
|
|
1168
|
-
| 属性 | 默认值 | 说明 |
|
|
1169
|
-
| ------------------------- | ------- | ------------ |
|
|
1170
|
-
| `--wui-tooltip-max-width` | `240px` | 提示最大宽度 |
|
|
1171
|
-
| `--wui-tooltip-font-size` | `13px` | 提示字号 |
|
|
1172
|
-
|
|
1173
|
-
#### `<web-ui-context-menu>`
|
|
1174
|
-
|
|
1175
|
-
右键上下文菜单。
|
|
1176
|
-
|
|
1177
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1178
|
-
| ------------------ | --------- | ------- | ------------------------------------------------ |
|
|
1179
|
-
| `disabled` | `boolean` | `false` | 右键不打开菜单,不做视觉置灰 |
|
|
1180
|
-
| `no-scroll-lock` | `boolean` | `false` | 允许背景滚动 |
|
|
1181
|
-
| `long-press` | `boolean` | `false` | 触屏长按打开菜单(opt-in;鼠标走 `contextmenu`) |
|
|
1182
|
-
| `long-press-delay` | `number` | `500` | `long-press` 触发前需按住的时长(ms) |
|
|
1183
|
-
|
|
1184
|
-
**事件:** `open-change` (`CustomEvent<{ open: boolean }>`)
|
|
1185
|
-
|
|
1186
|
-
**插槽:** `default`(菜单项内容)
|
|
1187
|
-
|
|
1188
|
-
**方法:** `openAt(x: number, y: number)`, `close()`
|
|
1189
|
-
|
|
1190
|
-
**CSS 自定义属性:**
|
|
1191
|
-
|
|
1192
|
-
| 属性 | 默认值 | 说明 |
|
|
1193
|
-
| ------------------------------------- | ---------------------------------- | ---------------------------- |
|
|
1194
|
-
| `--wui-context-menu-safe-area-bottom` | `env(safe-area-inset-bottom, 0px)` | 触屏菜单需要让开的底部 inset |
|
|
1195
|
-
|
|
1196
|
-
通过 `contextmenu` 事件打开。菜单项:`web-ui-dropdown-item`、`web-ui-dropdown-divider`、`web-ui-dropdown-header`。支持键盘导航和子菜单 hover。
|
|
1197
|
-
|
|
1198
|
-
菜单打开期间是模态的:它渲染进浏览器 top layer 里一个透明的 `<dialog>`,下层页面拿不到任何命中测试。因此「点外面」只会关掉菜单,不会顺带把下面那一行、那个 checkbox、那个链接一起激活。scrim 刻意保持完全透明——它的职责是吃掉这次命中,不是给页面加一层遮罩。另有一处需要知道的代价:菜单项激活后约 160ms 的退场过渡里,scrim 已经提前出了 top layer,下层页面在这段时间重新可点,菜单的层级也退回普通流。
|
|
1199
|
-
|
|
1200
|
-
`long-press` 是 opt-in 且只针对触屏:指针按住 `long-press-delay`(默认 `500` ms,与平台原生长按一致)后打开菜单,走与右键完全相同的路径。它只响应 `pointerType === 'touch'`;按住期间位移超过 10px 即取消,因此滚动不会误开菜单。由于浏览器会把同一次按住也识别为原生手势,长按打开后引擎补发的 `contextmenu` 与 `click` 会被吸收,既不会重复打开,也不会把刚打开的菜单立刻 light-dismiss 掉。
|
|
1201
|
-
|
|
1202
|
-
`long-press` 打开的菜单从按点**向下**展开:上缘对齐按点 `y`、水平居中于按点 `x`,与 iOS / Android 原生长按菜单的形态一致。下方空间不够时面板翻转到按点上方而不是跑出屏幕,两轴都会被夹回视口内,并让开 `--wui-context-menu-safe-area-bottom`,避免最下面一项落进 home indicator。分流依据是**输入方式**而非视口宽度:窄窗口里的鼠标右键仍然贴着光标左上角展开。两者只有水平对齐量不同 —— 定位走的是同一趟 `computePosition`,因此不会各自漂移。
|
|
1203
|
-
|
|
1204
|
-
`disabled` 只抑制菜单行为:右键与 `openAt()` 都不打开菜单。它不会给 default slot 里的内容置灰或改色——组件自身根本不渲染菜单面,没有可置灰的对象;禁用右键菜单也不等于禁用触发区。需要让禁用态在视觉上可读时,请在自己的内容上表达。
|
|
1205
|
-
|
|
1206
|
-
菜单打开期间,Consumer可以安全地使用条件渲染(如 Vue `v-if`)切换、移动或删除菜单项,无需重新插入到宿主元素;portal 内的变更会自动 reconcile,关闭时框架锚点随元素迁回宿主,保证后续框架更新正常。
|
|
1207
|
-
|
|
1208
|
-
关闭时,焦点会归还给菜单打开前持焦的元素。这条是浏览器模态 dialog 自带的行为,而不是组件自己实现的——也正因如此,它只在菜单还在 top layer 时才生效。菜单项激活是唯一会提前交还焦点的路径:在点击那一刻就还,退场过渡之后不再过问,于是菜单项回调若把焦点移到别处(例如就地进入编辑态),焦点就留在那里了。关闭过程不会再把焦点从新的焦点目标上拽走,因此按 blur 提交的那类组件也不会因此提交一次没改过的编辑。
|
|
1209
|
-
|
|
1210
|
-
---
|
|
1211
|
-
|
|
1212
|
-
### 菜单
|
|
1213
|
-
|
|
1214
|
-
#### `<web-ui-dropdown>`
|
|
1215
|
-
|
|
1216
|
-
下拉菜单,支持多级子菜单。
|
|
1217
|
-
|
|
1218
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1219
|
-
| ---------------- | ----------- | ---------------- | -------------------- |
|
|
1220
|
-
| `open` | `boolean` | `false` | 菜单可见性 |
|
|
1221
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
1222
|
-
| `placement` | `Placement` | `'bottom-start'` | Floating UI 位置 |
|
|
1223
|
-
| `offset` | `number` | `4` | 与触发器距离 |
|
|
1224
|
-
| `match-width` | `boolean` | `false` | 匹配触发器宽度 |
|
|
1225
|
-
| `no-scroll-lock` | `boolean` | `false` | 打开时不锁定页面滚动 |
|
|
1226
|
-
|
|
1227
|
-
**事件:** `open-change` (`CustomEvent<{ open: boolean }>`)
|
|
1228
|
-
|
|
1229
|
-
**插槽:** `trigger`, `default`(菜单项)
|
|
1230
|
-
|
|
1231
|
-
**方法:** `openMenu()`, `closeAll()`
|
|
1232
|
-
|
|
1233
|
-
菜单项:`web-ui-dropdown-item`、`web-ui-dropdown-divider`、`web-ui-dropdown-header`。子菜单在 `web-ui-dropdown-item` 上通过 `submenu` 属性启用。完整键盘导航支持。
|
|
1234
|
-
|
|
1235
|
-
**trigger ARIA:** `aria-haspopup="menu"`、`aria-expanded` 与 `aria-controls`(指向根菜单面板)回写到 `trigger` slot 的首个 assigned element。trigger slot 请放入可交互元素。
|
|
1236
|
-
|
|
1237
|
-
#### `<web-ui-dropdown-item>`
|
|
1238
|
-
|
|
1239
|
-
菜单项。
|
|
1240
|
-
|
|
1241
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1242
|
-
| ---------- | --------- | ------- | ------------ |
|
|
1243
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
1244
|
-
| `submenu` | `boolean` | `false` | 是否有子菜单 |
|
|
1245
|
-
| `value` | `string` | `''` | 菜单项值 |
|
|
1246
|
-
| `pl` | `string` | `''` | 左侧内边距 |
|
|
1247
|
-
|
|
1248
|
-
**插槽:** `prefix`, `default`, `suffix`
|
|
1249
|
-
|
|
1250
|
-
**方法:** `focusItem()`
|
|
1251
|
-
|
|
1252
|
-
角色:`menuitem`。
|
|
1253
|
-
|
|
1254
|
-
#### `<web-ui-dropdown-divider>`
|
|
1255
|
-
|
|
1256
|
-
菜单分隔线。角色:`separator`。无属性。
|
|
1257
|
-
|
|
1258
|
-
#### `<web-ui-dropdown-header>`
|
|
1259
|
-
|
|
1260
|
-
菜单分组标题。无属性。插槽:`default`(文本内容)。
|
|
1261
|
-
|
|
1262
|
-
---
|
|
1263
|
-
|
|
1264
|
-
### 数据展示
|
|
1265
|
-
|
|
1266
|
-
#### `<web-ui-avatar>`
|
|
1267
|
-
|
|
1268
|
-
头像组件,支持图片加载失败回退。
|
|
1269
|
-
|
|
1270
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1271
|
-
| ------- | ---------------------- | ---------- | ---------------------- |
|
|
1272
|
-
| `src` | `string` | `''` | 图片 URL |
|
|
1273
|
-
| `alt` | `string` | `''` | 替代文本 |
|
|
1274
|
-
| `name` | `string` | `''` | 展示名称(首字母回退) |
|
|
1275
|
-
| `size` | `number` | `40` | 头像尺寸(px) |
|
|
1276
|
-
| `shape` | `'circle' \| 'square'` | `'circle'` | 形状 |
|
|
1277
|
-
|
|
1278
|
-
**插槽:** `default`(图片加载失败的降级内容)
|
|
1279
|
-
|
|
1280
|
-
#### `<web-ui-badge>`
|
|
1281
|
-
|
|
1282
|
-
徽标 / 通知计数。
|
|
1283
|
-
|
|
1284
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1285
|
-
| -------------- | -------------------------------------------------------------- | ------------- | ---------------------- |
|
|
1286
|
-
| `count` | `number` | `0` | 显示数字 |
|
|
1287
|
-
| `max` | `number` | `99` | 最大值(超过显示 99+) |
|
|
1288
|
-
| `dot` | `boolean` | `false` | 点模式(不显示数字) |
|
|
1289
|
-
| `show-zero` | `boolean` | `false` | count 为 0 时也显示 |
|
|
1290
|
-
| `badge-hidden` | `boolean` | `false` | 完全隐藏 |
|
|
1291
|
-
| `offset-x` | `number` | `0` | 水平偏移 |
|
|
1292
|
-
| `offset-y` | `number` | `0` | 垂直偏移 |
|
|
1293
|
-
| `placement` | `'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left'` | `'top-right'` | 徽标位置 |
|
|
1294
|
-
|
|
1295
|
-
#### `<web-ui-empty>`
|
|
1296
|
-
|
|
1297
|
-
空状态占位。
|
|
1298
|
-
|
|
1299
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1300
|
-
| ------------- | -------- | ------ | --------------------------------------------- |
|
|
1301
|
-
| `title` | `string` | `''` | 标题 |
|
|
1302
|
-
| `description` | `string` | `''` | 描述文本 |
|
|
1303
|
-
| `size` | `number` | `56` | 版面缩放基准(px);非法值或非正数回退为 `56` |
|
|
1304
|
-
|
|
1305
|
-
`size` 是整个空态的单一旋钮:它驱动图标盒、`min-height`、内边距与标题/描述字号,因此大图标不再配 medium 留白:
|
|
1306
|
-
|
|
1307
|
-
| `size` | 图标盒 | 字形 | `min-height` | 内边距 块/行 | 标题 | 描述 |
|
|
1308
|
-
| ------ | ------ | ------ | ------------ | ------------ | ------ | ------ |
|
|
1309
|
-
| `40` | `40px` | `17px` | `171px` | `23px/17px` | `14px` | `13px` |
|
|
1310
|
-
| `56` | `56px` | `24px` | `240px` | `32px/24px` | `16px` | `14px` |
|
|
1311
|
-
| `72` | `72px` | `31px` | `309px` | `41px/31px` | `16px` | `14px` |
|
|
1312
|
-
|
|
1313
|
-
派生规则是 `min-height: round(size * 30 / 7)`、`padding-block: round(size * 4 / 7)`、`padding-inline: round(size * 3 / 7)`,默认字形是 `round(size * 3 / 7)`。
|
|
1314
|
-
|
|
1315
|
-
字号只有一档台阶而不是连续缩放:低于 `56` 时标题 `14px`、描述 `13px`,`56` 及以上为 `16px` 与 `14px`。更大的空态靠留白撑开,而不是靠字变大。
|
|
1316
|
-
|
|
1317
|
-
**从 `'small' \| 'medium' \| 'large'` 枚举迁移:** `size` 现在是数字。被删除的档位是手调值,这套比例只能逼近而复现不了——`small` 原本是 `160px` 高、`20px 16px` 内边距,`large` 原本是 `320px` 与 `48px 32px`。段间距与内容宽度从未跟随档位。
|
|
1318
|
-
|
|
1319
|
-
| 旧值 | 新值 | 默认字形 |
|
|
1320
|
-
| -------- | ---- | -------- |
|
|
1321
|
-
| `small` | `40` | `17` |
|
|
1322
|
-
| `medium` | `56` | `24` |
|
|
1323
|
-
| `large` | `72` | `31` |
|
|
1324
|
-
|
|
1325
|
-
**插槽:** `default`(标题,覆盖 `title` 属性)、`icon`、`description`、`action`
|
|
1326
|
-
|
|
1327
|
-
**CSS 自定义属性:**
|
|
1328
|
-
|
|
1329
|
-
每一项都覆盖 `size` 派生的值,因此八个属性始终优先——包括两个字号。默认值取默认 `size` 为 `56` 时的取值。
|
|
1330
|
-
|
|
1331
|
-
| 属性 | 默认值 | 说明 |
|
|
1332
|
-
| ----------------------------------- | ----------- | --------------------------- |
|
|
1333
|
-
| `--wui-empty-min-height` | `240px` | 最小高度 |
|
|
1334
|
-
| `--wui-empty-padding` | `32px 24px` | 内边距 |
|
|
1335
|
-
| `--wui-empty-icon-size` | `56px` | 图标容器尺寸,优先于 `size` |
|
|
1336
|
-
| `--wui-empty-content-width` | `480px` | 标题/描述/操作区最大宽度 |
|
|
1337
|
-
| `--wui-empty-title-margin-top` | `16px` | 图标与标题之间的间距 |
|
|
1338
|
-
| `--wui-empty-title-font-size` | `16px` | 标题字号 |
|
|
1339
|
-
| `--wui-empty-description-font-size` | `14px` | 描述字号 |
|
|
1340
|
-
| `--wui-empty-action-margin-top` | `20px` | 操作区上方的间距 |
|
|
1341
|
-
|
|
1342
|
-
#### `<web-ui-middle-ellipsis>`
|
|
1343
|
-
|
|
1344
|
-
单行中间省略:保留首尾、丢掉中间——`very-long-file-…-abcdefghij.txt`——而 `text-overflow: ellipsis` 只能保住头部。
|
|
1345
|
-
|
|
1346
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1347
|
-
| ----------------- | -------- | ------ | -------------------------------------------------------------------------------------------------------- |
|
|
1348
|
-
| `text` | `string` | `''` | 完整文本。组件只读它,不回写。 |
|
|
1349
|
-
| `marker` | `string` | `'…'` | 插在两端之间的标记。空串即截断但不给任何可见信号。 |
|
|
1350
|
-
| `marker-position` | `number` | `50` | 标记所在的横向比例,0–100。`0` 贴行末(只剩头部),`100` 贴行首(只剩尾部)。越界夹紧,`NaN` 回退 `50`。 |
|
|
1351
|
-
|
|
1352
|
-
使用条件与 `text-overflow: ellipsis` 相同:**单行、且有确定行内尺寸**。块级盒子、flex 行里的 `flex: 1; min-width: 0`、grid 轨道都可以。宿主按自身内容撑宽时不存在可用空间,组件按量到的宽度切一次就停止继续收缩,而不是一路塌到只剩标记。
|
|
1353
|
-
|
|
1354
|
-
文件名场景靠 `marker-position` 调节,头部是路径噪声、尾部才是扩展名时取大值。下表是示意而非可复现的取值——确切切点取决于字体——取自 16px `system-ui`、240px 盒子:
|
|
1355
|
-
|
|
1356
|
-
| `marker-position` | 240px 盒子里 |
|
|
1357
|
-
| ----------------- | --------------------------------- |
|
|
1358
|
-
| `0` | `very-long-file-name-abcdefghi…` |
|
|
1359
|
-
| `50`(默认) | `very-long-file-…-abcdefghij.txt` |
|
|
1360
|
-
| `80` | `very-l…file-name-abcdefghij.txt` |
|
|
1361
|
-
|
|
1362
|
-
切点一律落在**字素簇**边界上:代理对、组合符序列、ZWJ emoji 都不会被从中间劈开。截断期间完整原文写在文本元素的 `title` 上,放得下之后移除。宿主需为 `direction: ltr`;`direction: rtl` 下合串会被双向算法重排。
|
|
1363
|
-
|
|
1364
|
-
**已知限制——复制与辅助技术。** 元素里装的是屏幕上那串,因此选中复制拿到的是截断后的文本,读屏与页内查找命中的也是截断后的串。CSS 的 `text-overflow` 没有这两个问题(它从不改动文本节点),这是用 JS 算切点绕不开的代价;原文经 `title` 提供。保持全文留在 DOM 里的替代方案及其代价见 `docs/research/web-ui-middle-ellipsis-261007.md`。
|
|
1365
|
-
|
|
1366
|
-
#### `<web-ui-icon>`
|
|
1367
|
-
|
|
1368
|
-
图标渲染组件。接受 Iconify 数据对象。
|
|
1369
|
-
|
|
1370
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1371
|
-
| ------- | ------------- | ------- | ------------------------ |
|
|
1372
|
-
| `.icon` | `IconifyIcon` | — | 图标数据(Lit 属性绑定) |
|
|
1373
|
-
| `size` | `number` | `18` | 尺寸(px) |
|
|
1374
|
-
| `color` | `string` | — | 颜色 |
|
|
1375
|
-
| `spin` | `boolean` | `false` | 旋转动画 |
|
|
1376
|
-
|
|
1377
|
-
内置 `aria-hidden="true"`。
|
|
1378
|
-
|
|
1379
|
-
**画布:** SVG 的 `viewBox` 取自 `icon.left`/`icon.top`/`icon.width`/`icon.height`,偏移缺省为 `0`,尺寸缺省为 Iconify 规范默认的 `16`。生成的资产都自带画布,因此尺寸兜底只在直接传入原始 `IconifyIcon` 对象时才会生效。
|
|
1380
|
-
|
|
1381
|
-
```js
|
|
1382
|
-
import { lucideLoaderCircle } from '@greypan/web-ui/icons'
|
|
1383
|
-
html`<web-ui-icon .icon=${lucideLoaderCircle} spin />`
|
|
1384
|
-
```
|
|
1385
|
-
|
|
1386
|
-
**CSS 自定义属性:**
|
|
1387
|
-
|
|
1388
|
-
| 属性 | 默认值 | 说明 |
|
|
1389
|
-
| --------------------- | --------- | ----------------------------------------------- |
|
|
1390
|
-
| `--wui-icon-color` | `inherit` | 图标颜色 |
|
|
1391
|
-
| `--wui-duration-spin` | `600ms` | `spin` 旋转周期;reduce 下放慢到 `1600ms`,不停 |
|
|
1392
|
-
|
|
1393
|
-
#### `<web-ui-spinner>`
|
|
1394
|
-
|
|
1395
|
-
加载旋转器。
|
|
1396
|
-
|
|
1397
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1398
|
-
| ------------- | -------- | ------ | ---------- |
|
|
1399
|
-
| `size` | `number` | `24` | 尺寸(px) |
|
|
1400
|
-
| `color` | `string` | — | 颜色 |
|
|
1401
|
-
| `description` | `string` | `''` | 描述文字 |
|
|
1402
|
-
|
|
1403
|
-
角色:`status`,`aria-label="加载中"`。
|
|
1404
|
-
|
|
1405
|
-
**插槽:** `description`
|
|
1406
|
-
|
|
1407
|
-
**静态 API:**
|
|
1408
|
-
|
|
1409
|
-
```ts
|
|
1410
|
-
import { WebUiSpinner } from '@greypan/web-ui'
|
|
1411
|
-
|
|
1412
|
-
WebUiSpinner.show() // 显示
|
|
1413
|
-
WebUiSpinner.show({ size: 32, duration: 2000 }) // 带选项显示
|
|
1414
|
-
WebUiSpinner.hide() // 隐藏
|
|
1415
|
-
```
|
|
1416
|
-
|
|
1417
|
-
`WebUiSpinner.show(options?: { size?: number; duration?: number; description?: string })`
|
|
1418
|
-
|
|
1419
|
-
**CSS 自定义属性:**
|
|
1420
|
-
|
|
1421
|
-
| 属性 | 默认值 | 说明 |
|
|
1422
|
-
| ------------------------ | ------- | -------------------------------------------- |
|
|
1423
|
-
| `--wui-spinner-size` | `24px` | 尺寸,由 `size` 属性写入 |
|
|
1424
|
-
| `--wui-duration-spinner` | `800ms` | 叶片追光周期;reduce 下放慢到 `1600ms`,不停 |
|
|
1425
|
-
|
|
1426
|
-
---
|
|
1427
|
-
|
|
1428
|
-
### 布局与工具
|
|
1429
|
-
|
|
1430
|
-
#### `<web-ui-layout>`
|
|
1431
|
-
|
|
1432
|
-
响应式页面布局:支持可选全宽 Banner、桌面端可折叠侧边栏,以及移动端默认 drawer。页面本身滚动;Banner 滚出后,桌面端 sidebar 和 header 固定在视口内。
|
|
1433
|
-
|
|
1434
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1435
|
-
| ------------------- | --------- | --------- | -------------------------------------------------------------- |
|
|
1436
|
-
| `sidebar-collapsed` | `boolean` | `false` | 桌面端侧边栏受控折叠状态 |
|
|
1437
|
-
| `sidebar-open` | `boolean` | `false` | 移动端侧边栏 Drawer 受控打开状态 |
|
|
1438
|
-
| `header-glow` | `boolean` | `false` | 在 header 插槽内容背后显示装饰性晕染 |
|
|
1439
|
-
| `mobile` | `boolean` | `false` | 只读派生值:当前视口是否处于移动端断点及以下 |
|
|
1440
|
-
| `sidebar-width` | `string` | `'240px'` | 桌面端和移动端展开时的侧边栏宽度 |
|
|
1441
|
-
| `collapsed-width` | `string` | `'72px'` | 桌面端折叠时的侧边栏宽度 |
|
|
1442
|
-
| `sidebar-resizable` | `boolean` | `false` | 启用桌面端侧边栏右边缘拖拽调整宽度 |
|
|
1443
|
-
| `sidebar-min-width` | `string` | — | 拖拽调整的下限(px);默认回退到 `collapsed-width` |
|
|
1444
|
-
| `sidebar-max-width` | `string` | — | 拖拽调整的上限(px);钳制在视口一半以内,内置上限优先于配置值 |
|
|
1445
|
-
|
|
1446
|
-
**事件:** `sidebar-collapsed-change`(`CustomEvent<{ collapsed: boolean }>`)用于请求更新桌面端折叠状态;`sidebar-open-change`(`CustomEvent<{ open: boolean }>`)用于请求更新移动端 Drawer 打开状态;`sidebar-width-change`(`CustomEvent<{ width: string }>`)用于请求在拖拽调整结束后更新侧边栏宽度。Consumer 必须将请求值回写到对应的受控属性。`mobile-change`(`CustomEvent<{ mobile: boolean }>`)不是请求,它报告的是 layout 自己的派生视口状态。
|
|
1447
|
-
|
|
1448
|
-
**移动端视口状态:** `mobile` 是派生输出而不是第二个输入——由 layout 独占写入;property 只读(赋值不生效,严格模式下抛错),写 attribute 会在同一次 reaction 内被恢复、连值一起归位,两种写法都不派发事件。它与 layout 的渲染分支、以及 layout CSS 用来隐藏桌面 aside 的媒体查询(`@media (width <= 640px)`)是同一条条件,三者不可能互相错开;翻转不再经 resize 去抖,就发生在媒体查询变化的那一刻。`mobile-change` 每次跨断点只派发一次,元素在窄视口挂载时连接那一刻的那次求值也算一次。Consumer 用 `layout.mobile` 读当前值、用 `mobile-change` 收更新即可,CSS 侧可以写 `web-ui-layout[mobile]`。
|
|
1449
|
-
|
|
1450
|
-
**框架侧的消费方式:** layout 自身在 microtask 里重渲染,因此**在宏任务上响应的消费者会读到旧值,而新树已经在屏幕上了**:
|
|
1451
|
-
|
|
1452
|
-
- **Vue**:模板里绑 `@mobile-change`,用 `ref` 存值。Vue 在元素插入前就挂上模板监听(因此也能收到连接期那一次),且它的 flush 在 microtask,早于 layout 自己的更新。
|
|
1453
|
-
- **React**:用 ref 拿到元素,在 `useLayoutEffect` 里**先读 `layout.mobile` 再订阅**,并用 `flushSync` 回写。先读是为了拿到「挂在插入之后的监听收不到」的挂载初值;`flushSync` 是为了让运行时跨断点抢在 layout 重渲染之前——React 对 DOM 监听里的 setState 走宏任务 flush,否则会出现一帧「drawer 已按新状态渲染、consumer 的值还是旧的」(本仓 React demo 修复前实测 2/221 帧)。
|
|
1454
|
-
- **CSS**:直接按 `web-ui-layout[mobile]` 写样式,或把它包成一个变体。Tailwind v4 的 app 可以只写一次这条边界的**移动端那一侧**、而不是每个工具类各写一遍:`@custom-variant mobile (&:where(web-ui-layout[mobile], web-ui-layout[mobile] *));`。两条限制:属性选择器只够得到 layout 子树内的节点,被 overlay portal 到 `document.body` 的内容仍需媒体查询;**不要拿自定义变体做 min-width 那一侧**——Tailwind 把 `@custom-variant` 规则排在主题断点之后,同属性的 `desktop:` 变体会在它之上的每个宽度静默压掉 `md:`/`lg:`。
|
|
1455
|
-
|
|
1456
|
-
**折叠只作用于桌面端:** `sidebar-collapsed` 只收窄桌面端侧边栏,布局不对移动端 Drawer 施加任何折叠几何。两条分支承载的是同一份 `sidebar` 插槽内容,所以侧边栏内容密度跟着该属性变的 Consumer 必须按 `mobile` 收窄这个条件(例如 `collapsed && !layout.mobile`,或 CSS 的 `web-ui-layout[mobile]`),否则桌面的折叠偏好会一路渲染进 Drawer。见 issue #195。
|
|
1457
|
-
|
|
1458
|
-
**侧边栏调整宽度:** 启用 `sidebar-resizable` 后,桌面端侧边栏右边缘会出现调整手柄(折叠状态下隐藏);悬停或拖拽时显示 3px 宽的强调色垂直线和 `col-resize` 光标。
|
|
1459
|
-
|
|
1460
|
-
- 拖拽时实时更新宽度(禁止过渡动画,限制在 `[min, max]` 和视口范围内)。
|
|
1461
|
-
- 释放时触发 `sidebar-width-change` 事件并携带最终像素宽度,Consumer 回写后宽度重新由 `sidebar-width` 属性接管;`pointercancel` 会恢复属性控制的宽度且不触发事件。
|
|
1462
|
-
- 键盘操作(WAI-ARIA splitter 模式):聚焦后用 ←/→ 以 16px 步进调整(Shift 加速到 64px),Home/End 跳到 min/max,Enter 以同一 `sidebar-width-change` 请求提交,Escape 撤回未提交的调整。
|
|
1463
|
-
- 移动端 Drawer 始终通过其内置 `draggable` 抽屉支持拖拽关闭。
|
|
1464
|
-
|
|
1465
|
-
| 插槽 | 说明 |
|
|
1466
|
-
| --------- | ---------------------------------------------------------------- |
|
|
1467
|
-
| `banner` | 位于布局主体上方的可选全宽 Banner |
|
|
1468
|
-
| `header` | 内容区的 sticky header |
|
|
1469
|
-
| `sidebar` | 侧边栏卡片内容;桌面端内部固定区域与滚动容器由 Consumer 自行定义 |
|
|
1470
|
-
| `default` | 主内容区 |
|
|
1471
|
-
| `tabbar` | 底部 tabbar |
|
|
1472
|
-
|
|
1473
|
-
`web-ui-layout` 只约束侧边栏卡片的可用空间并管理桌面端 Toggle,不创建桌面端侧边栏 scrollport。若仅让桌面端侧边栏的一部分滚动,请将 `sidebar` 插槽根节点设为 `height: 100%; min-height: 0` 的 flex column,再将 `overflow-y: auto` 设置到目标子元素。这样 Consumer 可自行固定头部和底部,无需额外的公共 slot:
|
|
1474
|
-
|
|
1475
|
-
```html
|
|
1476
|
-
<div slot="sidebar" class="sidebar-root">
|
|
1477
|
-
<div class="sidebar-title">组件列表</div>
|
|
1478
|
-
<nav class="sidebar-nav">...</nav>
|
|
1479
|
-
</div>
|
|
1480
|
-
```
|
|
1481
|
-
|
|
1482
|
-
```css
|
|
1483
|
-
.sidebar-root {
|
|
1484
|
-
display: flex;
|
|
1485
|
-
flex-direction: column;
|
|
1486
|
-
height: 100%;
|
|
1487
|
-
min-height: 0;
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
|
-
.sidebar-title {
|
|
1491
|
-
flex-shrink: 0;
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
.sidebar-nav {
|
|
1495
|
-
flex: 1;
|
|
1496
|
-
min-height: 0;
|
|
1497
|
-
overflow-y: auto;
|
|
1498
|
-
}
|
|
1499
|
-
```
|
|
1500
|
-
|
|
1501
|
-
在 `640px` 及以下——与 `mobile` 报告的正是同一条条件——侧边栏会切换为使用内置 glass body、可滚动 content 和 drag zone 的 `web-ui-drawer`。Layout 会将 `sidebar-width` 映射为 `--wui-drawer-width`,将 `--wui-layout-sidebar-radius` 映射为 `--wui-drawer-radius`。移动端 Toggle 以 glass 变体位于 header 行内。其左缩进默认 `8px`,可通过 `--wui-layout-mobile-toggle-inset` 与 Consumer 自身的 header 内边距对齐。
|
|
1502
|
-
|
|
1503
|
-
`header-glow` 会在 header 插槽内容和移动端 Toggle 的背后添加 `pointer-events: none` 的装饰性晕染。它属于 Header 背景而非前景层,因此插槽内容始终位于其上方。晕染颜色取自 `--wui-color-page`,因此浅色/暗色模式自动跟随;要改颜色,在 `web-ui-layout`、theme 或其上层任意位置覆盖该属性即可。晕染由两层伪元素合成:`::before` 在 header 盒内用 `linear-gradient` 绘制底色并渐变到全透明,`::after` 再叠一层真实的 `backdrop-filter: blur(4px)`,并用 `mask` 让这层模糊的 alpha 沿高度衰减、到底缘附近归零。两层职责分开后,模糊强度与颜色浓度互不牵制。两层都用负 `margin` 而不是 `transform` 撑开;模糊层止于 header 上缘,只向下与向两侧延伸。横向裁剪归属 `.layout-content`,glow 因此不会撑出横向滚动条。布局层级顺序为 Header(`10`)< Auxiliary(`20`)< Banner(`30`)< Tabbar(`40`)< Sidebar(`50`)。
|
|
1504
|
-
|
|
1505
|
-
侧边栏卡片表面使用 `--wui-color-surface-sidebar`:浅色保持半透明中性分层,深色比 `--wui-color-page` 浅一档、与 `--wui-color-surface` 同级。它独立成 token,是因为 dialog、drawer 和 toast 共用 `--wui-color-surface-overlay`,可以采用不同表面。
|
|
1506
|
-
|
|
1507
|
-
**CSS 自定义属性:**
|
|
1508
|
-
|
|
1509
|
-
| 属性 | 默认值 | 说明 |
|
|
1510
|
-
| ----------------------------------- | ------ | ------------------------------------------------------------------- |
|
|
1511
|
-
| `--wui-layout-sidebar-radius` | `24px` | 侧边栏卡片圆角(桌面端和移动端共用) |
|
|
1512
|
-
| `--wui-layout-mobile-toggle-inset` | `8px` | 移动端 header Toggle 的左缩进 |
|
|
1513
|
-
| `--wui-layout-sidebar-toggle-width` | `44px` | 桌面端侧边栏折叠 Toggle 的宽度,下限为 `--wui-control-size`(36px) |
|
|
1514
|
-
|
|
1515
|
-
#### `<web-ui-back-top>`
|
|
1516
|
-
|
|
1517
|
-
回到顶部按钮。
|
|
1518
|
-
|
|
1519
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1520
|
-
| ----------------- | ----------------------- | ---------- | ------------------ |
|
|
1521
|
-
| `scroll-behavior` | `'smooth' \| 'auto'` | `'smooth'` | 滚动行为 |
|
|
1522
|
-
| `threshold` | `number` | `200` | 显示按钮的滚动阈值 |
|
|
1523
|
-
| `visible` | `boolean` | `false` | 当前可见状态 |
|
|
1524
|
-
| `scrollTarget` | `HTMLElement \| Window` | `window` | 滚动容器 |
|
|
1525
|
-
|
|
1526
|
-
**插槽:** `default`(自定义按钮内容)
|
|
1527
|
-
|
|
1528
|
-
**方法:** `toTop()`
|
|
1529
|
-
|
|
1530
|
-
**定位:** `scrollTarget` 为 `window` 时按钮固定在视口角落;为 `HTMLElement` 时需将元素放置在容器内部,按钮通过 `position: sticky` 悬浮于容器底部角落。偏移量沿用 `--wui-back-top-top/right/bottom/left` CSS 变量。
|
|
1531
|
-
|
|
1532
|
-
角色:`button`,键盘 Enter 触发回到顶部。
|
|
1533
|
-
|
|
1534
|
-
**CSS 自定义属性:**
|
|
1535
|
-
|
|
1536
|
-
| 属性 | 默认值 | 说明 |
|
|
1537
|
-
| ------------------------- | -------------------------------- | -------- |
|
|
1538
|
-
| `--wui-back-top-position` | `fixed` | CSS 定位 |
|
|
1539
|
-
| `--wui-back-top-z-index` | `var(--wui-layer-auxiliary, 20)` | 层级 |
|
|
1540
|
-
| `--wui-back-top-top` | `auto` | 上偏移 |
|
|
1541
|
-
| `--wui-back-top-right` | `20px` | 右偏移 |
|
|
1542
|
-
| `--wui-back-top-bottom` | `20px` | 下偏移 |
|
|
1543
|
-
| `--wui-back-top-left` | `auto` | 左偏移 |
|
|
1544
|
-
|
|
1545
|
-
#### `<web-ui-svg-draw-lines>`
|
|
1546
|
-
|
|
1547
|
-
SVG 线条绘制动画,基于 `stroke-dashoffset`。直接在原元素上动画 —— 不克隆、不操作 DOM。两个播放方向:默认按从无到有描出几何形状,`replay({ reverse: true })` 沿原路收回,从有到无。
|
|
1548
|
-
|
|
1549
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1550
|
-
| ------------- | --------- | ---------- | ---------------------------------------- |
|
|
1551
|
-
| `duration` | `number` | `1000` | 动画时长(毫秒),限制在 `[0, 30000]` |
|
|
1552
|
-
| `easing` | `string` | `'linear'` | CSS 缓动函数,传递给 `element.animate()` |
|
|
1553
|
-
| `no-autoplay` | `boolean` | `false` | 关闭内容首次出现时的自动播放 |
|
|
1554
|
-
|
|
1555
|
-
三个属性均会反射(reflected)。
|
|
1556
|
-
|
|
1557
|
-
**自动播放:** slot 内容第一次稳定时自动播放一次画入。设置 `no-autoplay` 后保持资产原样、由调用方决定何时播放 —— `<web-ui-checkbox>` 正是靠它避免挂载时就已勾选的勾在页面首帧画出来。
|
|
1558
|
-
|
|
1559
|
-
**方法:** `replay(options?: { reverse?: boolean }): Promise<void>` — 取消当前动画,重新从 DOM 收集几何元素并开始新动画。所有目标以相同的 duration/easing 并行播放。动画全部完成后 resolve。收回段以「空白」收尾,并把这一末态留在 DOM 上:动画本身在收尾时撤销,但让描边隐去的 dash 值作为内联样式保留,直到下一次 `replay()` 把它替换或撤掉。画入段则恢复消费者自己写的内联样式。收回总是从完整描边起收,所以在画入途中打断也会把整条路径描回去,而不是只收掉看得见的那一段。最近主题范围为 `motion="reduced"` 时不启动任何动画 —— 但上一次收回留下的空白仍会被撤掉,几何回到资产原样,可见性完全交给消费者自己的样式;`motion="system"` 在匹配 `prefers-reduced-motion: reduce` 时行为相同。
|
|
1560
|
-
|
|
1561
|
-
**收回末态:** 收回留下的空白是写在被动画的几何元素上的内联样式,包括嵌套开放 Shadow Root(如 `<web-ui-icon>`)里的节点。克隆这些节点(`cloneNode`、`innerHTML` 往返)会把空白一起带走;把节点摘下来再挂回去,描边也仍然是隐去的 —— 收回结束是一个 DOM 状态而不是一条还活着的动画,没有留下任何东西去复原它。要重新显示就调用 `replay()`。由于组件会递归进入嵌套的开放 Shadow Root,两层 `<web-ui-svg-draw-lines>` 不得覆盖同一批几何元素:每个实例只在第一次动画某个元素时记录它的内联 dash 值,内层留下的空白会被外层当成「消费者自己写的值」。
|
|
1562
|
-
|
|
1563
|
-
**插槽:** `default` — 需要动画的 SVG 内容。接受内联 `<svg>` 元素(light DOM)以及将 SVG 渲染在开放 Shadow DOM 中的组件(如 `<web-ui-icon>`)。closed Shadow Root 被跳过。
|
|
1564
|
-
|
|
1565
|
-
递归遍历 light DOM 和所有开放 Shadow Root,查找 `path`、`rect`、`circle`、`line`、`polyline`、`polygon`、`ellipse` 元素。以 `Z`/`z` 结尾的 `<path>` 会临时应用缺口修复逻辑确保闭合段正确渲染。画入段完成或取消后恢复所有内联样式。
|
|
1566
|
-
|
|
1567
|
-
#### `<web-ui-theme>`
|
|
1568
|
-
|
|
1569
|
-
主题提供者,定义 CSS 自定义属性 token。
|
|
1570
|
-
|
|
1571
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1572
|
-
| --------------------- | --------------------------------- | ---------- | ------------------------------ |
|
|
1573
|
-
| `appearance` | `'light' \| 'dark' \| 'system'` | `'light'` | 配色方案 |
|
|
1574
|
-
| `resolved-appearance` | `'light' \| 'dark'` | `'light'` | 解析后的配色方案;只读反射输出 |
|
|
1575
|
-
| `motion` | `'full' \| 'reduced' \| 'system'` | `'system'` | 当前嵌套主题范围的动效偏好 |
|
|
1576
|
-
|
|
1577
|
-
**方法:** `getOverlayRoot()` — 返回该主题拥有的 theme-owned overlay root
|
|
1578
|
-
|
|
1579
|
-
**Portal 挂载契约:** 每个 active `<web-ui-theme>` 同时是默认的 scoped theme-owned overlay root。Portal 类组件在未显式传入 `overlayContainer` 时,会解析到最近的 active theme 的 `getOverlayRoot()`;无 target 的调用优先使用 root theme 的 theme-owned overlay root。没有 active theme 提供 root 时,回退到全局 fallback overlay root。
|
|
1580
|
-
|
|
1581
|
-
在其子树中定义基础、颜色、层级、阴影和动效 token。`motion="system"` 跟随 `prefers-reduced-motion`;使用 `motion="reduced"` 降低当前作用域动效,或在嵌套主题中使用 `motion="full"` 恢复默认 token。System 配色模式跟随 `prefers-color-scheme`。
|
|
1582
|
-
|
|
1583
|
-
`resolved-appearance` 是只读的派生宿主属性(同时以 `resolvedAppearance` property 暴露):值恒为 `light` 或 `dark`;显式 `appearance` 直接透传,`appearance="system"` 从 `prefers-color-scheme` 解析,OS 深浅翻转时实时更新。未设置 `appearance` 的主题仍保持 inactive,但解析输出使用默认值 `light`。该 attribute 由组件独占写入并会恢复外部改动,因此消费端可以直接把选择器或 Tailwind custom variant 绑定到 `resolved-appearance`,无需再维护第二份主题状态。
|
|
1584
|
-
|
|
1585
|
-
配色变化是否使用 View Transitions API 播放动画,由当前主题范围的 `motion` 档位决定:`full` 始终播放揭示,`reduced` 直接落地新 appearance、不播放揭示,`system` 跟随 `prefers-reduced-motion`。根主题揭示整页;嵌套主题只揭示自己的 capture box。圆心优先取最近一次 pointerdown 坐标,否则回退主题盒或视口中心;揭示方向跟随解析后的外观,包括 `appearance="system"` 从 `prefers-color-scheme` 解析出的结果,深浅两个方向反向播放。不支持的浏览器、reduced-motion 作用域、时长为 0 以及同一 flight 内已有未完成请求都会立即落地新 appearance;当前版本对 `appearance="system"` 的 OS 深浅翻转不做动画。
|
|
1586
|
-
|
|
1587
|
-
揭示进行中,View Transitions 的 rendering-suppression 规则会强制所有 pointer hit-test 指向 document element,因此 CSS `pointer-events` 覆盖无法让底层页面继续交互。主题只在 `pointerdown` 或 wheel 时调用 `skipTransition()`;`pointermove` 与 `pointerup` 永远不会结束揭示,因此发起控件的收尾移动或过渡期间的普通移动都不会打断动画。触发 skip 的事件本身仍按规范以 document element 为目标,但失效窗口到此为止,不会持续满整个揭示时长。
|
|
1588
|
-
|
|
1589
|
-
**从已移除的 `transition` 属性迁移:** 删掉该属性,改用 `motion` 表达意图。原先带 `transition` 的主题在默认 `motion="system"` 下揭示行为不变;需要在系统偏好 reduce 时仍播放揭示,则加 `motion="full"`。原先不带 `transition` 的主题现在默认就会揭示,若要保持无动画直接切换,请显式设置 `motion="reduced"`。
|
|
1590
|
-
|
|
1591
|
-
主题宿主使用 `display: contents` 且不绘制任何背景:组件库不在宿主页面画背景,嵌入方对主题子树背后的表面保留完全控制权。自定义属性仍可靠继承到 slotted 内容。
|
|
1592
|
-
|
|
1593
|
-
**根节点 page 色同步:** 最外层 active theme 会把自己计算出的 `--wui-color-page` 以行内自定义属性的形式镜像到 `document.documentElement` 上。它补上的正是该 token 自己到不了的那一处——自定义属性的继承链到 documentElement 就断了,所以消费方写 `body { background: var(--wui-color-page) }` 只能给滚动区上色,橡皮筋回弹(macOS rubber-band)露出的画布区仍是 UA 背景色;有了这个镜像属性,回弹区也跟随主题。写入时机是主题连接时与 appearance 变化时(含 `appearance="system"` 跟随系统深浅翻转);写入的是该主题的计算值,因此消费者在 host 上覆盖 `--wui-color-page` 会被按当前值原样镜像。**非实时:** 组件不观察 host 自身的样式变化,因此运行中改该属性(改行内样式、切 class、host 级媒体查询)不会实时镜像到 root,要等下一个写入触发点——连接、`appearance` 变成另一个值、或系统深浅翻转;把 `appearance` 重新赋成当前值不算触发点,因为值没变时更新会被跳过。页面色需要独立于主题 appearance 变化时,消费者自己在该变量上持有它:在 `:root` 或 `body` 上用 `!important` 定义 `--wui-color-page`,重要声明在层级上压过本模块写在 documentElement 上的行内值;或者让页面色跟随 `appearance` 走。只有最外层主题会写入:嵌套主题保留自己的作用域,不改写 root 值。同步权按 connect 顺序归属,最外层断开时顺延给下一个已连接主题;最后一个主题断开时刻意保留最后值而不是移除,避免主题切换间隙闪回 UA 背景。组件库在 documentElement 上只写这一个自定义属性,不写其它内容。
|
|
1594
|
-
|
|
1595
|
-
**基础 token:**
|
|
1596
|
-
|
|
1597
|
-
| 属性 | 默认值 | 说明 |
|
|
1598
|
-
| ------------------------- | ------- | ------------------------ |
|
|
1599
|
-
| `--wui-input-width` | `200px` | 紧凑表单控件默认宽度 |
|
|
1600
|
-
| `--wui-control-size` | `36px` | 控件默认高度和方形最小宽 |
|
|
1601
|
-
| `--wui-overlay-min-width` | `200px` | 锚定浮层最小宽度 |
|
|
1602
|
-
| `--wui-focus-ring-width` | `3px` | Focus 指示器宽度 |
|
|
1603
|
-
|
|
1604
|
-
**排版 token:** 字号按文字扮演的角色命名,而不是按它在尺度里的位置——`caption` 是密集 chrome 上的小标签,`readout` 是数字读数,无后缀的基准字号是正文,`title` 是有界卡片标题。字重按重量命名,行高按这段文字需要的垂直空气命名。这一族与外观无关:light 和 dark 下取值相同。
|
|
1605
|
-
|
|
1606
|
-
| 属性 | 默认值 | 说明 |
|
|
1607
|
-
| ---------------------------- | ------ | -------------------------------------------------- |
|
|
1608
|
-
| `--wui-font-size-caption` | `12px` | 密集 chrome 标签(徽标、toast 时间戳、菜单分组头) |
|
|
1609
|
-
| `--wui-font-size-readout` | `13px` | 数字读数(图片预览的计数与缩放比) |
|
|
1610
|
-
| `--wui-font-size` | `14px` | 正文与控件的基础字号(含控件的占位文本) |
|
|
1611
|
-
| `--wui-font-size-title` | `18px` | 有界卡片标题(dialog、drawer) |
|
|
1612
|
-
| `--wui-font-weight-medium` | `500` | 强调行内文字的中等字重 |
|
|
1613
|
-
| `--wui-font-weight-semibold` | `600` | 标题的半粗字重 |
|
|
1614
|
-
| `--wui-line-height-tight` | `1.2` | 大号标题的紧行高 |
|
|
1615
|
-
| `--wui-line-height-snug` | `1.4` | 中号标题的适中行高 |
|
|
1616
|
-
| `--wui-line-height-normal` | `1.5` | 正文的常规行高 |
|
|
1617
|
-
| `--wui-line-height-relaxed` | `1.6` | 长文阅读的宽松行高 |
|
|
1618
|
-
|
|
1619
|
-
组件里的 `line-height: 1` 与 `line-height: 0` 刻意不做 token:前者是控件内单行标签的垂直居中手段,后者是把包裹盒高收缩到内容——两者都不是排版行高,token 化会诱使调用方把它们当成行高去整体调节。
|
|
1620
|
-
|
|
1621
|
-
**间距 token:** 4px 基准的六级阶,止于 24px——组件层以生效的静态节奏字面量写入的最大间距值。(唯一更宽的是 `<web-ui-empty>` 的 `32px 24px`,它由 `--wui-empty-size` 在 JS 侧派生,属尺寸那根轴而非节奏,理由见下面的排除项。)它不替代组件各自的覆盖 token(`--wui-button-px`、`--wui-dialog-padding` 等),而是充当那些 token 的 fallback 默认值——组件仍然决定自己的 padding,间距阶只说明这个值在整体节奏里的位置。因此覆盖某一级,就等于给嵌入应用一根作用于所有已挂阶调用点的密度杠杆——它调的是节奏,不是结构,理由见下面的排除项。
|
|
1622
|
-
|
|
1623
|
-
| 属性 | 默认值 | 说明 |
|
|
1624
|
-
| --------------- | ------ | -------------------------- |
|
|
1625
|
-
| `--wui-space-1` | `4px` | 紧凑内边距(徽标、菜单) |
|
|
1626
|
-
| `--wui-space-2` | `8px` | 控件间距、组间距、行内间距 |
|
|
1627
|
-
| `--wui-space-3` | `12px` | 控件水平内边距 |
|
|
1628
|
-
| `--wui-space-4` | `16px` | 标准区块间距 |
|
|
1629
|
-
| `--wui-space-5` | `20px` | 浮层内容内边距 |
|
|
1630
|
-
| `--wui-space-6` | `24px` | 宽浮层内边距 |
|
|
1631
|
-
|
|
1632
|
-
这一阶刻意不含 `1px`、`2px`、`6px`、`7.5px`、`10px` 和负值。`1px` 是发丝线和描边环的结构宽度;`2px`、`6px`、`7.5px` 是光学修正;`10px` 是 4px 阶之外的半档耦合值;负 margin 用来抵消 flex gap 或按钮的 padding。它们的「为什么是这个数」各自独立,没有共享间距语义,收进同一把尺子只会让覆盖其中之一时误伤其余。
|
|
1633
|
-
|
|
1634
|
-
第二类排除按语义而非按取值:对齐视口边缘或宿主自身内容边缘的偏移量,即使正好落在 4px 阶上也不挂阶——`--wui-image-preview-edge-gap`、`--wui-toast-viewport-gap`、`--wui-layout-mobile-toggle-inset`、`--wui-back-top-right` / `--wui-back-top-left` / `--wui-back-top-bottom`、`--wui-drawer-inset` 与 `--wui-drawer-close-right`。它们的参照系是屏幕或宿主的内容边缘,不是相邻元素之间的节奏;并进间距阶会让「收紧 `--wui-space-2`」意外把组件推离屏幕边缘。`--wui-drawer-close-top` 是反例:它经 `--wui-space-4` 解析,因为它要跟随 drawer 的 header padding,两者必须一起动。
|
|
1635
|
-
|
|
1636
|
-
第三类排除是由组件**尺寸** prop 派生、而非写成节奏值的间距:`<web-ui-empty>` 让 `padding`、`min-block-size` 与图形尺寸跟随 `--wui-empty-size` 缩放,`40` / `56` / `72` 三档实测 padding 分别是 `23px 17px` / `32px 24px` / `41px 31px`。这六个值里有四个是奇数,4px 阶根本无法表示;`56` 档的 `32px 24px` 落在基准上纯属尺寸选值的巧合——`56` 是三档里唯一被 7 整除的,商恰为 8,而 8 本身是 4 的倍数,取整在这一档根本没有发生。一根在三个尺寸里有两个静默失效、只在第三个上碰巧生效的密度杠杆,比没有杠杆更糟;何况整组值本来就随 `size` 走,因此不挂阶。这里直接写 px 即可。
|
|
1637
|
-
|
|
1638
|
-
覆盖必须落在 `<web-ui-theme>` 作用域内,因为 theme 宿主只为自己的子树声明这些级。已知边界:`assets/*.css` 的浮层样式有两条注入路径——经 theme 自带的 overlay root 注入时间距阶生效,退到 document 级 fallback overlay root(一个看不到 theme token 的普通 `div`)时,浮层间距恒取字面量 fallback。
|
|
1639
|
-
|
|
1640
|
-
**选择控件 token(radio、checkbox):**
|
|
1641
|
-
|
|
1642
|
-
| 属性 | 默认值 | 说明 |
|
|
1643
|
-
| ------------------------------ | -------------------- | ------------------------------------ |
|
|
1644
|
-
| `--wui-selection-control-size` | `18px` | 指示器(圆点 / 方框)宽高 |
|
|
1645
|
-
| `--wui-radio-group-gap` | `var(--wui-space-2)` | `<web-ui-radio-group>` 的成员间距 |
|
|
1646
|
-
| `--wui-checkbox-group-gap` | `var(--wui-space-2)` | `<web-ui-checkbox-group>` 的成员间距 |
|
|
1647
|
-
|
|
1648
|
-
**圆角 token:**
|
|
1649
|
-
|
|
1650
|
-
| 属性 | 默认值 | 说明 |
|
|
1651
|
-
| ---------------------- | ---------------------- | ---------------------------------------------------- |
|
|
1652
|
-
| `--wui-radius-control` | `calc(infinity * 1px)` | 小型控件的胶囊圆角(button、input、switch、option…) |
|
|
1653
|
-
| `--wui-radius-menu` | `18px` | 菜单/Popover 浮动面板、多行 textarea 与 toast |
|
|
1654
|
-
| `--wui-radius-overlay` | `28px` | 大型覆盖层:dialog、drawer、layout sidebar |
|
|
1655
|
-
|
|
1656
|
-
**层级 token:**
|
|
1657
|
-
|
|
1658
|
-
| 属性 | 默认值 | 说明 |
|
|
1659
|
-
| ---------------------------- | ------ | -------------- |
|
|
1660
|
-
| `--wui-layer-inline-overlay` | `1` | 组件内部浮层 |
|
|
1661
|
-
| `--wui-layer-header` | `10` | 页面 Header |
|
|
1662
|
-
| `--wui-layer-auxiliary` | `20` | 悬浮工具控件 |
|
|
1663
|
-
| `--wui-layer-banner` | `30` | Banner |
|
|
1664
|
-
| `--wui-layer-tabbar` | `40` | Tab Bar |
|
|
1665
|
-
| `--wui-layer-sidebar` | `50` | 侧边栏 |
|
|
1666
|
-
| `--wui-layer-menu` | `100` | 菜单/浮动面板 |
|
|
1667
|
-
| `--wui-layer-menu-nested` | `110` | 嵌套菜单 |
|
|
1668
|
-
| `--wui-layer-toast` | `200` | Toast |
|
|
1669
|
-
| `--wui-layer-loading` | `300` | 阻塞式 Loading |
|
|
1670
|
-
|
|
1671
|
-
**动效 token:** duration 默认值为 `--wui-duration-press: 80ms`、`--wui-duration-feedback: 100ms`、`--wui-duration-trigger: 160ms`、`--wui-duration-focus: 200ms`、`--wui-duration-float-enter: 240ms`、`--wui-duration-float-exit: 160ms`、`--wui-duration-dialog-enter: 320ms`、`--wui-duration-dialog-exit: 260ms`、`--wui-duration-drawer-enter: 280ms`、`--wui-duration-drawer-exit: 240ms`、`--wui-duration-drawer-nested: 450ms`、`--wui-duration-toast-enter: 280ms`、`--wui-duration-toast-exit: 200ms`、`--wui-duration-collapse-enter: 200ms`、`--wui-duration-collapse-exit: 160ms`、`--wui-duration-layout: 200ms`、`--wui-duration-swipe-settle: 220ms`(image-preview 轮播回位)、`--wui-duration-spin: 600ms`(icon 旋转)、`--wui-duration-spinner: 800ms`(spinner 叶片追光)与 `--wui-theme-transition-duration: 500ms`(主题配色揭示)。Easing token 是 `--wui-ease-enter`、`--wui-ease-dialog`(`cubic-bezier(0.2, 0, 0, 1)`)、`--wui-ease-slide`、`--wui-ease-float`(`cubic-bezier(0.4, 0.38, 0.2, 1)`,锚定浮动面板专用的无回弹弹簧拟合曲线)、`--wui-ease-swipe`(`cubic-bezier(0.32, 0.72, 0, 1)`,轮播回位的减速收尾)与 `--wui-theme-transition-easing: ease-in`;进入缩放是 `--wui-scale-enter: 0.95`(浮动面板与 image-preview 的图片舞台层由小到大展开),dialog 使用 `--wui-dialog-scale-enter: 1.1`(由大到小收缩入场,退场反向)。`motion="reduced"` 下所有转场时长归零(轮播回位一并归零),两个无限加载循环只放慢、不停(`--wui-duration-spin` / `--wui-duration-spinner`:`600ms` / `800ms` → `1600ms`)。加载指示冻结会被读成界面卡死。hover/active 背景反馈即时切换、无过渡动画;选中态、按压与 focus 走带时长的过渡,reduce 下同样归零。
|
|
1672
|
-
|
|
1673
|
-
**颜色 token:**
|
|
1674
|
-
|
|
1675
|
-
| 属性 | 浅色默认值 | 深色默认值 | 说明 |
|
|
1676
|
-
| ---------------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------- | -------------------------------- |
|
|
1677
|
-
| `--wui-color-page` | `#fff` | `#242628` | 页面背景 |
|
|
1678
|
-
| `--wui-color-surface` | `#fff` | `#2c2c2e` | 普通 表面 |
|
|
1679
|
-
| `--wui-color-surface-raised` | `#f2f2f7` | `#2c2c2e` | 抬升表面 |
|
|
1680
|
-
| `--wui-color-surface-control` | `#dfdfdf` | `#3a3a3c` | 中性可交互控件表面 |
|
|
1681
|
-
| `--wui-color-surface-track` | `#e5e5ea` | `#444446` | Slider/Switch 轨道表面 |
|
|
1682
|
-
| `--wui-color-surface-menu` | `rgb(250 250 250 / 0.76)` | `rgb(49 48 50 / 0.74)` | Menu 和浮动面板表面 |
|
|
1683
|
-
| `--wui-color-surface-glass` | `rgb(250 250 250 / 0.34)` | `rgb(44 44 46 / 0.42)` | 液态玻璃表面 |
|
|
1684
|
-
| `--wui-color-surface-glass-hover` | `color-mix(... text 6%, surface-glass)` | `color-mix(... text 6%, surface-glass)` | Glass 完整悬停背景 |
|
|
1685
|
-
| `--wui-color-surface-glass-active` | `color-mix(... text 15%, surface-glass)` | `color-mix(... text 15%, surface-glass)` | Glass 完整按下背景 |
|
|
1686
|
-
| `--wui-color-surface-overlay` | `rgb(248 248 248 / 0.92)` | `rgb(32 34 34 / 0.92)` | 半透明浮层表面 |
|
|
1687
|
-
| `--wui-color-surface-sidebar` | `rgb(233 233 233 / 0.82)` | `rgb(44 44 46 / 0.8)` | 侧边栏面板表面 |
|
|
1688
|
-
| `--wui-color-surface-segmented` | `#e5e5ea` | `#3a3a3c` | Segmented 指示器表面 |
|
|
1689
|
-
| `--wui-color-surface-selected` | `#fff` | `#5c5c5e` | 选中表面 |
|
|
1690
|
-
| `--wui-color-text` | `#1b1b1b` | `#e9eaea` | 主要文本 |
|
|
1691
|
-
| `--wui-color-text-secondary` | `#6a6a6a` | `#a1a1a6` | 次要文本 |
|
|
1692
|
-
| `--wui-color-text-tertiary` | `color-mix(in srgb, var(--wui-color-text) 35%, transparent)` | `color-mix(in srgb, var(--wui-color-text) 42%, transparent)` | 三级文本和弱意图图标 |
|
|
1693
|
-
| `--wui-color-text-disabled` | `color-mix(in srgb, var(--wui-color-text) 32%, transparent)` | `color-mix(in srgb, var(--wui-color-text) 38%, transparent)` | 禁用态前景文本 |
|
|
1694
|
-
| `--wui-color-state-layer-hover` | `color-mix(in srgb, var(--wui-color-text) 6%, transparent)` | `color-mix(in srgb, var(--wui-color-text) 6%, transparent)` | 透明悬停层 |
|
|
1695
|
-
| `--wui-color-state-layer-active` | `color-mix(in srgb, var(--wui-color-text) 15%, transparent)` | `color-mix(in srgb, var(--wui-color-text) 15%, transparent)` | 透明按下层 |
|
|
1696
|
-
| `--wui-color-border` | `rgb(0 0 0 / 0.1)` | `rgb(255 255 255 / 0.14)` | 常规边框和分隔线 |
|
|
1697
|
-
| `--wui-color-glass-highlight` | `rgb(255 255 255 / 0.9)` | `rgb(255 255 255 / 0.1)` | 表面 inset 高光 |
|
|
1698
|
-
| `--wui-color-glass-ring` | `rgb(0 0 0 / 0.05)` | `rgb(255 255 255 / 0.05)` | 1px 描边环底色 |
|
|
1699
|
-
| `--wui-color-glass-ring-sheen` | `rgb(255 255 255 / 0.5)` | `rgb(255 255 255 / 0.2)` | 描边环角落受光光泽 |
|
|
1700
|
-
| `--wui-color-glass-ring-shade` | `rgb(0 0 0 / 0.03)` | `rgb(0 0 0 / 0.5)` | 描边环角落背光压暗 |
|
|
1701
|
-
| `--wui-color-accent` | `#08f` | `#0a84ff` | Accent 和输入焦点边框 |
|
|
1702
|
-
| `--wui-color-on-accent` | `#fff` | `#fff` | Accent 上的前景色 |
|
|
1703
|
-
| `--wui-color-on-control` | `#fff` | `#f2f2f7` | 控件内芯前景色 |
|
|
1704
|
-
| `--wui-color-success` | `#16a34a` | `#30d158` | 成功 |
|
|
1705
|
-
| `--wui-color-warning` | `#d97706` | `#ff9f0a` | 警告 |
|
|
1706
|
-
| `--wui-color-danger` | `#dc2626` | `#ff453a` | 危险 |
|
|
1707
|
-
| `--wui-color-info` | `#2563eb` | `#64d2ff` | 信息 |
|
|
1708
|
-
| `--wui-color-backdrop` | `rgb(0 0 0 / 0.12)` | `rgb(0 0 0 / 0.48)` | Modal 遮罩 |
|
|
1709
|
-
| `--wui-color-focus-ring` | `color-mix(in srgb, var(--wui-color-accent, #08f) 40%, transparent)` | `color-mix(in srgb, var(--wui-color-accent, #0a84ff) 62%, transparent)` | Focus 指示器颜色,由 accent 派生 |
|
|
1710
|
-
|
|
1711
|
-
**阴影 token:**
|
|
1712
|
-
|
|
1713
|
-
| 属性 | 浅色默认值 | 深色默认值 | 说明 |
|
|
1714
|
-
| ---------------------- | -------------------------------- | ------------------------------- | ----------------- |
|
|
1715
|
-
| `--wui-shadow-overlay` | `2px 16px 40px rgb(0 0 0 / 0.4)` | `0 18px 48px rgb(0 0 0 / 0.48)` | Modal/Drawer 阴影 |
|
|
1716
|
-
| `--wui-shadow-panel` | `0 3px 9px rgb(0 0 0 / 0.27)` | `0 4px 16px rgb(0 0 0 / 0.32)` | 小型浮动面板阴影 |
|
|
1717
|
-
| `--wui-shadow-glass` | 四层扩散阴影 | `0 8px 24px rgb(0 0 0 / 0.08)` | 液态玻璃基础阴影 |
|
|
1718
|
-
|
|
1719
|
-
**玻璃效果 token:** `--wui-glass-brightness` 浅色模式为 `1.06`,深色模式为 `1.02`。`--wui-glass-corner-radius` 默认值为 `32px`;非 pill 玻璃表面以自身语义圆角(`--wui-radius-menu` / `--wui-radius-overlay`,drawer 为 `--wui-drawer-radius`)覆盖它,使对角描边光影跟随 token 覆盖联动。pill 控件例外:光影渐变需要物理尺寸,继续使用由控件实际尺寸或内部尺寸 token 派生的有限圆角值。
|
|
1720
|
-
|
|
1721
|
-
**内部 token:** 以 `--wui-internal-*` 为前缀的变量是 Shadow DOM 内部接线变量,不属于公共 token API,Consumer 不应覆盖。
|
|
1722
|
-
|
|
1723
|
-
---
|
|
1724
|
-
|
|
1725
|
-
### 通知
|
|
1726
|
-
|
|
1727
|
-
#### `<web-ui-toast>`
|
|
1728
|
-
|
|
1729
|
-
单个 Toast 通知。通过命令式 API 使用。
|
|
1730
|
-
|
|
1731
|
-
直接使用元素时,`no-close-button` 是标准布尔属性,用于隐藏关闭按钮。
|
|
1732
|
-
|
|
1733
|
-
**命令式 API:**
|
|
1734
|
-
|
|
1735
|
-
```ts
|
|
1736
|
-
import { toast } from '@greypan/web-ui'
|
|
1737
|
-
|
|
1738
|
-
// 创建
|
|
1739
|
-
toast.success('操作成功')
|
|
1740
|
-
toast.error('出错了', { duration: 5000 })
|
|
1741
|
-
toast.info('提示信息')
|
|
1742
|
-
toast.warning('请注意')
|
|
1743
|
-
|
|
1744
|
-
// 带选项
|
|
1745
|
-
const id = toast({ message: '自定义', type: 'info', position: 'bottom-right', duration: 4000, closable: true })
|
|
1746
|
-
|
|
1747
|
-
// 关闭
|
|
1748
|
-
toast.close(id)
|
|
1749
|
-
toast.clear()
|
|
1750
|
-
|
|
1751
|
-
// upsert:同一 id 再次调用是更新那一条,而不是再建一条
|
|
1752
|
-
toast.error('网络连接中断,正在重试…', { id: 'network' })
|
|
1753
|
-
toast.error('网络连接中断(第 2 次重试)', { id: 'network' })
|
|
1754
|
-
|
|
1755
|
-
// 一条 toast 走完整个生命周期
|
|
1756
|
-
toast.info('上传中 0%', { id: 'upload', duration: 0 })
|
|
1757
|
-
toast.info('上传中 60%', { id: 'upload' }) // 省略 duration,计时不动
|
|
1758
|
-
toast.success('上传完成', { id: 'upload', duration: 3000 }) // 换 type,重新开始倒计时
|
|
1759
|
-
```
|
|
1760
|
-
|
|
1761
|
-
每次调用都返回这条 toast 的最终 id,调用方无需区分新建还是更新。
|
|
1762
|
-
|
|
1763
|
-
**ToastOptions:**
|
|
1764
|
-
|
|
1765
|
-
| 选项 | 类型 | 默认值 | 说明 |
|
|
1766
|
-
| ----------- | --------------------------------------------- | ------------------------- | ------------------------------------- |
|
|
1767
|
-
| `message` | `string` | — | 通知文本 |
|
|
1768
|
-
| `type` | `'success' \| 'info' \| 'warning' \| 'error'` | `'info'` | 类型 |
|
|
1769
|
-
| `duration` | `number` | `3000`(error 为 `5000`) | 自动关闭时间(0=不自动关闭) |
|
|
1770
|
-
| `closable` | `boolean` | `true` | 显示关闭按钮 |
|
|
1771
|
-
| `id` | `string` | auto | 合并键:同一 id 的调用更新同一条 |
|
|
1772
|
-
| `heading` | `string` | `''` | 粗体标题 |
|
|
1773
|
-
| `position` | 6 种位置 | `'top-right'` | 屏幕位置 |
|
|
1774
|
-
| `target` | `Element` | — | 用于查找最近 theme-owned overlay root |
|
|
1775
|
-
| `container` | `HTMLElement` | — | 显式挂载容器(最高优先级) |
|
|
1776
|
-
|
|
1777
|
-
**upsert 语义** —— 同一 `id` 再次调用的结果:
|
|
1778
|
-
|
|
1779
|
-
| 目标状态 | 结果 |
|
|
1780
|
-
| -------------------- | ------------------------------------------------ |
|
|
1781
|
-
| 已挂载 | 返回同一个 id;给出的字段覆盖,未给出的保持原值 |
|
|
1782
|
-
| 同 tick 内仍在待挂载 | patch 待挂载的 options,挂载后生效,不产生第二条 |
|
|
1783
|
-
| 已关闭或正在退场 | 新建一条;正在离场的那条自行走完退场动画 |
|
|
1784
|
-
|
|
1785
|
-
`duration` 只有显式传入才重启倒计时(悬停暂停期间是例外:只记新值不点火,指针离开后按新时长计满);`message`、`heading`、`type` 是普通属性,不碰计时。`toast.error` 的 5000 默认值在**创建时**兜底,不算显式传入,因此重复调用 `toast.error(msg, { id })` 不会重置倒计时。`position` 变化会把元素搬到新容器并保住剩余计时(支持 `moveBefore` 的引擎直接搬,其余走暂停/续跑降级);若倒计时在主线程被占用期间已经到期,降级路径会在续跑时直接退场,而不是让它一直挂着。`container` 与 `target` 以首次调用为准,不支持把已存在的 toast 换到另一个 overlay root。
|
|
1786
|
-
|
|
1787
|
-
**关闭语义** —— `toast.close(id)` 覆盖 toast 出现的每个阶段,包括「还没开始显示」的两种:
|
|
1788
|
-
|
|
1789
|
-
| 目标状态 | 结果 |
|
|
1790
|
-
| --------------------- | ---------------------------------------- |
|
|
1791
|
-
| 已显示 | 播放退场动画,结束后派发 `toast-close` |
|
|
1792
|
-
| 已挂载、`show()` 未跑 | 没有退场动画可播,立即派发 `toast-close` |
|
|
1793
|
-
| 同 tick 仍在待挂载 | 挂载前出队:不会出现,也不派发事件 |
|
|
1794
|
-
| 已在退场 | 空操作,由那条 toast 自己走完退场 |
|
|
1795
|
-
|
|
1796
|
-
`toast.clear()` 的口径相同:队列条目一并取消,已挂载的全部关闭。
|
|
1797
|
-
|
|
1798
|
-
**事件:** `toast-close` (`CustomEvent<{ id: string; reason: 'auto' | 'manual' | 'programmatic' | 'clear' }>`)
|
|
1799
|
-
|
|
1800
|
-
悬停暂停自动关闭计时器(使用 `pointerenter`/`pointerleave`),指针离开后**续跑剩余时间**,而不是重新计满。暂停期间漏掉 `pointerleave`(指针拖出窗口、元素在悬停中被搬迁或摘出)时,由 document 级 `pointerover`/`pointerout`/`pointerleave` 兜底恢复,不会永久停在屏幕上。同一微任务中批量挂载 Toast。
|
|
1801
|
-
|
|
1802
|
-
**CSS 自定义属性:**
|
|
1803
|
-
|
|
1804
|
-
| 属性 | 默认值 | 说明 |
|
|
1805
|
-
| ------------------------------- | ------ | -------------------------- |
|
|
1806
|
-
| `--wui-toast-viewport-gap` | `16px` | Toast 到视口边缘的可见距离 |
|
|
1807
|
-
| `--wui-toast-container-padding` | `40px` | 容器预留的扩散阴影绘制空间 |
|
|
1808
|
-
|
|
1809
|
-
---
|
|
1810
|
-
|
|
1811
|
-
### 子项
|
|
1812
|
-
|
|
1813
|
-
#### `<web-ui-option>`
|
|
1814
|
-
|
|
1815
|
-
`<web-ui-select>` 的选择选项。
|
|
1816
|
-
|
|
1817
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1818
|
-
| ---------- | --------- | ------- | ---------------------------------- |
|
|
1819
|
-
| `value` | `string` | `''` | 选中值 |
|
|
1820
|
-
| `label` | `string` | `''` | 显示文本;未设置时回退默认插槽文本 |
|
|
1821
|
-
| `selected` | `boolean` | `false` | 当前是否选中 |
|
|
1822
|
-
| `disabled` | `boolean` | `false` | 禁用状态 |
|
|
1823
|
-
|
|
1824
|
-
**插槽:** `default`(标签文本回退)、`prefix`(标签前装饰内容)、`suffix`(标签后装饰内容)
|
|
1825
|
-
|
|
1826
|
-
非表单关联组件(父级 select 统一提交)。
|
|
1827
|
-
|
|
1828
|
-
**CSS 自定义属性:**
|
|
1829
|
-
|
|
1830
|
-
| 属性 | 默认值 | 说明 |
|
|
1831
|
-
| ---------------------------- | ------- | ------------------------- |
|
|
1832
|
-
| `--wui-option-check-display` | `block` | 选中勾选图标的 display 值 |
|
|
1833
|
-
|
|
1834
|
-
#### `<web-ui-segmented-trigger>`
|
|
1835
|
-
|
|
1836
|
-
`<web-ui-segmented>` 的分段按钮。
|
|
1837
|
-
|
|
1838
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
1839
|
-
| ---------- | --------- | ------- | ------------------ |
|
|
1840
|
-
| `value` | `string` | `''` | 分段值 |
|
|
1841
|
-
| `checked` | `boolean` | `false` | 当前选中 |
|
|
1842
|
-
| `disabled` | `boolean` | `false` | 单独禁用该 trigger |
|
|
1843
|
-
|
|
1844
|
-
**事件:** `change`
|
|
1845
|
-
|
|
1846
|
-
非表单关联组件(父级 segmented 统一提交)。
|
|
1847
|
-
|
|
1848
|
-
**CSS 自定义属性:**
|
|
1849
|
-
|
|
1850
|
-
| 属性 | 默认值 | 说明 |
|
|
1851
|
-
| ----------------------------------- | -------------------------------- | ---------------- |
|
|
1852
|
-
| `--wui-segmented-trigger-px` | `12px` | 水平内边距 |
|
|
1853
|
-
| `--wui-segmented-trigger-bg-hover` | `--wui-color-state-layer-hover` | Trigger 悬停背景 |
|
|
1854
|
-
| `--wui-segmented-trigger-bg-active` | `--wui-color-state-layer-active` | Trigger 按下背景 |
|
|
310
|
+
| **表单控件** | [`<web-ui-input>`](docs/input.md) |
|
|
311
|
+
| | [`<web-ui-textarea>`](docs/textarea.md) |
|
|
312
|
+
| | [`<web-ui-editable-text>`](docs/editable-text.md) |
|
|
313
|
+
| | [`<web-ui-input-number>`](docs/input-number.md) |
|
|
314
|
+
| | [`<web-ui-select>`](docs/select.md) |
|
|
315
|
+
| | [`<web-ui-autocomplete>`](docs/autocomplete.md) |
|
|
316
|
+
| | [`<web-ui-slider>`](docs/slider.md) |
|
|
317
|
+
| | [`<web-ui-checkbox>`](docs/checkbox.md) |
|
|
318
|
+
| | [`<web-ui-radio>`](docs/radio.md) |
|
|
319
|
+
| | [`<web-ui-switch>`](docs/switch.md) |
|
|
320
|
+
| | [`<web-ui-segmented>`](docs/segmented.md) |
|
|
321
|
+
| | [`<web-ui-checkbox-group>`](docs/checkbox-group.md) |
|
|
322
|
+
| | [`<web-ui-radio-group>`](docs/radio-group.md) |
|
|
323
|
+
| **按钮** | [`<web-ui-button>`](docs/button.md) |
|
|
324
|
+
| | [`<web-ui-button-group>`](docs/button-group.md) |
|
|
325
|
+
| **浮层 / 模态** | [`<web-ui-dialog>`](docs/dialog.md) |
|
|
326
|
+
| | [`<web-ui-drawer>`](docs/drawer.md) |
|
|
327
|
+
| | [`imagePreview()`](docs/image-preview.md) |
|
|
328
|
+
| **文档流 Disclosure** | [`<web-ui-collapse>`](docs/collapse.md) |
|
|
329
|
+
| **浮动** | [`<web-ui-popover>`](docs/popover.md) |
|
|
330
|
+
| | [`<web-ui-tooltip>`](docs/tooltip.md) |
|
|
331
|
+
| | [`<web-ui-context-menu>`](docs/context-menu.md) |
|
|
332
|
+
| **菜单** | [`<web-ui-dropdown>`](docs/dropdown.md) |
|
|
333
|
+
| | [`<web-ui-dropdown-item>`](docs/dropdown-item.md) |
|
|
334
|
+
| | [`<web-ui-dropdown-divider>`](docs/dropdown-divider.md) |
|
|
335
|
+
| | [`<web-ui-dropdown-header>`](docs/dropdown-header.md) |
|
|
336
|
+
| **数据展示** | [`<web-ui-avatar>`](docs/avatar.md) |
|
|
337
|
+
| | [`<web-ui-badge>`](docs/badge.md) |
|
|
338
|
+
| | [`<web-ui-empty>`](docs/empty.md) |
|
|
339
|
+
| | [`<web-ui-middle-ellipsis>`](docs/middle-ellipsis.md) |
|
|
340
|
+
| | [`<web-ui-icon>`](docs/icon.md) |
|
|
341
|
+
| | [`<web-ui-spinner>`](docs/spinner.md) |
|
|
342
|
+
| **布局与工具** | [`<web-ui-layout>`](docs/layout.md) |
|
|
343
|
+
| | [`<web-ui-back-top>`](docs/back-top.md) |
|
|
344
|
+
| | [`<web-ui-svg-draw-lines>`](docs/svg-draw-lines.md) |
|
|
345
|
+
| | [`<web-ui-theme>`](docs/theme.md) |
|
|
346
|
+
| **通知** | [`<web-ui-toast>`](docs/toast.md) |
|
|
347
|
+
| **子项** | [`<web-ui-option>`](docs/option.md) |
|
|
348
|
+
| | [`<web-ui-segmented-trigger>`](docs/segmented-trigger.md) |
|