@greypan/web-ui 2.1.1 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.CN.md +43 -39
  2. package/README.md +53 -48
  3. package/dist/assets/menu-portal.js +1 -1
  4. package/dist/assets/menu-portal.js.map +1 -1
  5. package/dist/assets/toast-containers.js +1 -1
  6. package/dist/assets/toast-containers.js.map +1 -1
  7. package/dist/components/back-top/index.d.ts +3 -1
  8. package/dist/components/back-top/index.js +16 -12
  9. package/dist/components/back-top/index.js.map +1 -1
  10. package/dist/components/back-top/style.js +1 -1
  11. package/dist/components/back-top/style.js.map +1 -1
  12. package/dist/components/context-menu/index.d.ts +3 -4
  13. package/dist/components/context-menu/index.js +55 -69
  14. package/dist/components/context-menu/index.js.map +1 -1
  15. package/dist/components/context-menu/style.js +1 -1
  16. package/dist/components/context-menu/style.js.map +1 -1
  17. package/dist/components/dialog/index.d.ts +4 -11
  18. package/dist/components/dialog/index.js +29 -53
  19. package/dist/components/dialog/index.js.map +1 -1
  20. package/dist/components/drawer/index.d.ts +4 -11
  21. package/dist/components/drawer/index.js +42 -63
  22. package/dist/components/drawer/index.js.map +1 -1
  23. package/dist/components/dropdown/index.d.ts +3 -3
  24. package/dist/components/dropdown/index.js +70 -78
  25. package/dist/components/dropdown/index.js.map +1 -1
  26. package/dist/components/dropdown/style.js +1 -1
  27. package/dist/components/dropdown/style.js.map +1 -1
  28. package/dist/components/dropdown-item/style.js +1 -1
  29. package/dist/components/dropdown-item/style.js.map +1 -1
  30. package/dist/components/option/style.js +1 -1
  31. package/dist/components/option/style.js.map +1 -1
  32. package/dist/components/popover/index.d.ts +2 -5
  33. package/dist/components/popover/index.js +47 -70
  34. package/dist/components/popover/index.js.map +1 -1
  35. package/dist/components/select/index.d.ts +4 -7
  36. package/dist/components/select/index.js +64 -74
  37. package/dist/components/select/index.js.map +1 -1
  38. package/dist/components/select/style.js +1 -1
  39. package/dist/components/select/style.js.map +1 -1
  40. package/dist/components/switch/style.js +1 -1
  41. package/dist/components/switch/style.js.map +1 -1
  42. package/dist/components/theme/style.js +1 -1
  43. package/dist/components/theme/style.js.map +1 -1
  44. package/dist/components/toast/manager.js +1 -1
  45. package/dist/components/toast/manager.js.map +1 -1
  46. package/dist/components/toast/toast.d.ts +1 -1
  47. package/dist/components/toast/toast.js +26 -26
  48. package/dist/components/toast/toast.js.map +1 -1
  49. package/dist/components/tooltip/index.d.ts +3 -5
  50. package/dist/components/tooltip/index.js +61 -70
  51. package/dist/components/tooltip/index.js.map +1 -1
  52. package/dist/icons/generated/ooui-close.d.ts +6 -0
  53. package/dist/icons/generated/ooui-close.js +10 -0
  54. package/dist/icons/generated/ooui-close.js.map +1 -0
  55. package/dist/icons/index.d.ts +1 -0
  56. package/dist/icons/index.js +2 -1
  57. package/dist/shared/menu-portal/menu-portal.js +8 -1
  58. package/dist/shared/menu-portal/menu-portal.js.map +1 -1
  59. package/dist/shared/menu-portal/menu-tree.js +29 -0
  60. package/dist/shared/menu-portal/menu-tree.js.map +1 -0
  61. package/dist/shared/overlay/anchored-panel.js +49 -0
  62. package/dist/shared/overlay/anchored-panel.js.map +1 -0
  63. package/dist/shared/overlay/native-dialog-presence.js +45 -0
  64. package/dist/shared/overlay/native-dialog-presence.js.map +1 -0
  65. package/dist/shared/overlay/overlay.js +5 -2
  66. package/dist/shared/overlay/overlay.js.map +1 -1
  67. package/dist/shared/overlay/portal.js +3 -3
  68. package/dist/shared/overlay/portal.js.map +1 -1
  69. package/dist/shared/overlay/presence.js +1 -1
  70. package/dist/shared/overlay/presence.js.map +1 -1
  71. package/dist/shared/scroll-lock/scroll-lock.js +15 -1
  72. package/dist/shared/scroll-lock/scroll-lock.js.map +1 -1
  73. package/dist/types/vue.d.ts +2 -1
  74. package/package.json +1 -1
  75. package/dist/shared/property-converters/boolean-with-false-string.js +0 -9
  76. package/dist/shared/property-converters/boolean-with-false-string.js.map +0 -1
package/README.CN.md CHANGED
@@ -6,8 +6,9 @@
6
6
 
7
7
  ## 演示
8
8
 
9
- [查看 vue 使用演示](https://pan-z2l0ahvi.github.io/mono/vue-web-ui-demo/)
10
- [查看 react 使用演示](https://pan-z2l0ahvi.github.io/mono/react-web-ui-demo/)
9
+ [查看组件在 vue 中使用](https://pan-z2l0ahvi.github.io/mono/vue-web-ui-demo/)
10
+
11
+ [查看组件在 react 中使用](https://pan-z2l0ahvi.github.io/mono/react-web-ui-demo/)
11
12
 
12
13
  ## 安装
13
14
 
@@ -65,13 +66,12 @@ function App() {
65
66
 
66
67
  React 19 按 JSX 键名中的 `on` 后缀原样注册 Custom Element 事件。事件名大小写敏感:`open-change`
67
68
  必须绑定为 `onopen-change`,不能写成 `onOpenChange`。标准 `input` 和 `change` 事件仍使用 React
68
- 惯用的 `onInput` 和 `onChange`。默认值为 `true` 的属性应使用驼峰 JavaScript 属性传入,这样 `false`
69
- 才会传递给组件。
69
+ 惯用的 `onInput` 和 `onChange`。布尔属性遵循原生 HTML 语义:属性缺失为 `false`,属性存在为 `true`。
70
70
 
71
71
  ```tsx
72
72
  <web-ui-dialog
73
73
  open={open}
74
- lockScroll={false}
74
+ noScrollLock
75
75
  onopen-change={event => setOpen(event.detail.open)}
76
76
  />
77
77
  <web-ui-select value={value} onChange={event => setValue(event.currentTarget.value)} />
@@ -248,7 +248,7 @@ ArrowUp/ArrowDown 键增减数值。
248
248
  | `disabled` | `boolean` | `false` | 禁用状态 |
249
249
  | `required` | `boolean` | `false` | 必填校验 |
250
250
  | `portal` | `boolean` | `false` | 在主题浮层容器中渲染 |
251
- | `lock-scroll` | `boolean` | `true` | 打开时锁定页面滚动 |
251
+ | `no-scroll-lock` | `boolean` | `false` | 打开时不锁定页面滚动 |
252
252
  | `overlayContainer` | `HTMLElement \| () => HTMLElement` | — | 显式 Portal 容器 |
253
253
 
254
254
  **事件:** `input`, `change`, `open-change` (`CustomEvent<{ open: boolean }>`)
@@ -426,11 +426,11 @@ ArrowUp/ArrowDown 键增减数值。
426
426
 
427
427
  模态对话框,使用原生 `<dialog>` 的 `showModal()`。
428
428
 
429
- | 属性 | 类型 | 默认值 | 说明 |
430
- | ------------------ | --------- | ------- | ------------------ |
431
- | `open` | `boolean` | `false` | 对话框可见性 |
432
- | `lock-scroll` | `boolean` | `true` | 打开时锁定页面滚动 |
433
- | `overlay-closable` | `boolean` | `true` | 点击遮罩关闭对话框 |
429
+ | 属性 | 类型 | 默认值 | 说明 |
430
+ | ------------------- | --------- | ------- | -------------------- |
431
+ | `open` | `boolean` | `false` | 对话框可见性 |
432
+ | `no-scroll-lock` | `boolean` | `false` | 打开时不锁定页面滚动 |
433
+ | `no-backdrop-close` | `boolean` | `false` | 禁止点击遮罩关闭 |
434
434
 
435
435
  **事件:** `open-change` (`CustomEvent<{ open: boolean }>`)
436
436
 
@@ -444,14 +444,14 @@ ArrowUp/ArrowDown 键增减数值。
444
444
 
445
445
  侧边抽屉,使用原生 `<dialog>` 并自带关闭动画。
446
446
 
447
- | 属性 | 类型 | 默认值 | 说明 |
448
- | ------------------ | ---------------------------------------- | --------- | -------------------------------- |
449
- | `open` | `boolean` | `false` | 抽屉可见性 |
450
- | `placement` | `'right' \| 'left' \| 'top' \| 'bottom'` | `'right'` | 滑入方向 |
451
- | `heading` | `string` | `''` | 标题文字(无 header 插槽时显示) |
452
- | `closable` | `boolean` | `false` | 显示关闭按钮 |
453
- | `lock-scroll` | `boolean` | `true` | 打开时锁定页面滚动 |
454
- | `overlay-closable` | `boolean` | `true` | 点击遮罩关闭抽屉 |
447
+ | 属性 | 类型 | 默认值 | 说明 |
448
+ | ------------------- | ---------------------------------------- | --------- | -------------------------------- |
449
+ | `open` | `boolean` | `false` | 抽屉可见性 |
450
+ | `placement` | `'right' \| 'left' \| 'top' \| 'bottom'` | `'right'` | 滑入方向 |
451
+ | `heading` | `string` | `''` | 标题文字(无 header 插槽时显示) |
452
+ | `closable` | `boolean` | `false` | 显示关闭按钮 |
453
+ | `no-scroll-lock` | `boolean` | `false` | 打开时不锁定页面滚动 |
454
+ | `no-backdrop-close` | `boolean` | `false` | 禁止点击遮罩关闭 |
455
455
 
456
456
  **事件:** `open-change` (`CustomEvent<{ open: boolean }>`)
457
457
 
@@ -459,7 +459,7 @@ ArrowUp/ArrowDown 键增减数值。
459
459
 
460
460
  **方法:** `show()`, `close()`
461
461
 
462
- 关闭时保留原生 dialog 的 top layer,待 `--wui-duration-drawer` 过渡完成(默认 280ms)后调用 `dialog.close()`。Escape 始终走此关闭路径;`overlay-closable` 仅控制遮罩点击。
462
+ 关闭时保留原生 dialog 的 top layer,待 `--wui-duration-drawer` 过渡完成(默认 280ms)后调用 `dialog.close()`。Escape 始终走此关闭路径;`no-backdrop-close` 仅控制遮罩点击。
463
463
 
464
464
  ---
465
465
 
@@ -513,10 +513,10 @@ Hover 模式使用 `pointerenter`/`pointerleave` 加延迟控制。Click 模式
513
513
 
514
514
  右键上下文菜单。
515
515
 
516
- | 属性 | 类型 | 默认值 | 说明 |
517
- | ------------- | --------- | ------- | ------------ |
518
- | `disabled` | `boolean` | `false` | 禁用状态 |
519
- | `lock-scroll` | `boolean` | `true` | 阻止背景滚动 |
516
+ | 属性 | 类型 | 默认值 | 说明 |
517
+ | ---------------- | --------- | ------- | ------------ |
518
+ | `disabled` | `boolean` | `false` | 禁用状态 |
519
+ | `no-scroll-lock` | `boolean` | `false` | 允许背景滚动 |
520
520
 
521
521
  **事件:** `open-change` (`CustomEvent<{ open: boolean }>`)
522
522
 
@@ -534,14 +534,14 @@ Hover 模式使用 `pointerenter`/`pointerleave` 加延迟控制。Click 模式
534
534
 
535
535
  下拉菜单,支持多级子菜单。
536
536
 
537
- | 属性 | 类型 | 默认值 | 说明 |
538
- | ------------- | ----------- | ---------------- | ---------------- |
539
- | `open` | `boolean` | `false` | 菜单可见性 |
540
- | `disabled` | `boolean` | `false` | 禁用状态 |
541
- | `placement` | `Placement` | `'bottom-start'` | Floating UI 位置 |
542
- | `offset` | `number` | `4` | 与触发器距离 |
543
- | `match-width` | `boolean` | `false` | 匹配触发器宽度 |
544
- | `lock-scroll` | `boolean` | `true` | 锁定页面滚动 |
537
+ | 属性 | 类型 | 默认值 | 说明 |
538
+ | ---------------- | ----------- | ---------------- | -------------------- |
539
+ | `open` | `boolean` | `false` | 菜单可见性 |
540
+ | `disabled` | `boolean` | `false` | 禁用状态 |
541
+ | `placement` | `Placement` | `'bottom-start'` | Floating UI 位置 |
542
+ | `offset` | `number` | `4` | 与触发器距离 |
543
+ | `match-width` | `boolean` | `false` | 匹配触发器宽度 |
544
+ | `no-scroll-lock` | `boolean` | `false` | 打开时不锁定页面滚动 |
545
545
 
546
546
  **事件:** `open-change` (`CustomEvent<{ open: boolean }>`)
547
547
 
@@ -684,17 +684,19 @@ WebUiSpinner.hide() // 隐藏
684
684
 
685
685
  回到顶部按钮。
686
686
 
687
- | 属性 | 类型 | 默认值 | 说明 |
688
- | -------------- | ----------------------- | -------- | ------------------ |
689
- | `smooth` | `boolean` | `true` | 平滑滚动 |
690
- | `threshold` | `number` | `200` | 显示按钮的滚动阈值 |
691
- | `visible` | `boolean` | `false` | 当前可见状态 |
692
- | `scrollTarget` | `HTMLElement \| Window` | `window` | 滚动容器 |
687
+ | 属性 | 类型 | 默认值 | 说明 |
688
+ | ----------------- | ----------------------- | ---------- | ------------------ |
689
+ | `scroll-behavior` | `'smooth' \| 'auto'` | `'smooth'` | 滚动行为 |
690
+ | `threshold` | `number` | `200` | 显示按钮的滚动阈值 |
691
+ | `visible` | `boolean` | `false` | 当前可见状态 |
692
+ | `scrollTarget` | `HTMLElement \| Window` | `window` | 滚动容器 |
693
693
 
694
694
  **插槽:** `default`(自定义按钮内容)
695
695
 
696
696
  **方法:** `toTop()`
697
697
 
698
+ **定位:** `scrollTarget` 为 `window` 时按钮固定在视口角落;为 `HTMLElement` 时需将元素放置在容器内部,按钮通过 `position: sticky` 悬浮于容器底部角落。偏移量沿用 `--web-ui-back-top-top/right/bottom/left` CSS 变量。
699
+
698
700
  角色:`button`,键盘 Enter 触发回到顶部。
699
701
 
700
702
  #### `<web-ui-svg-draw-lines>`
@@ -725,7 +727,7 @@ SVG 线条绘制动画,基于 `stroke-dashoffset`。直接在原元素上动
725
727
 
726
728
  **方法:** `getOverlayRoot()` — 返回 Portal 浮层容器
727
729
 
728
- 定义 `--wui-color-*`、`--wui-shadow-*`、`--wui-layer-*` 与 motion token。motion token 是稳定的主题契约,可在主题范围覆盖:`--wui-duration-press`、`--wui-duration-fast`、`--wui-duration-overlay-enter`、`--wui-duration-overlay-exit`、`--wui-duration-drawer`、`--wui-ease-out`、`--wui-ease-standard`、`--wui-scale-press`、`--wui-scale-enter`。`motion="system"` 跟随 `prefers-reduced-motion`;使用 `motion="reduced"` 降低当前作用域动效,或在嵌套主题中使用 `motion="full"` 恢复默认 token。System 配色模式跟随 `prefers-color-scheme`。
730
+ 定义 `--wui-color-*`、`--wui-shadow-*`、`--wui-layer-*` 与 motion token。motion token 是稳定的主题契约,可在主题范围覆盖:`--wui-duration-press`、`--wui-duration-feedback`、`--wui-duration-fast`、`--wui-duration-menu-enter`、`--wui-duration-menu-exit`、`--wui-duration-overlay-enter`、`--wui-duration-overlay-exit`、`--wui-duration-drawer`、`--wui-ease-out`、`--wui-ease-standard`、`--wui-scale-press`、`--wui-scale-enter`。`motion="system"` 跟随 `prefers-reduced-motion`;使用 `motion="reduced"` 降低当前作用域动效,或在嵌套主题中使用 `motion="full"` 恢复默认 token。System 配色模式跟随 `prefers-color-scheme`。
729
731
 
730
732
  ---
731
733
 
@@ -735,6 +737,8 @@ SVG 线条绘制动画,基于 `stroke-dashoffset`。直接在原元素上动
735
737
 
736
738
  单个 Toast 通知。通过命令式 API 使用。
737
739
 
740
+ 直接使用元素时,`no-close-button` 是标准布尔属性,用于隐藏关闭按钮。
741
+
738
742
  **命令式 API:**
739
743
 
740
744
  ```ts
package/README.md CHANGED
@@ -6,8 +6,9 @@ English | [简体中文](./README.CN.md)
6
6
 
7
7
  ## Demo
8
8
 
9
- [View used by vue demo](https://pan-z2l0ahvi.github.io/mono/vue-web-ui-demo/)
10
- [View used by react demo](https://pan-z2l0ahvi.github.io/mono/react-web-ui-demo/)
9
+ [View components used by vue](https://pan-z2l0ahvi.github.io/mono/vue-web-ui-demo/)
10
+
11
+ [View components used by react](https://pan-z2l0ahvi.github.io/mono/react-web-ui-demo/)
11
12
 
12
13
  ## Install
13
14
 
@@ -65,13 +66,13 @@ function App() {
65
66
 
66
67
  React 19 registers Custom Element events using the suffix of the JSX `on` key unchanged. Event names are
67
68
  case-sensitive: bind `open-change` as `onopen-change`, not `onOpenChange`. Standard `input` and `change`
68
- events continue to use React's conventional `onInput` and `onChange`. For a default-`true` property, pass the
69
- camel-cased JavaScript property so `false` reaches the element.
69
+ events continue to use React's conventional `onInput` and `onChange`. Boolean properties follow native HTML
70
+ semantics: absence is `false` and presence is `true`.
70
71
 
71
72
  ```tsx
72
73
  <web-ui-dialog
73
74
  open={open}
74
- lockScroll={false}
75
+ noScrollLock
75
76
  onopen-change={event => setOpen(event.detail.open)}
76
77
  />
77
78
  <web-ui-select value={value} onChange={event => setValue(event.currentTarget.value)} />
@@ -241,16 +242,16 @@ ArrowUp/ArrowDown keyboard increments and decrements the value.
241
242
 
242
243
  Select dropdown with option items, keyboard navigation, and portal support.
243
244
 
244
- | Attribute | Type | Default | Description |
245
- | ------------------ | ---------------------------------- | ------- | -------------------------------- |
246
- | `value` | `string` | `''` | Selected value |
247
- | `placeholder` | `string` | `''` | Placeholder text |
248
- | `name` | `string` | `''` | Form field name |
249
- | `disabled` | `boolean` | `false` | Disabled state |
250
- | `required` | `boolean` | `false` | Required validation |
251
- | `portal` | `boolean` | `false` | Render dropdown in theme overlay |
252
- | `lock-scroll` | `boolean` | `true` | Lock body scroll when open |
253
- | `overlayContainer` | `HTMLElement \| () => HTMLElement` | — | Explicit portal container |
245
+ | Attribute | Type | Default | Description |
246
+ | ------------------ | ---------------------------------- | ------- | --------------------------------- |
247
+ | `value` | `string` | `''` | Selected value |
248
+ | `placeholder` | `string` | `''` | Placeholder text |
249
+ | `name` | `string` | `''` | Form field name |
250
+ | `disabled` | `boolean` | `false` | Disabled state |
251
+ | `required` | `boolean` | `false` | Required validation |
252
+ | `portal` | `boolean` | `false` | Render dropdown in theme overlay |
253
+ | `no-scroll-lock` | `boolean` | `false` | Do not lock body scroll when open |
254
+ | `overlayContainer` | `HTMLElement \| () => HTMLElement` | — | Explicit portal container |
254
255
 
255
256
  **Events:** `input`, `change`, `open-change` (`CustomEvent<{ open: boolean }>`)
256
257
 
@@ -431,11 +432,11 @@ Propagates `direction` attribute to child buttons.
431
432
 
432
433
  Modal dialog using native `<dialog>` with `showModal()`.
433
434
 
434
- | Attribute | Type | Default | Description |
435
- | ------------------ | --------- | ------- | -------------------------- |
436
- | `open` | `boolean` | `false` | Dialog visibility |
437
- | `lock-scroll` | `boolean` | `true` | Lock body scroll when open |
438
- | `overlay-closable` | `boolean` | `true` | Close on backdrop click |
435
+ | Attribute | Type | Default | Description |
436
+ | ------------------- | --------- | ------- | --------------------------------- |
437
+ | `open` | `boolean` | `false` | Dialog visibility |
438
+ | `no-scroll-lock` | `boolean` | `false` | Do not lock body scroll when open |
439
+ | `no-backdrop-close` | `boolean` | `false` | Do not close on backdrop click |
439
440
 
440
441
  **Events:** `open-change` (`CustomEvent<{ open: boolean }>`)
441
442
 
@@ -449,14 +450,14 @@ Uses native `<dialog>` with `@cancel` prevention (Escape calls `close()`). Click
449
450
 
450
451
  Side drawer using native `<dialog>` with closing animation.
451
452
 
452
- | Attribute | Type | Default | Description |
453
- | ------------------ | ---------------------------------------- | --------- | ----------------------------------------- |
454
- | `open` | `boolean` | `false` | Drawer visibility |
455
- | `placement` | `'right' \| 'left' \| 'top' \| 'bottom'` | `'right'` | Slide-in direction |
456
- | `heading` | `string` | `''` | Title text (fallback when no header slot) |
457
- | `closable` | `boolean` | `false` | Show close button |
458
- | `lock-scroll` | `boolean` | `true` | Lock body scroll when open |
459
- | `overlay-closable` | `boolean` | `true` | Close on backdrop click |
453
+ | Attribute | Type | Default | Description |
454
+ | ------------------- | ---------------------------------------- | --------- | ----------------------------------------- |
455
+ | `open` | `boolean` | `false` | Drawer visibility |
456
+ | `placement` | `'right' \| 'left' \| 'top' \| 'bottom'` | `'right'` | Slide-in direction |
457
+ | `heading` | `string` | `''` | Title text (fallback when no header slot) |
458
+ | `closable` | `boolean` | `false` | Show close button |
459
+ | `no-scroll-lock` | `boolean` | `false` | Do not lock body scroll when open |
460
+ | `no-backdrop-close` | `boolean` | `false` | Do not close on backdrop click |
460
461
 
461
462
  **Events:** `open-change` (`CustomEvent<{ open: boolean }>`)
462
463
 
@@ -464,7 +465,7 @@ Side drawer using native `<dialog>` with closing animation.
464
465
 
465
466
  **Methods:** `show()`, `close()`
466
467
 
467
- Closing keeps the native dialog in the top layer until the `--wui-duration-drawer` transition completes (280ms by default), then calls `dialog.close()`. Escape always follows this close path; `overlay-closable` controls backdrop clicks only.
468
+ Closing keeps the native dialog in the top layer until the `--wui-duration-drawer` transition completes (280ms by default), then calls `dialog.close()`. Escape always follows this close path; `no-backdrop-close` controls backdrop clicks only.
468
469
 
469
470
  ---
470
471
 
@@ -518,10 +519,10 @@ Tooltip overlay using pointer/focus triggers.
518
519
 
519
520
  Right-click context menu.
520
521
 
521
- | Attribute | Type | Default | Description |
522
- | ------------- | --------- | ------- | ------------------------- |
523
- | `disabled` | `boolean` | `false` | Disabled state |
524
- | `lock-scroll` | `boolean` | `true` | Prevent background scroll |
522
+ | Attribute | Type | Default | Description |
523
+ | ---------------- | --------- | ------- | -------------------------- |
524
+ | `disabled` | `boolean` | `false` | Disabled state |
525
+ | `no-scroll-lock` | `boolean` | `false` | Allow background scrolling |
525
526
 
526
527
  **Events:** `open-change` (`CustomEvent<{ open: boolean }>`)
527
528
 
@@ -539,14 +540,14 @@ Opens on `contextmenu` event. Menu items: `<web-ui-dropdown-item>`, `<web-ui-dro
539
540
 
540
541
  Dropdown menu with multi-level submenu support.
541
542
 
542
- | Attribute | Type | Default | Description |
543
- | ------------- | ----------- | ---------------- | -------------------------- |
544
- | `open` | `boolean` | `false` | Menu visibility |
545
- | `disabled` | `boolean` | `false` | Disabled state |
546
- | `placement` | `Placement` | `'bottom-start'` | Floating UI placement |
547
- | `offset` | `number` | `4` | Offset from trigger |
548
- | `match-width` | `boolean` | `false` | Match trigger width |
549
- | `lock-scroll` | `boolean` | `true` | Lock body scroll when open |
543
+ | Attribute | Type | Default | Description |
544
+ | ---------------- | ----------- | ---------------- | --------------------------------- |
545
+ | `open` | `boolean` | `false` | Menu visibility |
546
+ | `disabled` | `boolean` | `false` | Disabled state |
547
+ | `placement` | `Placement` | `'bottom-start'` | Floating UI placement |
548
+ | `offset` | `number` | `4` | Offset from trigger |
549
+ | `match-width` | `boolean` | `false` | Match trigger width |
550
+ | `no-scroll-lock` | `boolean` | `false` | Do not lock body scroll when open |
550
551
 
551
552
  **Events:** `open-change` (`CustomEvent<{ open: boolean }>`)
552
553
 
@@ -689,17 +690,19 @@ Page layout grid.
689
690
 
690
691
  Scroll-to-top button.
691
692
 
692
- | Attribute | Type | Default | Description |
693
- | -------------- | ----------------------- | -------- | ------------------------ |
694
- | `smooth` | `boolean` | `true` | Smooth scroll |
695
- | `threshold` | `number` | `200` | Scroll threshold to show |
696
- | `visible` | `boolean` | `false` | Current visibility state |
697
- | `scrollTarget` | `HTMLElement \| Window` | `window` | Scroll container |
693
+ | Attribute | Type | Default | Description |
694
+ | ----------------- | ----------------------- | ---------- | ------------------------ |
695
+ | `scroll-behavior` | `'smooth' \| 'auto'` | `'smooth'` | Scroll behavior |
696
+ | `threshold` | `number` | `200` | Scroll threshold to show |
697
+ | `visible` | `boolean` | `false` | Current visibility state |
698
+ | `scrollTarget` | `HTMLElement \| Window` | `window` | Scroll container |
698
699
 
699
700
  **Slots:** `default` (custom button content)
700
701
 
701
702
  **Methods:** `toTop()`
702
703
 
704
+ **Positioning:** With `scrollTarget` as `window`, the button is fixed to the viewport corner. With `scrollTarget` as an `HTMLElement`, place the element inside that container and the button floats at the container's bottom corner via `position: sticky`. Offsets follow the `--web-ui-back-top-top/right/bottom/left` CSS variables.
705
+
703
706
  Role: `button`, keyboard Enter scrolls to top.
704
707
 
705
708
  #### `<web-ui-svg-draw-lines>`
@@ -730,7 +733,7 @@ Theme provider defining CSS custom property tokens.
730
733
 
731
734
  **Methods:** `getOverlayRoot()` — returns the portal overlay container
732
735
 
733
- Defines `--wui-color-*`, `--wui-shadow-*`, `--wui-layer-*`, and motion tokens. Motion tokens are stable and may be overridden per theme scope: `--wui-duration-press`, `--wui-duration-fast`, `--wui-duration-overlay-enter`, `--wui-duration-overlay-exit`, `--wui-duration-drawer`, `--wui-ease-out`, `--wui-ease-standard`, `--wui-scale-press`, and `--wui-scale-enter`. `motion="system"` follows `prefers-reduced-motion`; use `motion="reduced"` to reduce animation in a scope or `motion="full"` in a nested theme to restore the normal token values. System appearance follows `prefers-color-scheme`.
736
+ Defines `--wui-color-*`, `--wui-shadow-*`, `--wui-layer-*`, and motion tokens. Motion tokens are stable and may be overridden per theme scope: `--wui-duration-press`, `--wui-duration-feedback`, `--wui-duration-fast`, `--wui-duration-menu-enter`, `--wui-duration-menu-exit`, `--wui-duration-overlay-enter`, `--wui-duration-overlay-exit`, `--wui-duration-drawer`, `--wui-ease-out`, `--wui-ease-standard`, `--wui-scale-press`, and `--wui-scale-enter`. `motion="system"` follows `prefers-reduced-motion`; use `motion="reduced"` to reduce animation in a scope or `motion="full"` in a nested theme to restore the normal token values. System appearance follows `prefers-color-scheme`.
734
737
 
735
738
  ---
736
739
 
@@ -740,6 +743,8 @@ Defines `--wui-color-*`, `--wui-shadow-*`, `--wui-layer-*`, and motion tokens. M
740
743
 
741
744
  Individual toast notification element. Managed by imperative API.
742
745
 
746
+ When using the element directly, `no-close-button` is a standard boolean attribute that hides its close button.
747
+
743
748
  **Imperative API:**
744
749
 
745
750
  ```ts
@@ -1,5 +1,5 @@
1
1
  //#region src/assets/menu-portal.css?inline
2
- var e = ".wui-menu-portal-overlay{--wui-glass-shadow:var(--wui-shadow-pop,0 3px 9px #00000045);z-index:var(--wui-layer-menu,100);box-sizing:border-box;min-width:200px;max-width:90vw;max-height:80vh;color:var(--wui-color-text,#1b1b1b);background-color:var(--wui-color-surface-overlay,#f6f6f6e0);border-radius:20px;padding:4px;position:fixed;overflow-y:auto}.wui-menu-portal-overlay.dropdown-overlay .dropdown-scroll{min-width:200px;max-height:320px;overflow-y:auto}.wui-menu-portal-overlay.dropdown-overlay[data-level]:not([data-level=\"0\"]),.wui-menu-portal-overlay.context-submenu{z-index:var(--wui-layer-menu-submenu,110)}";
2
+ var e = ".wui-menu-portal-overlay{--wui-glass-shadow:var(--wui-shadow-pop,0 3px 9px #00000045);--wui-duration-overlay-enter:var(--wui-duration-menu-enter,.14s);--wui-duration-overlay-exit:var(--wui-duration-menu-exit,.1s);-webkit-user-select:none;user-select:none;z-index:var(--wui-layer-menu,100);box-sizing:border-box;min-width:200px;max-width:90vw;max-height:80vh;color:var(--wui-color-text,#1b1b1b);background-color:var(--wui-color-surface-overlay,#f6f6f6e0);border-radius:20px;position:fixed;overflow:hidden}.wui-menu-portal-overlay .wui-menu-scroll{max-height:80vh;overflow-y:auto}.wui-menu-portal-overlay .wui-menu-content{padding:4px}.wui-menu-portal-overlay.dropdown-overlay .wui-menu-scroll{max-height:320px}.wui-menu-portal-overlay.dropdown-overlay[data-level]:not([data-level=\"0\"]),.wui-menu-portal-overlay.context-submenu{z-index:var(--wui-layer-menu-submenu,110)}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"menu-portal.js","names":[],"sources":["../../src/assets/menu-portal.css?inline"],"sourcesContent":[".wui-menu-portal-overlay {\n --wui-glass-shadow: var(--wui-shadow-pop, 0 3px 9px rgb(0 0 0 / 0.27));\n\n position: fixed;\n z-index: var(--wui-layer-menu, 100);\n\n overflow-y: auto;\n\n box-sizing: border-box;\n min-width: 200px;\n max-width: 90vw;\n max-height: 80vh;\n padding: 4px;\n border-radius: 20px;\n\n color: var(--wui-color-text, #1b1b1b);\n\n background-color: var(--wui-color-surface-overlay, rgb(246 246 246 / 0.88));\n}\n\n.wui-menu-portal-overlay.dropdown-overlay .dropdown-scroll {\n overflow-y: auto;\n min-width: 200px;\n max-height: 320px;\n}\n\n.wui-menu-portal-overlay.dropdown-overlay[data-level]:not([data-level='0']),\n.wui-menu-portal-overlay.context-submenu {\n z-index: var(--wui-layer-menu-submenu, 110);\n}\n"],"mappings":""}
1
+ {"version":3,"file":"menu-portal.js","names":[],"sources":["../../src/assets/menu-portal.css?inline"],"sourcesContent":[".wui-menu-portal-overlay {\n --wui-glass-shadow: var(--wui-shadow-pop, 0 3px 9px rgb(0 0 0 / 0.27));\n --wui-duration-overlay-enter: var(--wui-duration-menu-enter, 140ms);\n --wui-duration-overlay-exit: var(--wui-duration-menu-exit, 100ms);\n user-select: none;\n\n position: fixed;\n z-index: var(--wui-layer-menu, 100);\n\n overflow: hidden;\n\n box-sizing: border-box;\n min-width: 200px;\n max-width: 90vw;\n max-height: 80vh;\n border-radius: 20px;\n\n color: var(--wui-color-text, #1b1b1b);\n\n background-color: var(--wui-color-surface-overlay, rgb(246 246 246 / 0.88));\n\n & .wui-menu-scroll {\n overflow-y: auto;\n max-height: 80vh;\n }\n\n & .wui-menu-content {\n padding: 4px;\n }\n\n &.dropdown-overlay {\n & .wui-menu-scroll {\n max-height: 320px;\n }\n\n &[data-level]:not([data-level='0']) {\n z-index: var(--wui-layer-menu-submenu, 110);\n }\n }\n\n &.context-submenu {\n z-index: var(--wui-layer-menu-submenu, 110);\n }\n}\n"],"mappings":""}
@@ -1,5 +1,5 @@
1
1
  //#region src/assets/toast-containers.css?inline
2
- var e = ".wui-toast-container{pointer-events:none;z-index:var(--wui-layer-toast,200);overscroll-behavior:contain;flex-direction:column;gap:8px;max-height:100vh;padding:20px;display:flex;position:fixed;overflow-y:auto}.wui-toast-container>*{pointer-events:auto}.wui-toast-top-left{align-items:flex-start;top:0;left:0}.wui-toast-top-center{align-items:center;top:0;left:50%;transform:translate(-50%)}.wui-toast-top-right{align-items:flex-end;top:0;right:0}.wui-toast-bottom-left{flex-direction:column-reverse;align-items:flex-start;bottom:0;left:0}.wui-toast-bottom-center{flex-direction:column-reverse;align-items:center;bottom:0;left:50%;transform:translate(-50%)}.wui-toast-bottom-right{flex-direction:column-reverse;align-items:flex-end;bottom:0;right:0}";
2
+ var e = ".wui-toast-container{pointer-events:none;z-index:var(--wui-layer-toast,200);overscroll-behavior:contain;flex-direction:column;gap:8px;max-height:100vh;padding:24px;display:flex;position:fixed;overflow-y:auto}.wui-toast-container>*{pointer-events:auto}.wui-toast-top-left{align-items:flex-start;top:0;left:0}.wui-toast-top-center{align-items:center;top:0;left:50%;transform:translate(-50%)}.wui-toast-top-right{align-items:flex-end;top:0;right:0}.wui-toast-bottom-left{flex-direction:column-reverse;align-items:flex-start;bottom:0;left:0}.wui-toast-bottom-center{flex-direction:column-reverse;align-items:center;bottom:0;left:50%;transform:translate(-50%)}.wui-toast-bottom-right{flex-direction:column-reverse;align-items:flex-end;bottom:0;right:0}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"toast-containers.js","names":[],"sources":["../../src/assets/toast-containers.css?inline"],"sourcesContent":["/* Toast 容器定位 — 由 toast 命令式 API 动态注入到 body */\n\n.wui-toast-container {\n pointer-events: none;\n\n position: fixed;\n z-index: var(--wui-layer-toast, 200);\n\n overflow-y: auto;\n overscroll-behavior: contain;\n display: flex;\n flex-direction: column;\n gap: 8px;\n\n max-height: 100vh;\n\n /* padding 需要 >= 阴影模糊半径,否则 overflow 会裁剪子元素 box-shadow */\n padding: 20px;\n}\n\n.wui-toast-container > * {\n pointer-events: auto;\n}\n\n/* ===== 顶部位置 ===== */\n\n.wui-toast-top-left {\n top: 0;\n left: 0;\n align-items: flex-start;\n}\n\n.wui-toast-top-center {\n top: 0;\n left: 50%;\n transform: translateX(-50%);\n align-items: center;\n}\n\n.wui-toast-top-right {\n top: 0;\n right: 0;\n align-items: flex-end;\n}\n\n/* ===== 底部位置 ===== */\n\n.wui-toast-bottom-left {\n bottom: 0;\n left: 0;\n flex-direction: column-reverse;\n align-items: flex-start;\n}\n\n.wui-toast-bottom-center {\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n\n flex-direction: column-reverse;\n align-items: center;\n}\n\n.wui-toast-bottom-right {\n right: 0;\n bottom: 0;\n flex-direction: column-reverse;\n align-items: flex-end;\n}\n"],"mappings":""}
1
+ {"version":3,"file":"toast-containers.js","names":[],"sources":["../../src/assets/toast-containers.css?inline"],"sourcesContent":["/* Toast 容器定位 — 由 toast 命令式 API 动态注入到 body */\n\n.wui-toast-container {\n pointer-events: none;\n\n position: fixed;\n z-index: var(--wui-layer-toast, 200);\n\n overflow-y: auto;\n overscroll-behavior: contain;\n display: flex;\n flex-direction: column;\n gap: 8px;\n\n max-height: 100vh;\n\n /* padding 需要 >= 阴影模糊半径,否则 overflow 会裁剪子元素 box-shadow */\n padding: 24px;\n}\n\n.wui-toast-container > * {\n pointer-events: auto;\n}\n\n/* ===== 顶部位置 ===== */\n\n.wui-toast-top-left {\n top: 0;\n left: 0;\n align-items: flex-start;\n}\n\n.wui-toast-top-center {\n top: 0;\n left: 50%;\n transform: translateX(-50%);\n align-items: center;\n}\n\n.wui-toast-top-right {\n top: 0;\n right: 0;\n align-items: flex-end;\n}\n\n/* ===== 底部位置 ===== */\n\n.wui-toast-bottom-left {\n bottom: 0;\n left: 0;\n flex-direction: column-reverse;\n align-items: flex-start;\n}\n\n.wui-toast-bottom-center {\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n\n flex-direction: column-reverse;\n align-items: center;\n}\n\n.wui-toast-bottom-right {\n right: 0;\n bottom: 0;\n flex-direction: column-reverse;\n align-items: flex-end;\n}\n"],"mappings":""}
@@ -1,7 +1,9 @@
1
1
  import { LitElement, PropertyValues } from 'lit';
2
2
  export declare class WebUiBackTop extends LitElement {
3
3
  static styles: import('lit').CSSResult;
4
- smooth: boolean;
4
+ get scrollBehavior(): 'smooth' | 'auto';
5
+ set scrollBehavior(value: string);
6
+ private _scrollBehavior;
5
7
  visible: boolean;
6
8
  scrollTarget: HTMLElement | Window;
7
9
  get threshold(): number;
@@ -1,9 +1,8 @@
1
1
  import e from "../../_virtual/_@oxc-project_runtime@0.140.0/helpers/esm/decorate.js";
2
2
  import "../icon/index.js";
3
3
  import t from "../../icons/generated/lucide-arrow-up-to-line.js";
4
- import { normalizeNumber as n } from "../../shared/normalize/index.js";
4
+ import { normalizeLiteral as n, normalizeNumber as r } from "../../shared/normalize/index.js";
5
5
  import "../button/index.js";
6
- import { booleanWithFalseString as r } from "../../shared/property-converters/boolean-with-false-string.js";
7
6
  import i from "./style.js";
8
7
  import { LitElement as a, html as o, unsafeCSS as s } from "lit";
9
8
  import { customElement as c, property as l, state as u } from "lit/decorators.js";
@@ -11,17 +10,24 @@ import { getRootScrollTop as d } from "@greypan/browser-kit";
11
10
  //#region src/components/back-top/index.ts
12
11
  var f = class extends a {
13
12
  constructor(...e) {
14
- super(...e), this.smooth = !0, this.visible = !1, this.scrollTarget = window, this._threshold = 200;
13
+ super(...e), this._scrollBehavior = "smooth", this.visible = !1, this.scrollTarget = window, this._threshold = 200;
15
14
  }
16
15
  static {
17
16
  this.styles = s(i);
18
17
  }
18
+ get scrollBehavior() {
19
+ return this._scrollBehavior;
20
+ }
21
+ set scrollBehavior(e) {
22
+ let t = this._scrollBehavior;
23
+ this._scrollBehavior = n(e, ["smooth", "auto"], "smooth"), this.requestUpdate("scrollBehavior", t);
24
+ }
19
25
  get threshold() {
20
26
  return this._threshold;
21
27
  }
22
28
  set threshold(e) {
23
29
  let t = this._threshold;
24
- this._threshold = n(e, 0, 1e4, 200), this.requestUpdate("threshold", t);
30
+ this._threshold = r(e, 0, 1e4, 200), this.requestUpdate("threshold", t);
25
31
  }
26
32
  connectedCallback() {
27
33
  super.connectedCallback(), this.computeVisible(), this.onScrollTarget();
@@ -30,7 +36,7 @@ var f = class extends a {
30
36
  super.disconnectedCallback(), this.eventController?.abort();
31
37
  }
32
38
  willUpdate(e) {
33
- super.willUpdate(e), (e.has("threshold") || e.has("scrollTarget")) && this.computeVisible(), e.has("scrollTarget") && this.hasUpdated && this.onScrollTarget();
39
+ super.willUpdate(e), (e.has("threshold") || e.has("scrollTarget")) && this.computeVisible(), e.has("scrollTarget") && (this.toggleAttribute("container-mode", this.target !== window), this.onScrollTarget());
34
40
  }
35
41
  get target() {
36
42
  return this.scrollTarget ? this.scrollTarget === window || this.scrollTarget === document.documentElement || this.scrollTarget === document.body ? window : this.scrollTarget : window;
@@ -52,7 +58,7 @@ var f = class extends a {
52
58
  toTop() {
53
59
  this.target.scrollTo({
54
60
  top: 0,
55
- behavior: this.smooth ? "smooth" : "auto"
61
+ behavior: this.scrollBehavior
56
62
  });
57
63
  }
58
64
  onEnter(e) {
@@ -72,14 +78,12 @@ var f = class extends a {
72
78
  };
73
79
  e([l({
74
80
  reflect: !0,
75
- converter: r
76
- })], f.prototype, "smooth", void 0), e([l({
81
+ attribute: "scroll-behavior",
82
+ useDefault: !0
83
+ })], f.prototype, "scrollBehavior", null), e([l({
77
84
  type: Boolean,
78
85
  reflect: !0
79
- })], f.prototype, "visible", void 0), e([l({
80
- type: Object,
81
- attribute: !1
82
- })], f.prototype, "scrollTarget", void 0), e([l({
86
+ })], f.prototype, "visible", void 0), e([l({ attribute: !1 })], f.prototype, "scrollTarget", void 0), e([l({
83
87
  type: Number,
84
88
  reflect: !0
85
89
  })], f.prototype, "threshold", null), e([u()], f.prototype, "eventController", void 0), f = e([c("web-ui-back-top")], f);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/back-top/index.ts"],"sourcesContent":["import { getRootScrollTop } from '@greypan/browser-kit'\nimport { html, LitElement, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport '@/components/icon'\nimport '@/components/button'\nimport { lucideArrowUpToLine } from '@/icons'\nimport { normalizeNumber } from '@/shared/normalize'\nimport { booleanWithFalseString } from '@/shared/property-converters/boolean-with-false-string'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-back-top')\nexport class WebUiBackTop extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ reflect: true, converter: booleanWithFalseString }) smooth = true\n @property({ type: Boolean, reflect: true }) visible = false\n @property({ type: Object, attribute: false }) scrollTarget: HTMLElement | Window = window\n\n @property({ type: Number, reflect: true })\n get threshold(): number {\n return this._threshold\n }\n set threshold(v: number) {\n const old = this._threshold\n this._threshold = normalizeNumber(v, 0, 10000, 200)\n this.requestUpdate('threshold', old)\n }\n private _threshold = 200\n\n @state() private eventController?: AbortController\n\n override connectedCallback() {\n super.connectedCallback()\n this.computeVisible()\n this.onScrollTarget()\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.eventController?.abort()\n }\n\n protected override willUpdate(props: PropertyValues) {\n super.willUpdate(props)\n\n if (props.has('threshold') || props.has('scrollTarget')) {\n this.computeVisible()\n }\n if (props.has('scrollTarget') && this.hasUpdated) {\n this.onScrollTarget()\n }\n }\n\n private get target() {\n if (!this.scrollTarget) return window\n const isRoot =\n this.scrollTarget === window ||\n this.scrollTarget === document.documentElement ||\n this.scrollTarget === document.body\n return isRoot ? window : this.scrollTarget\n }\n\n private onScrollTarget() {\n this.eventController?.abort()\n this.eventController = new AbortController()\n\n this.target.addEventListener(\n 'scroll',\n () => {\n this.computeVisible()\n },\n { signal: this.eventController.signal, passive: true }\n )\n }\n\n private computeVisible() {\n const target = this.target\n if (target === window) {\n this.visible = getRootScrollTop() >= this.threshold\n } else if (target instanceof HTMLElement) {\n this.visible = target.scrollTop >= this.threshold\n } else {\n throw new Error('Prop scrollTarget must be HTMLElement or Window.')\n }\n }\n\n toTop() {\n this.target.scrollTo({\n top: 0,\n behavior: this.smooth ? 'smooth' : 'auto'\n })\n }\n\n private onEnter(e: KeyboardEvent) {\n if (e.key === 'Enter') {\n e.preventDefault()\n this.toTop()\n }\n }\n\n override render() {\n return html`\n <div class=\"back-top-inner\" role=\"button\" tabindex=\"0\" @click=${this.toTop} @keydown=${this.onEnter}>\n <slot>\n <web-ui-button tabindex=\"-1\" icon>\n <web-ui-icon .icon=${lucideArrowUpToLine}></web-ui-icon>\n </web-ui-button>\n </slot>\n </div>\n `\n }\n\n declare readonly $events: Record<string, never>\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-back-top': WebUiBackTop\n }\n}\n"],"mappings":";;;;;;;;;;;AAaO,IAAM,IAAN,cAA2B,EAAW;;EAgBtB,aAboD,KAAA,SAAA,IACnB,KAAA,UAAA,IAC6B,KAAA,eAAA,QAW9D,KAAA,aAAA;;;EAfI,KAAA,SAAA,EAAU,CAAK;;CAMxC,IACI,YAAoB;EACtB,OAAO,KAAK;CACd;CACA,IAAI,UAAU,GAAW;EACvB,IAAM,IAAM,KAAK;EAEjB,AADA,KAAK,aAAa,EAAgB,GAAG,GAAG,KAAO,GAAG,GAClD,KAAK,cAAc,aAAa,CAAG;CACrC;CAKA,oBAA6B;EAG3B,AAFA,MAAM,kBAAkB,GACxB,KAAK,eAAe,GACpB,KAAK,eAAe;CACtB;CAEA,uBAAgC;EAE9B,AADA,MAAM,qBAAqB,GAC3B,KAAK,iBAAiB,MAAM;CAC9B;CAEA,WAA8B,GAAuB;EAMnD,AALA,MAAM,WAAW,CAAK,IAElB,EAAM,IAAI,WAAW,KAAK,EAAM,IAAI,cAAc,MACpD,KAAK,eAAe,GAElB,EAAM,IAAI,cAAc,KAAK,KAAK,cACpC,KAAK,eAAe;CAExB;CAEA,IAAY,SAAS;EAMnB,OALK,KAAK,eAER,KAAK,iBAAiB,UACtB,KAAK,iBAAiB,SAAS,mBAC/B,KAAK,iBAAiB,SAAS,OACjB,SAAS,KAAK,eALC;CAMjC;CAEA,iBAAyB;EAIvB,AAHA,KAAK,iBAAiB,MAAM,GAC5B,KAAK,kBAAkB,IAAI,gBAAgB,GAE3C,KAAK,OAAO,iBACV,gBACM;GACJ,KAAK,eAAe;EACtB,GACA;GAAE,QAAQ,KAAK,gBAAgB;GAAQ,SAAS;EAAK,CACvD;CACF;CAEA,iBAAyB;EACvB,IAAM,IAAS,KAAK;EACpB,IAAI,MAAW,QACb,KAAK,UAAU,EAAiB,KAAK,KAAK;OACrC,IAAI,aAAkB,aAC3B,KAAK,UAAU,EAAO,aAAa,KAAK;OAExC,MAAU,MAAM,kDAAkD;CAEtE;CAEA,QAAQ;EACN,KAAK,OAAO,SAAS;GACnB,KAAK;GACL,UAAU,KAAK,SAAS,WAAW;EACrC,CAAC;CACH;CAEA,QAAgB,GAAkB;EAChC,AAAI,EAAE,QAAQ,YACZ,EAAE,eAAe,GACjB,KAAK,MAAM;CAEf;CAEA,SAAkB;EAChB,OAAO,CAAI;sEACuD,KAAK,MAAM,YAAY,KAAK,QAAQ;;;iCAGzE,EAAoB;;;;;CAKnD;AAGF;AAnGG,EAAA,CAAA,EAAS;CAAE,SAAS;CAAM,WAAW;AAAuB,CAAC,CAAA,GAAA,EAAA,WAAA,UAAA,KAAA,CAAA,GAC7D,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,WAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,WAAW;AAAM,CAAC,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,GAE3C,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,aAAA,IAAA,GAWxC,EAAA,CAAA,EAAM,CAAA,GAAA,EAAA,WAAA,mBAAA,KAAA,CAAA,GAnBR,IAAA,EAAA,CAAA,EAAc,iBAAiB,CAAA,GAAA,CAAA"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/back-top/index.ts"],"sourcesContent":["import { getRootScrollTop } from '@greypan/browser-kit'\nimport { html, LitElement, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport '@/components/icon'\nimport '@/components/button'\nimport { lucideArrowUpToLine } from '@/icons'\nimport { normalizeLiteral, normalizeNumber } from '@/shared/normalize'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-back-top')\nexport class WebUiBackTop extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ reflect: true, attribute: 'scroll-behavior', useDefault: true })\n get scrollBehavior(): 'smooth' | 'auto' {\n return this._scrollBehavior\n }\n set scrollBehavior(value: string) {\n const old = this._scrollBehavior\n this._scrollBehavior = normalizeLiteral(value, ['smooth', 'auto'] as const, 'smooth')\n this.requestUpdate('scrollBehavior', old)\n }\n private _scrollBehavior: 'smooth' | 'auto' = 'smooth'\n @property({ type: Boolean, reflect: true }) visible = false\n @property({ attribute: false }) scrollTarget: HTMLElement | Window = window\n\n @property({ type: Number, reflect: true })\n get threshold(): number {\n return this._threshold\n }\n set threshold(v: number) {\n const old = this._threshold\n this._threshold = normalizeNumber(v, 0, 10000, 200)\n this.requestUpdate('threshold', old)\n }\n private _threshold = 200\n\n @state() private eventController?: AbortController\n\n override connectedCallback() {\n super.connectedCallback()\n this.computeVisible()\n this.onScrollTarget()\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.eventController?.abort()\n }\n\n protected override willUpdate(props: PropertyValues) {\n super.willUpdate(props)\n\n if (props.has('threshold') || props.has('scrollTarget')) {\n this.computeVisible()\n }\n // 不设 hasUpdated 门控:scrollTarget 可能在首次更新完成前赋值(如框架 onMounted 中),\n // 此时 connectedCallback 已按 window 绑定,必须重绑到新容器;onScrollTarget 幂等,重复调用安全\n if (props.has('scrollTarget')) {\n // 容器模式:scrollTarget 为元素时按钮通过 sticky 悬浮于容器底部,而非固定视口角落\n this.toggleAttribute('container-mode', this.target !== window)\n this.onScrollTarget()\n }\n }\n\n private get target() {\n if (!this.scrollTarget) return window\n const isRoot =\n this.scrollTarget === window ||\n this.scrollTarget === document.documentElement ||\n this.scrollTarget === document.body\n return isRoot ? window : this.scrollTarget\n }\n\n private onScrollTarget() {\n this.eventController?.abort()\n this.eventController = new AbortController()\n\n this.target.addEventListener(\n 'scroll',\n () => {\n this.computeVisible()\n },\n { signal: this.eventController.signal, passive: true }\n )\n }\n\n private computeVisible() {\n const target = this.target\n if (target === window) {\n this.visible = getRootScrollTop() >= this.threshold\n } else if (target instanceof HTMLElement) {\n this.visible = target.scrollTop >= this.threshold\n } else {\n throw new Error('Prop scrollTarget must be HTMLElement or Window.')\n }\n }\n\n toTop() {\n this.target.scrollTo({\n top: 0,\n behavior: this.scrollBehavior\n })\n }\n\n private onEnter(e: KeyboardEvent) {\n if (e.key === 'Enter') {\n e.preventDefault()\n this.toTop()\n }\n }\n\n override render() {\n return html`\n <div class=\"back-top-inner\" role=\"button\" tabindex=\"0\" @click=${this.toTop} @keydown=${this.onEnter}>\n <slot>\n <web-ui-button tabindex=\"-1\" icon>\n <web-ui-icon .icon=${lucideArrowUpToLine}></web-ui-icon>\n </web-ui-button>\n </slot>\n </div>\n `\n }\n\n declare readonly $events: Record<string, never>\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-back-top': WebUiBackTop\n }\n}\n"],"mappings":";;;;;;;;;;AAYO,IAAM,IAAN,cAA2B,EAAW;;EAyBtB,aAbwB,KAAA,kBAAA,UACS,KAAA,UAAA,IACe,KAAA,eAAA,QAWhD,KAAA,aAAA;;;EAxBI,KAAA,SAAA,EAAU,CAAK;;CAExC,IACI,iBAAoC;EACtC,OAAO,KAAK;CACd;CACA,IAAI,eAAe,GAAe;EAChC,IAAM,IAAM,KAAK;EAEjB,AADA,KAAK,kBAAkB,EAAiB,GAAO,CAAC,UAAU,MAAM,GAAY,QAAQ,GACpF,KAAK,cAAc,kBAAkB,CAAG;CAC1C;CAKA,IACI,YAAoB;EACtB,OAAO,KAAK;CACd;CACA,IAAI,UAAU,GAAW;EACvB,IAAM,IAAM,KAAK;EAEjB,AADA,KAAK,aAAa,EAAgB,GAAG,GAAG,KAAO,GAAG,GAClD,KAAK,cAAc,aAAa,CAAG;CACrC;CAKA,oBAA6B;EAG3B,AAFA,MAAM,kBAAkB,GACxB,KAAK,eAAe,GACpB,KAAK,eAAe;CACtB;CAEA,uBAAgC;EAE9B,AADA,MAAM,qBAAqB,GAC3B,KAAK,iBAAiB,MAAM;CAC9B;CAEA,WAA8B,GAAuB;EAQnD,AAPA,MAAM,WAAW,CAAK,IAElB,EAAM,IAAI,WAAW,KAAK,EAAM,IAAI,cAAc,MACpD,KAAK,eAAe,GAIlB,EAAM,IAAI,cAAc,MAE1B,KAAK,gBAAgB,kBAAkB,KAAK,WAAW,MAAM,GAC7D,KAAK,eAAe;CAExB;CAEA,IAAY,SAAS;EAMnB,OALK,KAAK,eAER,KAAK,iBAAiB,UACtB,KAAK,iBAAiB,SAAS,mBAC/B,KAAK,iBAAiB,SAAS,OACjB,SAAS,KAAK,eALC;CAMjC;CAEA,iBAAyB;EAIvB,AAHA,KAAK,iBAAiB,MAAM,GAC5B,KAAK,kBAAkB,IAAI,gBAAgB,GAE3C,KAAK,OAAO,iBACV,gBACM;GACJ,KAAK,eAAe;EACtB,GACA;GAAE,QAAQ,KAAK,gBAAgB;GAAQ,SAAS;EAAK,CACvD;CACF;CAEA,iBAAyB;EACvB,IAAM,IAAS,KAAK;EACpB,IAAI,MAAW,QACb,KAAK,UAAU,EAAiB,KAAK,KAAK;OACrC,IAAI,aAAkB,aAC3B,KAAK,UAAU,EAAO,aAAa,KAAK;OAExC,MAAU,MAAM,kDAAkD;CAEtE;CAEA,QAAQ;EACN,KAAK,OAAO,SAAS;GACnB,KAAK;GACL,UAAU,KAAK;EACjB,CAAC;CACH;CAEA,QAAgB,GAAkB;EAChC,AAAI,EAAE,QAAQ,YACZ,EAAE,eAAe,GACjB,KAAK,MAAM;CAEf;CAEA,SAAkB;EAChB,OAAO,CAAI;sEACuD,KAAK,MAAM,YAAY,KAAK,QAAQ;;;iCAGzE,EAAoB;;;;;CAKnD;AAGF;AAhHG,EAAA,CAAA,EAAS;CAAE,SAAS;CAAM,WAAW;CAAmB,YAAY;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,kBAAA,IAAA,GAU1E,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,WAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS,EAAE,WAAW,GAAM,CAAC,CAAA,GAAA,EAAA,WAAA,gBAAA,KAAA,CAAA,GAE7B,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,aAAA,IAAA,GAWxC,EAAA,CAAA,EAAM,CAAA,GAAA,EAAA,WAAA,mBAAA,KAAA,CAAA,GA5BR,IAAA,EAAA,CAAA,EAAc,iBAAiB,CAAA,GAAA,CAAA"}
@@ -1,5 +1,5 @@
1
1
  //#region src/components/back-top/style.css?inline
2
- var e = "*,:before,:after{box-sizing:border-box}:host{--wui-shadow-glass:var(--wui-shadow-pop);display:block}.back-top-inner{pointer-events:none;z-index:var(--web-ui-back-top-z-index,var(--wui-layer-auxiliary,20));top:var(--web-ui-back-top-top,auto);right:var(--web-ui-back-top-right,20px);bottom:var(--web-ui-back-top-bottom,20px);left:var(--web-ui-back-top-left,auto);visibility:hidden;opacity:0;position:fixed}.back-top-inner .back-top-inner:focus-visible{outline:3px solid var(--wui-focus-ring,#08f6);outline-offset:2px;border-radius:2147483647px}:host([visible]) .back-top-inner{pointer-events:auto;visibility:visible;opacity:1}";
2
+ var e = "*,:before,:after{box-sizing:border-box}:host{--wui-shadow-glass:var(--wui-shadow-pop);display:block}:host([container-mode]){bottom:var(--web-ui-back-top-bottom,20px);position:sticky}.back-top-inner{pointer-events:none;z-index:var(--web-ui-back-top-z-index,var(--wui-layer-auxiliary,20));top:var(--web-ui-back-top-top,auto);right:var(--web-ui-back-top-right,20px);bottom:var(--web-ui-back-top-bottom,20px);left:var(--web-ui-back-top-left,auto);visibility:hidden;opacity:0;position:fixed}.back-top-inner .back-top-inner:focus-visible{outline:3px solid var(--wui-focus-ring,#08f6);outline-offset:2px;border-radius:2147483647px}:host([container-mode]) .back-top-inner{margin-right:var(--web-ui-back-top-right,20px);margin-left:var(--web-ui-back-top-left,auto);position:static}:host([visible]) .back-top-inner{pointer-events:auto;visibility:visible;opacity:1}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/back-top/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n --wui-shadow-glass: var(--wui-shadow-pop);\n display: block;\n}\n\n.back-top-inner {\n pointer-events: none;\n\n position: fixed;\n z-index: var(--web-ui-back-top-z-index, var(--wui-layer-auxiliary, 20));\n top: var(--web-ui-back-top-top, auto);\n right: var(--web-ui-back-top-right, 20px);\n bottom: var(--web-ui-back-top-bottom, 20px);\n left: var(--web-ui-back-top-left, auto);\n\n visibility: hidden;\n opacity: 0;\n\n .back-top-inner:focus-visible {\n border-radius: calc(infinity * 1px);\n outline: 3px solid var(--wui-focus-ring, rgb(0 136 255 / 0.4));\n outline-offset: 2px;\n }\n}\n\n:host([visible]) .back-top-inner {\n pointer-events: auto;\n visibility: visible;\n opacity: 1;\n}\n"],"mappings":""}
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/back-top/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n --wui-shadow-glass: var(--wui-shadow-pop);\n display: block;\n}\n\n/* 容器模式:scrollTarget 为元素时按钮悬浮于容器底部(元素需置于容器内部),\n 横向偏移沿用 --web-ui-back-top-left/right,纵向沿用 --web-ui-back-top-bottom */\n:host([container-mode]) {\n position: sticky;\n bottom: var(--web-ui-back-top-bottom, 20px);\n}\n\n.back-top-inner {\n pointer-events: none;\n\n position: fixed;\n z-index: var(--web-ui-back-top-z-index, var(--wui-layer-auxiliary, 20));\n top: var(--web-ui-back-top-top, auto);\n right: var(--web-ui-back-top-right, 20px);\n bottom: var(--web-ui-back-top-bottom, 20px);\n left: var(--web-ui-back-top-left, auto);\n\n visibility: hidden;\n opacity: 0;\n\n .back-top-inner:focus-visible {\n border-radius: calc(infinity * 1px);\n outline: 3px solid var(--wui-focus-ring, rgb(0 136 255 / 0.4));\n outline-offset: 2px;\n }\n}\n\n/* 容器模式下按钮脱离 fixed,改为流内布局并按偏移变量左/右对齐 */\n:host([container-mode]) .back-top-inner {\n position: static;\n margin-right: var(--web-ui-back-top-right, 20px);\n margin-left: var(--web-ui-back-top-left, auto);\n}\n\n:host([visible]) .back-top-inner {\n pointer-events: auto;\n visibility: visible;\n opacity: 1;\n}\n"],"mappings":""}
@@ -1,8 +1,8 @@
1
1
  import { LitElement } from 'lit';
2
2
  export declare class WebUiContextMenu extends LitElement {
3
- static styles: import('lit').CSSResult[];
3
+ static styles: import('lit').CSSResult;
4
4
  disabled: boolean;
5
- lockScroll: boolean;
5
+ noScrollLock: boolean;
6
6
  private _isOpen;
7
7
  private _x;
8
8
  private _y;
@@ -14,7 +14,7 @@ export declare class WebUiContextMenu extends LitElement {
14
14
  private _ignoreOutsideClickTimer?;
15
15
  private _hoverCleanupFns;
16
16
  private _menu?;
17
- private _hasScrollLock;
17
+ private readonly _scrollLock;
18
18
  private _restoreFocusTarget?;
19
19
  private _shouldOpenInstantly;
20
20
  /** 当前菜单是否打开 */
@@ -55,7 +55,6 @@ export declare class WebUiContextMenu extends LitElement {
55
55
  private _onKeydown;
56
56
  private _onClickOutside;
57
57
  private _onMenuClick;
58
- private _getDropdownItemFromEvent;
59
58
  private _getLevelItems;
60
59
  private _bindLevelHovers;
61
60
  private _ignoreCurrentOutsideClick;