@greypan/web-ui 12.0.0 → 12.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/README.CN.md +2 -0
  2. package/README.md +2 -0
  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/autocomplete/index.d.ts +5 -0
  8. package/dist/components/autocomplete/index.d.ts.map +1 -1
  9. package/dist/components/autocomplete/index.js +17 -1
  10. package/dist/components/autocomplete/index.js.map +1 -1
  11. package/dist/components/autocomplete/style.js +1 -1
  12. package/dist/components/autocomplete/style.js.map +1 -1
  13. package/dist/components/back-top/style.js +1 -1
  14. package/dist/components/back-top/style.js.map +1 -1
  15. package/dist/components/button-group/style.js +1 -1
  16. package/dist/components/button-group/style.js.map +1 -1
  17. package/dist/components/collapse/index.d.ts +2 -2
  18. package/dist/components/collapse/index.js.map +1 -1
  19. package/dist/components/collapse/style.js.map +1 -1
  20. package/dist/components/dialog/index.d.ts +5 -1
  21. package/dist/components/dialog/index.d.ts.map +1 -1
  22. package/dist/components/dialog/index.js +16 -4
  23. package/dist/components/dialog/index.js.map +1 -1
  24. package/dist/components/dialog/style.js +1 -1
  25. package/dist/components/dialog/style.js.map +1 -1
  26. package/dist/components/drawer/index.js.map +1 -1
  27. package/dist/components/drawer/style.js.map +1 -1
  28. package/dist/components/dropdown/index.js +1 -1
  29. package/dist/components/icon/index.d.ts +1 -1
  30. package/dist/components/icon/index.d.ts.map +1 -1
  31. package/dist/components/index.d.ts +3 -2
  32. package/dist/components/index.d.ts.map +1 -1
  33. package/dist/components/index.js +2 -1
  34. package/dist/components/layout/index.d.ts +8 -0
  35. package/dist/components/layout/index.d.ts.map +1 -1
  36. package/dist/components/layout/index.js +23 -13
  37. package/dist/components/layout/index.js.map +1 -1
  38. package/dist/components/layout/style.js +1 -1
  39. package/dist/components/layout/style.js.map +1 -1
  40. package/dist/components/popover/index.js +1 -1
  41. package/dist/components/popover/style.js +1 -1
  42. package/dist/components/popover/style.js.map +1 -1
  43. package/dist/components/progress/index.d.ts +30 -0
  44. package/dist/components/progress/index.d.ts.map +1 -0
  45. package/dist/components/progress/index.js +103 -0
  46. package/dist/components/progress/index.js.map +1 -0
  47. package/dist/components/progress/style.js +5 -0
  48. package/dist/components/progress/style.js.map +1 -0
  49. package/dist/components/segmented-trigger/index.d.ts +2 -0
  50. package/dist/components/segmented-trigger/index.d.ts.map +1 -1
  51. package/dist/components/segmented-trigger/index.js +7 -0
  52. package/dist/components/segmented-trigger/index.js.map +1 -1
  53. package/dist/components/select/index.d.ts +13 -0
  54. package/dist/components/select/index.d.ts.map +1 -1
  55. package/dist/components/select/index.js +41 -3
  56. package/dist/components/select/index.js.map +1 -1
  57. package/dist/components/select/style.js +1 -1
  58. package/dist/components/select/style.js.map +1 -1
  59. package/dist/components/spinner/index.d.ts +0 -3
  60. package/dist/components/spinner/index.d.ts.map +1 -1
  61. package/dist/components/spinner/index.js +5 -20
  62. package/dist/components/spinner/index.js.map +1 -1
  63. package/dist/components/spinner/style.js +1 -1
  64. package/dist/components/spinner/style.js.map +1 -1
  65. package/dist/components/theme/style.js +1 -1
  66. package/dist/components/theme/style.js.map +1 -1
  67. package/dist/components/tooltip/index.js +1 -1
  68. package/dist/components/tooltip/style.js +1 -1
  69. package/dist/components/tooltip/style.js.map +1 -1
  70. package/dist/icons/generated/ant-design-sync-outlined.d.ts +7 -0
  71. package/dist/icons/generated/ant-design-sync-outlined.d.ts.map +1 -0
  72. package/dist/icons/generated/ant-design-sync-outlined.js +9 -0
  73. package/dist/icons/generated/ant-design-sync-outlined.js.map +1 -0
  74. package/dist/icons/generated/ic-baseline-phonelink.d.ts +7 -0
  75. package/dist/icons/generated/ic-baseline-phonelink.d.ts.map +1 -0
  76. package/dist/icons/generated/ic-baseline-phonelink.js +9 -0
  77. package/dist/icons/generated/ic-baseline-phonelink.js.map +1 -0
  78. package/dist/icons/generated/ic-outline-motion-photos-off.d.ts +7 -0
  79. package/dist/icons/generated/ic-outline-motion-photos-off.d.ts.map +1 -0
  80. package/dist/icons/generated/ic-outline-motion-photos-off.js +9 -0
  81. package/dist/icons/generated/ic-outline-motion-photos-off.js.map +1 -0
  82. package/dist/icons/generated/ic-outline-motion-photos-on.d.ts +7 -0
  83. package/dist/icons/generated/ic-outline-motion-photos-on.d.ts.map +1 -0
  84. package/dist/icons/generated/ic-outline-motion-photos-on.js +9 -0
  85. package/dist/icons/generated/ic-outline-motion-photos-on.js.map +1 -0
  86. package/dist/icons/generated/ic-round-menu-open.d.ts +7 -0
  87. package/dist/icons/generated/ic-round-menu-open.d.ts.map +1 -0
  88. package/dist/icons/generated/ic-round-menu-open.js +9 -0
  89. package/dist/icons/generated/ic-round-menu-open.js.map +1 -0
  90. package/dist/icons/generated/lucide-accessibility.d.ts +7 -0
  91. package/dist/icons/generated/lucide-accessibility.d.ts.map +1 -0
  92. package/dist/icons/generated/lucide-accessibility.js +9 -0
  93. package/dist/icons/generated/lucide-accessibility.js.map +1 -0
  94. package/dist/icons/generated/lucide-boxes.d.ts +7 -0
  95. package/dist/icons/generated/lucide-boxes.d.ts.map +1 -0
  96. package/dist/icons/generated/lucide-boxes.js +9 -0
  97. package/dist/icons/generated/lucide-boxes.js.map +1 -0
  98. package/dist/icons/generated/lucide-contrast.d.ts +7 -0
  99. package/dist/icons/generated/lucide-contrast.d.ts.map +1 -0
  100. package/dist/icons/generated/lucide-contrast.js +9 -0
  101. package/dist/icons/generated/lucide-contrast.js.map +1 -0
  102. package/dist/icons/generated/lucide-keyboard.d.ts +7 -0
  103. package/dist/icons/generated/lucide-keyboard.d.ts.map +1 -0
  104. package/dist/icons/generated/lucide-keyboard.js +9 -0
  105. package/dist/icons/generated/lucide-keyboard.js.map +1 -0
  106. package/dist/icons/generated/lucide-languages.d.ts +7 -0
  107. package/dist/icons/generated/lucide-languages.d.ts.map +1 -0
  108. package/dist/icons/generated/lucide-languages.js +9 -0
  109. package/dist/icons/generated/lucide-languages.js.map +1 -0
  110. package/dist/icons/generated/lucide-list.d.ts +7 -0
  111. package/dist/icons/generated/lucide-list.d.ts.map +1 -0
  112. package/dist/icons/generated/lucide-list.js +9 -0
  113. package/dist/icons/generated/lucide-list.js.map +1 -0
  114. package/dist/icons/generated/lucide-moon.d.ts +7 -0
  115. package/dist/icons/generated/lucide-moon.d.ts.map +1 -0
  116. package/dist/icons/generated/lucide-moon.js +9 -0
  117. package/dist/icons/generated/lucide-moon.js.map +1 -0
  118. package/dist/icons/generated/lucide-sparkles.d.ts +7 -0
  119. package/dist/icons/generated/lucide-sparkles.d.ts.map +1 -0
  120. package/dist/icons/generated/lucide-sparkles.js +9 -0
  121. package/dist/icons/generated/lucide-sparkles.js.map +1 -0
  122. package/dist/icons/generated/lucide-sun.d.ts +7 -0
  123. package/dist/icons/generated/lucide-sun.d.ts.map +1 -0
  124. package/dist/icons/generated/lucide-sun.js +9 -0
  125. package/dist/icons/generated/lucide-sun.js.map +1 -0
  126. package/dist/icons/generated/tabler-brand-typescript.d.ts +7 -0
  127. package/dist/icons/generated/tabler-brand-typescript.d.ts.map +1 -0
  128. package/dist/icons/generated/tabler-brand-typescript.js +9 -0
  129. package/dist/icons/generated/tabler-brand-typescript.js.map +1 -0
  130. package/dist/icons/index.d.ts +87 -72
  131. package/dist/icons/index.js +16 -1
  132. package/dist/shared/group-management/index.js.map +1 -1
  133. package/dist/shared/locale/builtin-labels.js +2 -1
  134. package/dist/shared/locale/builtin-labels.js.map +1 -1
  135. package/dist/shared/open-state/index.js.map +1 -1
  136. package/dist/shared/overlay/overlay.js.map +1 -1
  137. package/package.json +4 -2
@@ -25,6 +25,7 @@ import { WebUiLayout } from "./layout/index.js";
25
25
  import { WebUiMiddleEllipsis } from "./middle-ellipsis/index.js";
26
26
  import { WebUiOption } from "./option/index.js";
27
27
  import { WebUiPopover } from "./popover/index.js";
28
+ import { WebUiProgress } from "./progress/index.js";
28
29
  import { WebUiRadio } from "./radio/index.js";
29
30
  import { WebUiRadioGroup } from "./radio-group/index.js";
30
31
  import { WebUiSegmented } from "./segmented/index.js";
@@ -39,4 +40,4 @@ import { WebUiToast } from "./toast/toast.js";
39
40
  import "./toast/index.js";
40
41
  import { WebUiSwitch } from "./switch/index.js";
41
42
  import { WebUiTooltip } from "./tooltip/index.js";
42
- export { WebUiAutocomplete, WebUiAvatar, WebUiBackTop, WebUiBadge, WebUiButton, WebUiButtonGroup, WebUiCheckbox, WebUiCheckboxGroup, WebUiCollapse, WebUiContextMenu, WebUiDialog, WebUiDrawer, WebUiDropdown, WebUiDropdownDivider, WebUiDropdownHeader, WebUiDropdownItem, WebUiEditableText, WebUiEmpty, WebUiIcon, WebUiInput, WebUiInputNumber, WebUiLayout, WebUiMiddleEllipsis, WebUiOption, WebUiPopover, WebUiRadio, WebUiRadioGroup, WebUiSegmented, WebUiSegmentedTrigger, WebUiSelect, WebUiSlider, WebUiSpinner, WebUiSvgDrawLines, WebUiSwitch, WebUiTextarea, WebUiTheme, WebUiToast, WebUiTooltip, imagePreview, toast };
43
+ export { WebUiAutocomplete, WebUiAvatar, WebUiBackTop, WebUiBadge, WebUiButton, WebUiButtonGroup, WebUiCheckbox, WebUiCheckboxGroup, WebUiCollapse, WebUiContextMenu, WebUiDialog, WebUiDrawer, WebUiDropdown, WebUiDropdownDivider, WebUiDropdownHeader, WebUiDropdownItem, WebUiEditableText, WebUiEmpty, WebUiIcon, WebUiInput, WebUiInputNumber, WebUiLayout, WebUiMiddleEllipsis, WebUiOption, WebUiPopover, WebUiProgress, WebUiRadio, WebUiRadioGroup, WebUiSegmented, WebUiSegmentedTrigger, WebUiSelect, WebUiSlider, WebUiSpinner, WebUiSvgDrawLines, WebUiSwitch, WebUiTextarea, WebUiTheme, WebUiToast, WebUiTooltip, imagePreview, toast };
@@ -33,6 +33,14 @@ export declare class WebUiLayout extends LitElement {
33
33
  * 侧边栏相关事件与属性在该模式下不产生可见效果。
34
34
  */
35
35
  noSidebar: boolean;
36
+ /**
37
+ * 启用桌面端 Sidebar 的 collapse 入口。
38
+ *
39
+ * 只有声明了本属性,layout 才渲染收起 / 展开 Toggle,也才会因为点击而派发
40
+ * `sidebar-collapsed-change`。桌面宽度仍由受控属性 `sidebar-collapsed` 决定,
41
+ * 未声明本属性不会阻止它收窄;移动端 Drawer 的 header Toggle 不受影响。
42
+ */
43
+ sidebarCollapsible: boolean;
36
44
  /**
37
45
  * 桌面端 Sidebar 的持久布局偏好。
38
46
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/layout/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;iBAyCiB;EACf;EACA;EACA;EACA;EACA;;qBAIW,oBAAoB;SACf,sBAAM;;;;;;;;EAUtB;;;;;;;;;;;;EAcA;EAGA;EAGA;;EAIA;;;;;EAOA;;EAIA;;;;;;;EASA;EAGA;;EAGgC,aAAa;UASrC;;;;;;;MAQJ;UAII;mBAES;UAST;EAUC,yBAAyB,cAAc,yBAAyB;mBAoBxD;mBAMA;UAET;UASA;UAUA;UAUA;UAKA;UACA;UAES;UAET;UAKA;UAMA;UASA;UAIA;UA+CA;UAUA;UAoDA;EAQC;EAQA;EAOA;UAID;UAIA;YAiBW,WAAW,SAAS;EAK9B,wBAAM;WAgIE;IACf,4BAA4B;MAAc;;IAC1C,uBAAuB;MAAc;;IACrC,wBAAwB;MAAc;;IACtC,iBAAiB;MAAc;;;;QAI3B;YACI;IACR,iBAAiB"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/layout/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;iBAyCiB;EACf;EACA;EACA;EACA;EACA;;qBAIW,oBAAoB;SACf,sBAAM;;;;;;;;EAUtB;;;;;;;;EAUA;;;;;;;;;;;;EAcA;EAGA;EAGA;;EAIA;;;;;EAOA;;EAIA;;;;;;;EASA;EAGA;;EAGgC,aAAa;UASrC;;;;;;;MAQJ;UAII;mBAES;UAST;EAUC,yBAAyB,cAAc,yBAAyB;mBAoBxD;mBAMA;UAET;UASA;UAUA;UAUA;UAKA;UACA;UAES;UAET;UAKA;UAMA;UASA;UAIA;UA+CA;UAUA;UAoDA;EAQC;EAQA;EAOA;UAID;UAIA;YAiBW,WAAW,SAAS;EAK9B,wBAAM;WAqIE;IACf,4BAA4B;MAAc;;IAC1C,uBAAuB;MAAc;;IACrC,wBAAwB;MAAc;;IACtC,iBAAiB;MAAc;;;;QAI3B;YACI;IACR,iBAAiB"}
@@ -2,6 +2,7 @@ import __decorate from "../../_virtual/_@oxc-project_runtime@0.155.0/helpers/esm
2
2
  import "../icon/index.js";
3
3
  import glass_default from "../../classes/glass.js";
4
4
  import radix_icons_panel_left_minimized_default from "../../icons/generated/radix-icons-panel-left-minimized.js";
5
+ import ic_round_menu_open_default from "../../icons/generated/ic-round-menu-open.js";
5
6
  import "../button/index.js";
6
7
  import { resolveBuiltinLabelSet } from "../../shared/locale/builtin-labels.js";
7
8
  import glow_default from "../../classes/glow.js";
@@ -27,9 +28,10 @@ let WebUiLayout = class WebUiLayout extends LitElement {
27
28
  constructor(..._args) {
28
29
  super(..._args);
29
30
  this.noSidebar = false;
31
+ this.sidebarCollapsible = false;
30
32
  this.sidebarCollapsed = false;
31
33
  this.sidebarWidth = "240px";
32
- this.collapsedWidth = "68px";
34
+ this.collapsedWidth = "60px";
33
35
  this.sidebarResizable = false;
34
36
  this.sidebarMinWidth = "";
35
37
  this.sidebarMaxWidth = "";
@@ -246,17 +248,20 @@ let WebUiLayout = class WebUiLayout extends LitElement {
246
248
  const isSidebarOpen = !this.sidebarCollapsed;
247
249
  const sidebarStyle = this._mobile ? {} : { width: this._resizeWidth ?? (this.sidebarCollapsed ? this.collapsedWidth : this.sidebarWidth) };
248
250
  const toggleLabel = resolveBuiltinLabelSet(this.ariaLabels, isSidebarOpen ? "collapseSidebar" : "expandSidebar");
249
- const desktopToggle = html`
250
- <web-ui-button
251
- class="sidebar-toggle"
252
- icon
253
- variant="secondary"
254
- @click="${this._toggleSidebar}"
255
- aria-label=${toggleLabel}
256
- >
257
- <web-ui-icon .icon=${radix_icons_panel_left_minimized_default}></web-ui-icon>
258
- </web-ui-button>
259
- `;
251
+ const desktopToggle = this.sidebarCollapsible ? html`
252
+ <web-ui-button
253
+ class="sidebar-toggle"
254
+ icon
255
+ variant="ghost"
256
+ @click="${this._toggleSidebar}"
257
+ aria-label=${toggleLabel}
258
+ >
259
+ <web-ui-icon
260
+ .icon=${ic_round_menu_open_default}
261
+ style=${styleMap({ transform: isSidebarOpen ? "none" : "scaleX(-1)" })}
262
+ ></web-ui-icon>
263
+ </web-ui-button>
264
+ ` : nothing;
260
265
  const sidebarViewport = html`
261
266
  <div class="sidebar-viewport">
262
267
  <slot name="sidebar"></slot>
@@ -294,7 +299,7 @@ let WebUiLayout = class WebUiLayout extends LitElement {
294
299
  >
295
300
  <div class="aside-panel wui-glass">
296
301
  ${sidebarViewport}
297
- <div class="sidebar-toggle-area">${desktopToggle}</div>
302
+ ${this.sidebarCollapsible ? html`<div class="sidebar-toggle-area">${desktopToggle}</div>` : nothing}
298
303
  </div>
299
304
  ${this.sidebarResizable && !this.sidebarCollapsed ? html`
300
305
  <div
@@ -344,6 +349,11 @@ __decorate([property({
344
349
  attribute: "no-sidebar",
345
350
  reflect: true
346
351
  })], WebUiLayout.prototype, "noSidebar", void 0);
352
+ __decorate([property({
353
+ type: Boolean,
354
+ attribute: "sidebar-collapsible",
355
+ reflect: true
356
+ })], WebUiLayout.prototype, "sidebarCollapsible", void 0);
347
357
  __decorate([property({
348
358
  type: Boolean,
349
359
  attribute: "sidebar-collapsed",
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["glass","glow","style","radixIconsPanelLeftMinimized"],"sources":["../../../src/components/layout/index.ts"],"sourcesContent":["import '@/components/button'\nimport '@/components/drawer'\nimport '@/components/icon'\n\nimport { html, LitElement, nothing, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { styleMap } from 'lit/directives/style-map.js'\n\nimport glass from '@/classes/glass.css?inline'\nimport glow from '@/classes/glow.css?inline'\nimport { radixIconsPanelLeftMinimized } from '@/icons'\nimport { attachDragGesture, clamp, type DragGestureHandle } from '@/shared/gesture'\nimport { resolveBuiltinLabelSet } from '@/shared/locale/builtin-labels'\nimport { defineVisibleAreaTracker, VisibleAreaController } from '@/shared/visible-area'\n\nimport style from './style.css?inline'\n\n/**\n * 移动端断点的媒体查询。\n *\n * 必须与 `style.css` 里那份 `@media (width <= 640px)` 是同一条条件:CSS 用它在窄视口隐藏桌面\n * aside,TS 用同一条件决定渲染哪一套树,两者错开就会出现「树还在桌面、aside 已经被藏起来」\n * 的空档。两份字面量的相等由 `components/drawer/__tests__/breakpoint-parity.spec.ts` 守着。\n */\nconst MOBILE_VIEWPORT_QUERY = '(width <= 640px)'\n\n/**\n * Layout 组件 - 提供页面布局框架,支持 collapsible sidebar 与无 sidebar 模式\n *\n * @fires sidebar-collapsed-change - 桌面端侧边栏 collapse 状态变更请求时触发\n * @fires sidebar-open-change - 移动端侧边栏 Drawer toggle 请求时触发\n * @fires sidebar-width-change - 桌面端侧边栏拖拽调宽结束时触发,携带最终宽度(受控请求)\n * @fires mobile-change - 视口跨越移动端断点、`mobile` 翻转时触发(派生状态通知)\n * @slot banner - 顶部 Banner 区域(全宽,随页面滚动,可选)\n * @slot header - 内容区顶部 header(sticky)\n * @slot sidebar - 侧边栏内容;Consumer 决定其内部固定区域与滚动容器;`no-sidebar` 置位时不渲染\n * @slot tabbar - 底部 tabbar 区域\n * @slot default - 主内容区\n */\n/** `web-ui-layout` 内置无障碍名的成组覆盖;每个 key 对应一处图标控件的 accessible name。 */\nexport interface LayoutAriaLabels {\n collapseSidebar?: string\n expandSidebar?: string\n resizeSidebar?: string\n openNavigationMenu?: string\n mainNavigation?: string\n}\n\n@customElement('web-ui-layout')\nexport class WebUiLayout extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(glow), unsafeCSS(style)]\n\n /**\n * 关闭整块 sidebar:桌面端不渲染 aside 与 collapse Toggle,移动端不渲染 header Toggle 与 Drawer。\n *\n * 它是布局模式而不是 collapse 的别名:`sidebar-collapsed` 只收窄桌面 aside,本属性把侧边栏\n * 结构本身从树里去掉。`sidebar` slot 随之不渲染(消费方节点仍留在 light DOM),\n * 侧边栏相关事件与属性在该模式下不产生可见效果。\n */\n @property({ type: Boolean, attribute: 'no-sidebar', reflect: true })\n noSidebar = false\n\n /**\n * 桌面端 Sidebar 的持久布局偏好。\n *\n * `true` 只会将桌面 Sidebar 收窄到 `collapsedWidth`,不会影响移动端 Drawer。\n * 它不与 `sidebarOpen` 合并:前者是跨视口保留的密度选择,后者是移动端瞬时的 modal 可见性。\n *\n * 两处渲染的是同一份 `sidebar` slot 内容,所以本属性只负责桌面 aside 的几何:Consumer\n * 若让自己的 slot 内容跟着它变形态(隐藏 label、改排布),必须按 `mobile` 收窄那个条件\n * (例如 `collapsed && !layout.mobile`,或 CSS 的 `web-ui-layout[mobile]`),\n * 否则 collapsed state 会一路渲染进移动端 Drawer(issue #195)。\n */\n @property({ type: Boolean, attribute: 'sidebar-collapsed', reflect: true })\n sidebarCollapsed = false\n\n @property({ type: String, attribute: 'sidebar-width', reflect: true })\n sidebarWidth = '240px'\n\n @property({ type: String, attribute: 'collapsed-width', reflect: true })\n collapsedWidth = '68px'\n\n /** 启用桌面端 Sidebar 拖拽调宽(collapsed state 隐藏 handle)。 */\n @property({ type: Boolean, attribute: 'sidebar-resizable', reflect: true })\n sidebarResizable = false\n\n /**\n * 拖拽调宽的下限(px,其他单位不解析);未设置时回退为 `collapsedWidth`,\n * 防止拖到不可用宽度。上限 `sidebar-max-width` 受内置 50vw 硬上限钳制。\n */\n @property({ type: String, attribute: 'sidebar-min-width', reflect: true })\n sidebarMinWidth = ''\n\n /** 拖拽调宽的上限(px,其他单位不解析);始终受内置 50vw 硬上限钳制。 */\n @property({ type: String, attribute: 'sidebar-max-width', reflect: true })\n sidebarMaxWidth = ''\n\n /**\n * 移动端 web-ui-drawer 的瞬时可见性。\n *\n * Drawer Toggle、Escape 和 backdrop 关闭都通过 `sidebar-open-change` 请求更新它;它不会改变桌面端的 collapse preference。\n * 因而不与 `sidebarCollapsed` 合并,避免响应式切换时将一次移动端交互误写为桌面端布局选择。\n */\n @property({ type: Boolean, attribute: 'sidebar-open', reflect: true })\n sidebarOpen = false\n\n @property({ type: Boolean, attribute: 'header-glow', reflect: true })\n headerGlow = false\n\n /** 内置无障碍名的成组覆盖;未提供的 key 回退内置英文文案(见 ADR-0016)。 */\n @property({ attribute: false }) ariaLabels?: LayoutAriaLabels\n\n /*\n * `mobile` 是派生输出,不是第二个输入:像 `web-ui-theme` 的 `resolved-appearance` 一样由组件\n * 独占写入,读取取 `MOBILE_VIEWPORT_QUERY`。外部写 attribute 会在同一次 reaction 内被恢复,\n * 写 property 不生效(只读 accessor,严格模式下抛错)——两种写法都不构成本属性之外的\n * 第三个视口来源。\n */\n @property({ type: Boolean, attribute: 'mobile', reflect: true })\n private _mobile = false\n\n /**\n * 当前视口是否落在移动端分支(即 `MOBILE_VIEWPORT_QUERY` 命中)。\n *\n * 与组件渲染哪一套树、以及 `mobile-change` 的派发是同一个值:翻转瞬间三者一起变,\n * Consumer 不必自己写断点判断,也不会与 layout 内部的树切换错开半拍。派生状态只读。\n */\n get mobile(): boolean {\n return this._mobile\n }\n\n private _mobileQuery: MediaQueryList | null = null\n\n private readonly _onMobileQueryChange = (event: MediaQueryListEvent) => {\n this._syncMobile(event.matches)\n }\n\n /*\n * 本方法由媒体查询翻转驱动,也被连接时的初次求值调用;attribute 的恢复路径见\n * `attributeChangedCallback`,那里不改判、也不派发通知,所以 `mobile-change` 只在\n * 视口真正跨断点时出现一次(窄视口挂载时那次算第一次)。\n */\n private _syncMobile(next: boolean) {\n if (next === this._mobile) return\n this._mobile = next\n // 跨断点会卸载桌面 layout(resize handle 随之消失);必须先终结进行中的拖拽,\n // 否则手势悬挂,切回桌面后所有新拖拽都在入口被 `_isResizing()` 拦下。\n if (this._isResizing()) this._resetActiveResize()\n this.toggleAttribute('mobile', next)\n this.dispatchEvent(new CustomEvent('mobile-change', { detail: { mobile: next }, bubbles: true, composed: true }))\n }\n\n override attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {\n super.attributeChangedCallback(name, oldValue, newValue)\n // super 已把外部值写进 backing field,所以这里要按媒体查询重算,不能拿字段当真相。\n if (name !== 'mobile') return\n // 无 matchMedia 的环境没有视口可判,`mobile` 恒为 false(见 connectedCallback)。\n const derived = this._mobileQuery?.matches ?? false\n if (this._mobile !== derived) this._mobile = derived\n /*\n * 布尔 attribute 只论存在性,但值本身也要归位:`setAttribute('mobile', 'false')` 在移动端\n * 会让 backing field 与 attribute 的**存在性**一致、值却留在宿主上(`getAttribute` 会回读\n * 到 consumer 写的那串)。组件写的形态是空串,这里按同一形态恢复,主题的\n * `resolved-appearance` 先例同理。\n */\n if (derived) {\n if (this.getAttribute('mobile') !== '') this.setAttribute('mobile', '')\n return\n }\n if (this.hasAttribute('mobile')) this.removeAttribute('mobile')\n }\n\n private readonly _visibleBanner = defineVisibleAreaTracker({\n onVisibleAreaChange: area => {\n this.style.setProperty('--wui-layout-visible-banner-height', `${area.height}px`)\n }\n }).make()\n\n private readonly _visibleBannerController = new VisibleAreaController(this, this._visibleBanner)\n\n private _toggleSidebar() {\n if (this._mobile) {\n this._emitSidebarOpenChange(!this.sidebarOpen)\n return\n }\n\n this._emitSidebarCollapsedChange(!this.sidebarCollapsed)\n }\n\n private _emitSidebarCollapsedChange(collapsed: boolean) {\n this.dispatchEvent(\n new CustomEvent('sidebar-collapsed-change', {\n detail: { collapsed },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _emitSidebarOpenChange(open: boolean) {\n this.dispatchEvent(\n new CustomEvent('sidebar-open-change', {\n detail: { open },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _onDrawerChange(e: CustomEvent<{ open: boolean }>) {\n e.stopPropagation()\n this._emitSidebarOpenChange(e.detail.open)\n }\n\n private _resizeGesture: DragGestureHandle | null = null\n private _resizeStartWidth = 0\n // 拖拽中的临时宽度;由 render 的 styleMap 统一写入,避免与 Lit 样式管理竞争。\n @state() private _resizeWidth: string | null = null\n\n private _parsePx(value: string, fallback: number): number {\n const parsed = Number.parseFloat(value)\n return Number.isFinite(parsed) ? parsed : fallback\n }\n\n private _resolveSidebarMinWidth(): number {\n return this.sidebarMinWidth\n ? this._parsePx(this.sidebarMinWidth, this._parsePx(this.collapsedWidth, 68))\n : this._parsePx(this.collapsedWidth, 68)\n }\n\n private _resolveSidebarMaxWidth(): number {\n // 组件内置硬上限:Sidebar 最多占视口一半(无需调用方设置)。\n // 调用方 sidebar-max-width 在该上限内生效,防止拖拽占据整个视口。\n const halfViewport = Math.floor(window.innerWidth / 2)\n const configured = this.sidebarMaxWidth ? this._parsePx(this.sidebarMaxWidth, halfViewport) : halfViewport\n const upper = Math.min(configured, halfViewport)\n return Math.max(upper, this._resolveSidebarMinWidth())\n }\n\n private _isResizing(): boolean {\n return this._resizeGesture?.isDragging() ?? false\n }\n\n private _handleResizePointerDown(e: PointerEvent) {\n if (!this.sidebarResizable || this.sidebarCollapsed || this._mobile || this._isResizing()) return\n // jsdom 等环境的 PointerEvent 可能缺失 isPrimary,仅在明确为 false(多点触控副指针)时拒绝。\n if (e.isPrimary === false) return\n\n const aside = this.renderRoot.querySelector('aside')\n if (!aside) return\n\n // 拖拽起点以 computed width 为准,并丢弃键盘未提交的调整(Enter 才提交、\n // Escape 撤回;抓取隐式放弃),保证零位移松手不会派发陈旧宽度。\n this._resizeStartWidth = this._parsePx(window.getComputedStyle(aside).width, this._parsePx(this.sidebarWidth, 240))\n this._resizeWidth = null\n aside.classList.add('is-resizing')\n\n this._resizeGesture = attachDragGesture(e, {\n axis: 'x',\n onMove: info => {\n // 向右拖增宽、向左拖收窄(handle 在右缘);钳制在 [min, max] 与视口内。\n const next = this._resizeStartWidth + info.deltaX\n const clamped = clamp(next, this._resolveSidebarMinWidth(), this._resolveSidebarMaxWidth())\n this._resizeWidth = `${Math.round(clamped)}px`\n },\n onEnd: () => {\n // 零位移松手(点击而非拖拽)不视为调宽动作:按 cancel 语义静默收尾。\n if (this._resizeWidth === null) {\n this._resetActiveResize()\n return\n }\n const width = this._resizeWidth\n this._resetActiveResize()\n // 受控契约:松手派发请求并交还 prop 管辖,由 Consumer 回写 sidebar-width。\n this.dispatchEvent(\n new CustomEvent('sidebar-width-change', {\n detail: { width },\n bubbles: true,\n composed: true\n })\n )\n },\n onCancel: () => {\n this._resetActiveResize()\n }\n })\n }\n\n // 终结进行中的拖拽:清手势状态、移除跟手 class,宽度过渡回 prop 管辖,不派发事件。\n // pointer cancel、跨越移动端断点和切入 no-sidebar 时共用此收尾。\n private _resetActiveResize() {\n this._resizeGesture?.destroy()\n this._resizeGesture = null\n this.renderRoot.querySelector('aside')?.classList.remove('is-resizing')\n this._resizeWidth = null\n }\n\n // 键盘调宽(WAI-ARIA splitter 模式):方向键步进 16px(Shift 加速 64px)调整\n // 临时宽度,Enter 以受控请求派发 `sidebar-width-change`(与指针松手同语义),\n // Escape 撤回临时宽度交还 prop 管辖,Home/End 直接到 min/max。\n private _handleResizeKeydown(e: KeyboardEvent) {\n if (!this.sidebarResizable || this.sidebarCollapsed || this._mobile || this._isResizing()) return\n\n const min = this._resolveSidebarMinWidth()\n const max = this._resolveSidebarMaxWidth()\n const aside = this.renderRoot.querySelector('aside')\n const current = this._resizeWidth\n ? this._parsePx(this._resizeWidth, min)\n : this._parsePx(aside ? window.getComputedStyle(aside).width : this.sidebarWidth, min)\n const step = e.shiftKey ? 64 : 16\n let next: number | null = null\n\n switch (e.key) {\n case 'ArrowLeft':\n next = Math.max(min, current - step)\n break\n case 'ArrowRight':\n next = Math.min(max, current + step)\n break\n case 'Home':\n next = min\n break\n case 'End':\n next = max\n break\n case 'Enter':\n // 无未提交调整时不派发(焦点停留时的回车噪声)\n if (this._resizeWidth !== null) {\n const width = this._resizeWidth\n this._resizeWidth = null\n this.dispatchEvent(\n new CustomEvent('sidebar-width-change', { detail: { width }, bubbles: true, composed: true })\n )\n }\n e.preventDefault()\n return\n case 'Escape':\n this._resizeWidth = null\n e.preventDefault()\n return\n default:\n return\n }\n e.preventDefault()\n this._resizeWidth = `${Math.round(next)}px`\n }\n\n /*\n * 视口判定订阅媒体查询,而不是监听 window resize:跨断点每次只派发一次 change,没有\n * resize 事件流要挡。判定、CSS 的 `@media (width <= 640px)` 与对外暴露的 `mobile` 因此\n * 在同一时刻翻转,不会留下「树还在桌面、aside 已被 CSS 藏起来」的空档。\n */\n private _syncMobileViewportSubscription() {\n this._mobileQuery?.removeEventListener('change', this._onMobileQueryChange)\n // jsdom 等没有 matchMedia 的环境没有视口可判:`mobile` 停在 false,订阅与渲染照常。\n this._mobileQuery = typeof window.matchMedia === 'function' ? window.matchMedia(MOBILE_VIEWPORT_QUERY) : null\n this._mobileQuery?.addEventListener('change', this._onMobileQueryChange)\n this._syncMobile(this._mobileQuery?.matches ?? false)\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._syncMobileViewportSubscription()\n void this.updateComplete.then(() => {\n if (this.isConnected) this._syncBannerPresence()\n })\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._resetActiveResize()\n this._mobileQuery?.removeEventListener('change', this._onMobileQueryChange)\n this._mobileQuery = null\n }\n\n override firstUpdated() {\n this._syncBannerPresence()\n }\n\n private _onBannerSlotChange() {\n this._syncBannerPresence()\n }\n\n private _syncBannerPresence() {\n const bannerSlot = this.renderRoot.querySelector<HTMLSlotElement>('slot[name=\"banner\"]')\n const hasBanner =\n bannerSlot\n ?.assignedNodes()\n .some(\n node =>\n node.nodeType === Node.ELEMENT_NODE || (node.nodeType === Node.TEXT_NODE && !!node.textContent?.trim())\n ) ?? false\n const banner = this.shadowRoot?.querySelector<HTMLElement>('.layout-banner') ?? null\n this._visibleBanner.setTarget(hasBanner ? banner : null)\n }\n\n /*\n * 切到无 sidebar 模式会卸载 aside;先终结进行中的拖拽与未提交的临时宽度,\n * 否则手势悬挂,切回桌面后新拖拽会在 `_isResizing()` 入口被拦下。\n */\n protected override willUpdate(changed: PropertyValues) {\n super.willUpdate(changed)\n if (changed.has('noSidebar') && this.noSidebar) this._resetActiveResize()\n }\n\n override render() {\n const showSidebar = !this.noSidebar\n const isSidebarOpen = !this.sidebarCollapsed\n // 拖拽中的临时宽度优先于 prop 宽度;移动端宽度由 Drawer 内部变量管理。\n const sidebarStyle = this._mobile\n ? {}\n : { width: this._resizeWidth ?? (this.sidebarCollapsed ? this.collapsedWidth : this.sidebarWidth) }\n\n const toggleLabel = resolveBuiltinLabelSet(this.ariaLabels, isSidebarOpen ? 'collapseSidebar' : 'expandSidebar')\n\n const desktopToggle = html`\n <web-ui-button\n class=\"sidebar-toggle\"\n icon\n variant=\"secondary\"\n @click=\"${this._toggleSidebar}\"\n aria-label=${toggleLabel}\n >\n <web-ui-icon .icon=${radixIconsPanelLeftMinimized}></web-ui-icon>\n </web-ui-button>\n `\n\n const sidebarViewport = html`\n <div class=\"sidebar-viewport\">\n <slot name=\"sidebar\"></slot>\n </div>\n `\n\n const header = html`\n <header class=\"${classMap({ 'wui-glow': this.headerGlow })}\">\n ${\n this._mobile && showSidebar\n ? html`\n <web-ui-button\n class=\"mobile-toggle\"\n icon\n variant=\"glass\"\n @click=\"${this._toggleSidebar}\"\n aria-label=${resolveBuiltinLabelSet(this.ariaLabels, 'openNavigationMenu')}\n >\n <web-ui-icon .icon=${radixIconsPanelLeftMinimized}></web-ui-icon>\n </web-ui-button>\n `\n : nothing\n }\n <slot name=\"header\"></slot>\n </header>\n `\n\n const contentLayout = html`\n <div class=\"layout-content\">\n ${header}\n <main><slot></slot></main>\n <footer><slot name=\"tabbar\"></slot></footer>\n </div>\n `\n\n const desktopLayout = html`\n <div class=\"layout-body\">\n <div class=\"sidebar-wrapper\">\n <aside\n class=\"${classMap({\n collapsed: !isSidebarOpen\n })}\"\n style=\"${styleMap(sidebarStyle)}\"\n >\n <div class=\"aside-panel wui-glass\">\n ${sidebarViewport}\n <div class=\"sidebar-toggle-area\">${desktopToggle}</div>\n </div>\n ${\n this.sidebarResizable && !this.sidebarCollapsed\n ? html`\n <div\n class=\"sidebar-resize-handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n aria-label=${resolveBuiltinLabelSet(this.ariaLabels, 'resizeSidebar')}\n @pointerdown=${this._handleResizePointerDown}\n @keydown=${this._handleResizeKeydown}\n ></div>\n `\n : nothing\n }\n </aside>\n </div>\n ${contentLayout}\n </div>\n `\n\n const mobileDrawer = html`\n <web-ui-drawer\n placement=\"left\"\n ?open=\"${this.sidebarOpen}\"\n controlled\n draggable\n aria-dialog-label=${resolveBuiltinLabelSet(this.ariaLabels, 'mainNavigation')}\n @open-change=\"${this._onDrawerChange}\"\n style=\"${styleMap({\n '--wui-drawer-width': this.sidebarWidth,\n '--wui-drawer-radius': 'var(--wui-layout-sidebar-radius, 24px)'\n })}\"\n >\n ${sidebarViewport}\n </web-ui-drawer>\n `\n\n /*\n * 无 sidebar 模式只留内容列:桌面 aside 与移动端 Toggle / Drawer 都不进树,\n * `sidebar` slot 因而没有渲染出口(消费方节点仍留在 light DOM)。\n */\n const layoutBody = showSidebar\n ? this._mobile\n ? html`${contentLayout}${mobileDrawer}`\n : desktopLayout\n : contentLayout\n\n return html`\n <div class=\"layout-page\">\n <div class=\"layout-banner\">\n <slot name=\"banner\" @slotchange=\"${this._onBannerSlotChange}\"></slot>\n </div>\n ${layoutBody}\n </div>\n `\n }\n\n declare readonly $events: {\n 'sidebar-collapsed-change': CustomEvent<{ collapsed: boolean }>\n 'sidebar-open-change': CustomEvent<{ open: boolean }>\n 'sidebar-width-change': CustomEvent<{ width: string }>\n 'mobile-change': CustomEvent<{ mobile: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-layout': WebUiLayout\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["glass","glow","style","icRoundMenuOpen","radixIconsPanelLeftMinimized"],"sources":["../../../src/components/layout/index.ts"],"sourcesContent":["import '@/components/button'\nimport '@/components/drawer'\nimport '@/components/icon'\n\nimport { html, LitElement, nothing, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { styleMap } from 'lit/directives/style-map.js'\n\nimport glass from '@/classes/glass.css?inline'\nimport glow from '@/classes/glow.css?inline'\nimport { icRoundMenuOpen, radixIconsPanelLeftMinimized } from '@/icons'\nimport { attachDragGesture, clamp, type DragGestureHandle } from '@/shared/gesture'\nimport { resolveBuiltinLabelSet } from '@/shared/locale/builtin-labels'\nimport { defineVisibleAreaTracker, VisibleAreaController } from '@/shared/visible-area'\n\nimport style from './style.css?inline'\n\n/**\n * 移动端断点的媒体查询。\n *\n * 必须与 `style.css` 里那份 `@media (width <= 640px)` 是同一条条件:CSS 用它在窄视口隐藏桌面\n * aside,TS 用同一条件决定渲染哪一套树,两者错开就会出现「树还在桌面、aside 已经被藏起来」\n * 的空档。两份字面量的相等由 `components/drawer/__tests__/breakpoint-parity.spec.ts` 守着。\n */\nconst MOBILE_VIEWPORT_QUERY = '(width <= 640px)'\n\n/**\n * Layout 组件 - 提供页面布局框架,支持 collapsible sidebar 与无 sidebar 模式\n *\n * @fires sidebar-collapsed-change - 桌面端侧边栏 collapse 状态变更请求时触发\n * @fires sidebar-open-change - 移动端侧边栏 Drawer toggle 请求时触发\n * @fires sidebar-width-change - 桌面端侧边栏拖拽调宽结束时触发,携带最终宽度(受控请求)\n * @fires mobile-change - 视口跨越移动端断点、`mobile` 翻转时触发(派生状态通知)\n * @slot banner - 顶部 Banner 区域(全宽,随页面滚动,可选)\n * @slot header - 内容区顶部 header(sticky)\n * @slot sidebar - 侧边栏内容;Consumer 决定其内部固定区域与滚动容器;`no-sidebar` 置位时不渲染\n * @slot tabbar - 底部 tabbar 区域\n * @slot default - 主内容区\n */\n/** `web-ui-layout` 内置无障碍名的成组覆盖;每个 key 对应一处图标控件的 accessible name。 */\nexport interface LayoutAriaLabels {\n collapseSidebar?: string\n expandSidebar?: string\n resizeSidebar?: string\n openNavigationMenu?: string\n mainNavigation?: string\n}\n\n@customElement('web-ui-layout')\nexport class WebUiLayout extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(glow), unsafeCSS(style)]\n\n /**\n * 关闭整块 sidebar:桌面端不渲染 aside 与 collapse Toggle,移动端不渲染 header Toggle 与 Drawer。\n *\n * 它是布局模式而不是 collapse 的别名:`sidebar-collapsed` 只收窄桌面 aside,本属性把侧边栏\n * 结构本身从树里去掉。`sidebar` slot 随之不渲染(消费方节点仍留在 light DOM),\n * 侧边栏相关事件与属性在该模式下不产生可见效果。\n */\n @property({ type: Boolean, attribute: 'no-sidebar', reflect: true })\n noSidebar = false\n\n /**\n * 启用桌面端 Sidebar 的 collapse 入口。\n *\n * 只有声明了本属性,layout 才渲染收起 / 展开 Toggle,也才会因为点击而派发\n * `sidebar-collapsed-change`。桌面宽度仍由受控属性 `sidebar-collapsed` 决定,\n * 未声明本属性不会阻止它收窄;移动端 Drawer 的 header Toggle 不受影响。\n */\n @property({ type: Boolean, attribute: 'sidebar-collapsible', reflect: true })\n sidebarCollapsible = false\n\n /**\n * 桌面端 Sidebar 的持久布局偏好。\n *\n * `true` 只会将桌面 Sidebar 收窄到 `collapsedWidth`,不会影响移动端 Drawer。\n * 它不与 `sidebarOpen` 合并:前者是跨视口保留的密度选择,后者是移动端瞬时的 modal 可见性。\n *\n * 两处渲染的是同一份 `sidebar` slot 内容,所以本属性只负责桌面 aside 的几何:Consumer\n * 若让自己的 slot 内容跟着它变形态(隐藏 label、改排布),必须按 `mobile` 收窄那个条件\n * (例如 `collapsed && !layout.mobile`,或 CSS 的 `web-ui-layout[mobile]`),\n * 否则 collapsed state 会一路渲染进移动端 Drawer(issue #195)。\n */\n @property({ type: Boolean, attribute: 'sidebar-collapsed', reflect: true })\n sidebarCollapsed = false\n\n @property({ type: String, attribute: 'sidebar-width', reflect: true })\n sidebarWidth = '240px'\n\n @property({ type: String, attribute: 'collapsed-width', reflect: true })\n collapsedWidth = '60px'\n\n /** 启用桌面端 Sidebar 拖拽调宽(collapsed state 隐藏 handle)。 */\n @property({ type: Boolean, attribute: 'sidebar-resizable', reflect: true })\n sidebarResizable = false\n\n /**\n * 拖拽调宽的下限(px,其他单位不解析);未设置时回退为 `collapsedWidth`,\n * 防止拖到不可用宽度。上限 `sidebar-max-width` 受内置 50vw 硬上限钳制。\n */\n @property({ type: String, attribute: 'sidebar-min-width', reflect: true })\n sidebarMinWidth = ''\n\n /** 拖拽调宽的上限(px,其他单位不解析);始终受内置 50vw 硬上限钳制。 */\n @property({ type: String, attribute: 'sidebar-max-width', reflect: true })\n sidebarMaxWidth = ''\n\n /**\n * 移动端 web-ui-drawer 的瞬时可见性。\n *\n * Drawer Toggle、Escape 和 backdrop 关闭都通过 `sidebar-open-change` 请求更新它;它不会改变桌面端的 collapse preference。\n * 因而不与 `sidebarCollapsed` 合并,避免响应式切换时将一次移动端交互误写为桌面端布局选择。\n */\n @property({ type: Boolean, attribute: 'sidebar-open', reflect: true })\n sidebarOpen = false\n\n @property({ type: Boolean, attribute: 'header-glow', reflect: true })\n headerGlow = false\n\n /** 内置无障碍名的成组覆盖;未提供的 key 回退内置英文文案(见 ADR-0016)。 */\n @property({ attribute: false }) ariaLabels?: LayoutAriaLabels\n\n /*\n * `mobile` 是派生输出,不是第二个输入:像 `web-ui-theme` 的 `resolved-appearance` 一样由组件\n * 独占写入,读取取 `MOBILE_VIEWPORT_QUERY`。外部写 attribute 会在同一次 reaction 内被恢复,\n * 写 property 不生效(只读 accessor,严格模式下抛错)——两种写法都不构成本属性之外的\n * 第三个视口来源。\n */\n @property({ type: Boolean, attribute: 'mobile', reflect: true })\n private _mobile = false\n\n /**\n * 当前视口是否落在移动端分支(即 `MOBILE_VIEWPORT_QUERY` 命中)。\n *\n * 与组件渲染哪一套树、以及 `mobile-change` 的派发是同一个值:翻转瞬间三者一起变,\n * Consumer 不必自己写断点判断,也不会与 layout 内部的树切换错开半拍。派生状态只读。\n */\n get mobile(): boolean {\n return this._mobile\n }\n\n private _mobileQuery: MediaQueryList | null = null\n\n private readonly _onMobileQueryChange = (event: MediaQueryListEvent) => {\n this._syncMobile(event.matches)\n }\n\n /*\n * 本方法由媒体查询翻转驱动,也被连接时的初次求值调用;attribute 的恢复路径见\n * `attributeChangedCallback`,那里不改判、也不派发通知,所以 `mobile-change` 只在\n * 视口真正跨断点时出现一次(窄视口挂载时那次算第一次)。\n */\n private _syncMobile(next: boolean) {\n if (next === this._mobile) return\n this._mobile = next\n // 跨断点会卸载桌面 layout(resize handle 随之消失);必须先终结进行中的拖拽,\n // 否则手势悬挂,切回桌面后所有新拖拽都在入口被 `_isResizing()` 拦下。\n if (this._isResizing()) this._resetActiveResize()\n this.toggleAttribute('mobile', next)\n this.dispatchEvent(new CustomEvent('mobile-change', { detail: { mobile: next }, bubbles: true, composed: true }))\n }\n\n override attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {\n super.attributeChangedCallback(name, oldValue, newValue)\n // super 已把外部值写进 backing field,所以这里要按媒体查询重算,不能拿字段当真相。\n if (name !== 'mobile') return\n // 无 matchMedia 的环境没有视口可判,`mobile` 恒为 false(见 connectedCallback)。\n const derived = this._mobileQuery?.matches ?? false\n if (this._mobile !== derived) this._mobile = derived\n /*\n * 布尔 attribute 只论存在性,但值本身也要归位:`setAttribute('mobile', 'false')` 在移动端\n * 会让 backing field 与 attribute 的**存在性**一致、值却留在宿主上(`getAttribute` 会回读\n * 到 consumer 写的那串)。组件写的形态是空串,这里按同一形态恢复,主题的\n * `resolved-appearance` 先例同理。\n */\n if (derived) {\n if (this.getAttribute('mobile') !== '') this.setAttribute('mobile', '')\n return\n }\n if (this.hasAttribute('mobile')) this.removeAttribute('mobile')\n }\n\n private readonly _visibleBanner = defineVisibleAreaTracker({\n onVisibleAreaChange: area => {\n this.style.setProperty('--wui-layout-visible-banner-height', `${area.height}px`)\n }\n }).make()\n\n private readonly _visibleBannerController = new VisibleAreaController(this, this._visibleBanner)\n\n private _toggleSidebar() {\n if (this._mobile) {\n this._emitSidebarOpenChange(!this.sidebarOpen)\n return\n }\n\n this._emitSidebarCollapsedChange(!this.sidebarCollapsed)\n }\n\n private _emitSidebarCollapsedChange(collapsed: boolean) {\n this.dispatchEvent(\n new CustomEvent('sidebar-collapsed-change', {\n detail: { collapsed },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _emitSidebarOpenChange(open: boolean) {\n this.dispatchEvent(\n new CustomEvent('sidebar-open-change', {\n detail: { open },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _onDrawerChange(e: CustomEvent<{ open: boolean }>) {\n e.stopPropagation()\n this._emitSidebarOpenChange(e.detail.open)\n }\n\n private _resizeGesture: DragGestureHandle | null = null\n private _resizeStartWidth = 0\n // 拖拽中的临时宽度;由 render 的 styleMap 统一写入,避免与 Lit 样式管理竞争。\n @state() private _resizeWidth: string | null = null\n\n private _parsePx(value: string, fallback: number): number {\n const parsed = Number.parseFloat(value)\n return Number.isFinite(parsed) ? parsed : fallback\n }\n\n private _resolveSidebarMinWidth(): number {\n return this.sidebarMinWidth\n ? this._parsePx(this.sidebarMinWidth, this._parsePx(this.collapsedWidth, 68))\n : this._parsePx(this.collapsedWidth, 68)\n }\n\n private _resolveSidebarMaxWidth(): number {\n // 组件内置硬上限:Sidebar 最多占视口一半(无需调用方设置)。\n // 调用方 sidebar-max-width 在该上限内生效,防止拖拽占据整个视口。\n const halfViewport = Math.floor(window.innerWidth / 2)\n const configured = this.sidebarMaxWidth ? this._parsePx(this.sidebarMaxWidth, halfViewport) : halfViewport\n const upper = Math.min(configured, halfViewport)\n return Math.max(upper, this._resolveSidebarMinWidth())\n }\n\n private _isResizing(): boolean {\n return this._resizeGesture?.isDragging() ?? false\n }\n\n private _handleResizePointerDown(e: PointerEvent) {\n if (!this.sidebarResizable || this.sidebarCollapsed || this._mobile || this._isResizing()) return\n // jsdom 等环境的 PointerEvent 可能缺失 isPrimary,仅在明确为 false(多点触控副指针)时拒绝。\n if (e.isPrimary === false) return\n\n const aside = this.renderRoot.querySelector('aside')\n if (!aside) return\n\n // 拖拽起点以 computed width 为准,并丢弃键盘未提交的调整(Enter 才提交、\n // Escape 撤回;抓取隐式放弃),保证零位移松手不会派发陈旧宽度。\n this._resizeStartWidth = this._parsePx(window.getComputedStyle(aside).width, this._parsePx(this.sidebarWidth, 240))\n this._resizeWidth = null\n aside.classList.add('is-resizing')\n\n this._resizeGesture = attachDragGesture(e, {\n axis: 'x',\n onMove: info => {\n // 向右拖增宽、向左拖收窄(handle 在右缘);钳制在 [min, max] 与视口内。\n const next = this._resizeStartWidth + info.deltaX\n const clamped = clamp(next, this._resolveSidebarMinWidth(), this._resolveSidebarMaxWidth())\n this._resizeWidth = `${Math.round(clamped)}px`\n },\n onEnd: () => {\n // 零位移松手(点击而非拖拽)不视为调宽动作:按 cancel 语义静默收尾。\n if (this._resizeWidth === null) {\n this._resetActiveResize()\n return\n }\n const width = this._resizeWidth\n this._resetActiveResize()\n // 受控契约:松手派发请求并交还 prop 管辖,由 Consumer 回写 sidebar-width。\n this.dispatchEvent(\n new CustomEvent('sidebar-width-change', {\n detail: { width },\n bubbles: true,\n composed: true\n })\n )\n },\n onCancel: () => {\n this._resetActiveResize()\n }\n })\n }\n\n // 终结进行中的拖拽:清手势状态、移除跟手 class,宽度过渡回 prop 管辖,不派发事件。\n // pointer cancel、跨越移动端断点和切入 no-sidebar 时共用此收尾。\n private _resetActiveResize() {\n this._resizeGesture?.destroy()\n this._resizeGesture = null\n this.renderRoot.querySelector('aside')?.classList.remove('is-resizing')\n this._resizeWidth = null\n }\n\n // 键盘调宽(WAI-ARIA splitter 模式):方向键步进 16px(Shift 加速 64px)调整\n // 临时宽度,Enter 以受控请求派发 `sidebar-width-change`(与指针松手同语义),\n // Escape 撤回临时宽度交还 prop 管辖,Home/End 直接到 min/max。\n private _handleResizeKeydown(e: KeyboardEvent) {\n if (!this.sidebarResizable || this.sidebarCollapsed || this._mobile || this._isResizing()) return\n\n const min = this._resolveSidebarMinWidth()\n const max = this._resolveSidebarMaxWidth()\n const aside = this.renderRoot.querySelector('aside')\n const current = this._resizeWidth\n ? this._parsePx(this._resizeWidth, min)\n : this._parsePx(aside ? window.getComputedStyle(aside).width : this.sidebarWidth, min)\n const step = e.shiftKey ? 64 : 16\n let next: number | null = null\n\n switch (e.key) {\n case 'ArrowLeft':\n next = Math.max(min, current - step)\n break\n case 'ArrowRight':\n next = Math.min(max, current + step)\n break\n case 'Home':\n next = min\n break\n case 'End':\n next = max\n break\n case 'Enter':\n // 无未提交调整时不派发(焦点停留时的回车噪声)\n if (this._resizeWidth !== null) {\n const width = this._resizeWidth\n this._resizeWidth = null\n this.dispatchEvent(\n new CustomEvent('sidebar-width-change', { detail: { width }, bubbles: true, composed: true })\n )\n }\n e.preventDefault()\n return\n case 'Escape':\n this._resizeWidth = null\n e.preventDefault()\n return\n default:\n return\n }\n e.preventDefault()\n this._resizeWidth = `${Math.round(next)}px`\n }\n\n /*\n * 视口判定订阅媒体查询,而不是监听 window resize:跨断点每次只派发一次 change,没有\n * resize 事件流要挡。判定、CSS 的 `@media (width <= 640px)` 与对外暴露的 `mobile` 因此\n * 在同一时刻翻转,不会留下「树还在桌面、aside 已被 CSS 藏起来」的空档。\n */\n private _syncMobileViewportSubscription() {\n this._mobileQuery?.removeEventListener('change', this._onMobileQueryChange)\n // jsdom 等没有 matchMedia 的环境没有视口可判:`mobile` 停在 false,订阅与渲染照常。\n this._mobileQuery = typeof window.matchMedia === 'function' ? window.matchMedia(MOBILE_VIEWPORT_QUERY) : null\n this._mobileQuery?.addEventListener('change', this._onMobileQueryChange)\n this._syncMobile(this._mobileQuery?.matches ?? false)\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._syncMobileViewportSubscription()\n void this.updateComplete.then(() => {\n if (this.isConnected) this._syncBannerPresence()\n })\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._resetActiveResize()\n this._mobileQuery?.removeEventListener('change', this._onMobileQueryChange)\n this._mobileQuery = null\n }\n\n override firstUpdated() {\n this._syncBannerPresence()\n }\n\n private _onBannerSlotChange() {\n this._syncBannerPresence()\n }\n\n private _syncBannerPresence() {\n const bannerSlot = this.renderRoot.querySelector<HTMLSlotElement>('slot[name=\"banner\"]')\n const hasBanner =\n bannerSlot\n ?.assignedNodes()\n .some(\n node =>\n node.nodeType === Node.ELEMENT_NODE || (node.nodeType === Node.TEXT_NODE && !!node.textContent?.trim())\n ) ?? false\n const banner = this.shadowRoot?.querySelector<HTMLElement>('.layout-banner') ?? null\n this._visibleBanner.setTarget(hasBanner ? banner : null)\n }\n\n /*\n * 切到无 sidebar 模式会卸载 aside;先终结进行中的拖拽与未提交的临时宽度,\n * 否则手势悬挂,切回桌面后新拖拽会在 `_isResizing()` 入口被拦下。\n */\n protected override willUpdate(changed: PropertyValues) {\n super.willUpdate(changed)\n if (changed.has('noSidebar') && this.noSidebar) this._resetActiveResize()\n }\n\n override render() {\n const showSidebar = !this.noSidebar\n const isSidebarOpen = !this.sidebarCollapsed\n // 拖拽中的临时宽度优先于 prop 宽度;移动端宽度由 Drawer 内部变量管理。\n const sidebarStyle = this._mobile\n ? {}\n : { width: this._resizeWidth ?? (this.sidebarCollapsed ? this.collapsedWidth : this.sidebarWidth) }\n\n const toggleLabel = resolveBuiltinLabelSet(this.ariaLabels, isSidebarOpen ? 'collapseSidebar' : 'expandSidebar')\n\n const desktopToggle = this.sidebarCollapsible\n ? html`\n <web-ui-button\n class=\"sidebar-toggle\"\n icon\n variant=\"ghost\"\n @click=\"${this._toggleSidebar}\"\n aria-label=${toggleLabel}\n >\n <web-ui-icon\n .icon=${icRoundMenuOpen}\n style=${styleMap({ transform: isSidebarOpen ? 'none' : 'scaleX(-1)' })}\n ></web-ui-icon>\n </web-ui-button>\n `\n : nothing\n\n const sidebarViewport = html`\n <div class=\"sidebar-viewport\">\n <slot name=\"sidebar\"></slot>\n </div>\n `\n\n const header = html`\n <header class=\"${classMap({ 'wui-glow': this.headerGlow })}\">\n ${\n this._mobile && showSidebar\n ? html`\n <web-ui-button\n class=\"mobile-toggle\"\n icon\n variant=\"glass\"\n @click=\"${this._toggleSidebar}\"\n aria-label=${resolveBuiltinLabelSet(this.ariaLabels, 'openNavigationMenu')}\n >\n <web-ui-icon .icon=${radixIconsPanelLeftMinimized}></web-ui-icon>\n </web-ui-button>\n `\n : nothing\n }\n <slot name=\"header\"></slot>\n </header>\n `\n\n const contentLayout = html`\n <div class=\"layout-content\">\n ${header}\n <main><slot></slot></main>\n <footer><slot name=\"tabbar\"></slot></footer>\n </div>\n `\n\n const desktopLayout = html`\n <div class=\"layout-body\">\n <div class=\"sidebar-wrapper\">\n <aside\n class=\"${classMap({\n collapsed: !isSidebarOpen\n })}\"\n style=\"${styleMap(sidebarStyle)}\"\n >\n <div class=\"aside-panel wui-glass\">\n ${sidebarViewport}\n ${this.sidebarCollapsible ? html`<div class=\"sidebar-toggle-area\">${desktopToggle}</div>` : nothing}\n </div>\n ${\n this.sidebarResizable && !this.sidebarCollapsed\n ? html`\n <div\n class=\"sidebar-resize-handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n aria-label=${resolveBuiltinLabelSet(this.ariaLabels, 'resizeSidebar')}\n @pointerdown=${this._handleResizePointerDown}\n @keydown=${this._handleResizeKeydown}\n ></div>\n `\n : nothing\n }\n </aside>\n </div>\n ${contentLayout}\n </div>\n `\n\n const mobileDrawer = html`\n <web-ui-drawer\n placement=\"left\"\n ?open=\"${this.sidebarOpen}\"\n controlled\n draggable\n aria-dialog-label=${resolveBuiltinLabelSet(this.ariaLabels, 'mainNavigation')}\n @open-change=\"${this._onDrawerChange}\"\n style=\"${styleMap({\n '--wui-drawer-width': this.sidebarWidth,\n '--wui-drawer-radius': 'var(--wui-layout-sidebar-radius, 24px)'\n })}\"\n >\n ${sidebarViewport}\n </web-ui-drawer>\n `\n\n /*\n * 无 sidebar 模式只留内容列:桌面 aside 与移动端 Toggle / Drawer 都不进树,\n * `sidebar` slot 因而没有渲染出口(消费方节点仍留在 light DOM)。\n */\n const layoutBody = showSidebar\n ? this._mobile\n ? html`${contentLayout}${mobileDrawer}`\n : desktopLayout\n : contentLayout\n\n return html`\n <div class=\"layout-page\">\n <div class=\"layout-banner\">\n <slot name=\"banner\" @slotchange=\"${this._onBannerSlotChange}\"></slot>\n </div>\n ${layoutBody}\n </div>\n `\n }\n\n declare readonly $events: {\n 'sidebar-collapsed-change': CustomEvent<{ collapsed: boolean }>\n 'sidebar-open-change': CustomEvent<{ open: boolean }>\n 'sidebar-width-change': CustomEvent<{ width: string }>\n 'mobile-change': CustomEvent<{ mobile: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-layout': WebUiLayout\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  // src/components/layout/style.css?inline
2
- var style_default = "*,:before,:after{box-sizing:border-box}:host{--wui-layout-visible-banner-height:0px;display:block}.layout-page{flex-direction:column;min-height:100dvh;display:flex}.layout-banner{z-index:var(--wui-layer-banner,30);position:relative}.layout-body{flex:1;display:flex}.sidebar-wrapper{align-self:stretch;position:relative}.sidebar-viewport{flex:1;min-height:0}aside{z-index:var(--wui-layer-sidebar,50);max-width:50vw;height:calc(100vh - var(--wui-layout-visible-banner-height));height:calc(100dvh - var(--wui-layout-visible-banner-height));max-height:calc(100vh - var(--wui-layout-visible-banner-height));max-height:calc(100dvh - var(--wui-layout-visible-banner-height));padding:var(--wui-space-2,8px) 0 var(--wui-space-2,8px) var(--wui-space-2,8px);transition:width var(--wui-duration-layout,.2s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1));flex-direction:column;flex-shrink:0;display:flex;position:sticky;top:0}aside .aside-panel{--wui-glass-corner-radius:var(--wui-layout-sidebar-radius,24px);border-radius:var(--wui-layout-sidebar-radius,24px);background-color:var(--wui-color-surface-sidebar,#e9e9e9d1);flex-direction:column;flex:1;min-height:0;display:flex}aside .sidebar-toggle-area{flex-shrink:0;align-items:center;display:flex}aside .sidebar-toggle{--wui-button-width:var(--wui-layout-sidebar-toggle-width,44px);margin:var(--wui-space-2,8px)}aside.collapsed .sidebar-viewport{overflow:hidden}.sidebar-resize-handle{touch-action:none;cursor:col-resize;z-index:1;width:12px;position:absolute;top:0;bottom:0;right:-6px}.sidebar-resize-handle:before{content:\"\";border-radius:var(--wui-radius-control,calc(infinity * 1px));opacity:0;background:var(--wui-color-accent,#0071e3);width:3px;transition:opacity var(--wui-duration-feedback,.1s) ease;position:absolute;top:0;bottom:0;left:50%;translate:-50%}.sidebar-resize-handle:hover:before,.sidebar-resize-handle:focus-visible:before,aside.is-resizing .sidebar-resize-handle:before{opacity:1}.sidebar-resize-handle:focus-visible{border-radius:var(--wui-radius-control,calc(infinity * 1px));outline:var(--wui-focus-ring-width,3px) solid var(--wui-color-focus-ring,color-mix(in srgb, var(--wui-color-accent,#0071e3) 40%, transparent));outline-offset:2px}aside.is-resizing{transition:none}.layout-content{flex-direction:column;flex:1;min-width:0;display:flex;overflow-x:clip}header{isolation:isolate;z-index:var(--wui-layer-header,10);gap:var(--wui-space-2,8px);align-items:center;display:flex;position:sticky;top:0}.mobile-toggle{margin-left:var(--wui-layout-mobile-toggle-inset,8px)}main{flex:1}footer{z-index:var(--wui-layer-tabbar,40);position:sticky;bottom:0}@media (width<=640px){aside,.sidebar-wrapper{display:none}header{align-items:flex-start}.mobile-toggle{margin-top:var(--wui-space-2,8px);align-self:flex-start}}@media (prefers-reduced-motion:reduce){aside,.sidebar-resize-handle:before{transition:none}}\n";
2
+ var style_default = "*,:before,:after{box-sizing:border-box}:host{--wui-layout-visible-banner-height:0px;--wui-internal-layout-viewport-height:var(--wui-layout-viewport-height,100vh);display:block}.layout-page{min-height:var(--wui-internal-layout-viewport-height);flex-direction:column;display:flex}.layout-banner{z-index:var(--wui-layer-banner,300);position:relative}.layout-body{flex:1;display:flex}.sidebar-wrapper{align-self:stretch;position:relative}.sidebar-viewport{flex:1;min-height:0}aside{z-index:var(--wui-layer-sidebar,500);max-width:50vw;height:calc(var(--wui-internal-layout-viewport-height) - var(--wui-layout-visible-banner-height));max-height:calc(var(--wui-internal-layout-viewport-height) - var(--wui-layout-visible-banner-height));padding:var(--wui-space-2,8px) 0 var(--wui-space-2,8px) var(--wui-space-2,8px);transition:width var(--wui-duration-layout,.2s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1));flex-direction:column;flex-shrink:0;display:flex;position:sticky;top:0}aside .aside-panel{--wui-glass-corner-radius:var(--wui-layout-sidebar-radius,24px);border-radius:var(--wui-layout-sidebar-radius,24px);background-color:var(--wui-color-surface-sidebar,#e9e9e9d1);flex-direction:column;flex:1;min-height:0;display:flex}aside .sidebar-toggle-area{flex-shrink:0;align-items:center;display:flex}aside .sidebar-toggle{--wui-button-width:var(--wui-layout-sidebar-toggle-width,36px);margin:var(--wui-space-2,8px)}aside.collapsed .sidebar-viewport{overflow:hidden}@supports (height:100dvh){:host{--wui-internal-layout-viewport-height:var(--wui-layout-viewport-height,100dvh)}}.sidebar-resize-handle{touch-action:none;cursor:col-resize;z-index:1;width:12px;position:absolute;top:0;bottom:0;right:-6px}.sidebar-resize-handle:before{content:\"\";border-radius:var(--wui-radius-control,calc(infinity * 1px));opacity:0;background:var(--wui-color-accent,#0071e3);width:3px;transition:opacity var(--wui-duration-feedback,.1s) ease;position:absolute;top:0;bottom:0;left:50%;translate:-50%}.sidebar-resize-handle:hover:before,.sidebar-resize-handle:focus-visible:before,aside.is-resizing .sidebar-resize-handle:before{opacity:1}.sidebar-resize-handle:focus-visible{border-radius:var(--wui-radius-control,calc(infinity * 1px));outline:var(--wui-focus-ring-width,3px) solid var(--wui-color-focus-ring,color-mix(in srgb, var(--wui-color-accent,#0071e3) 40%, transparent));outline-offset:2px}aside.is-resizing{transition:none}.layout-content{flex-direction:column;flex:1;min-width:0;display:flex;overflow-x:clip}header{isolation:isolate;z-index:var(--wui-layer-header,100);gap:var(--wui-space-2,8px);align-items:center;display:flex;position:sticky;top:0}.mobile-toggle{margin-left:var(--wui-layout-mobile-toggle-inset,8px)}main{flex:1}footer{z-index:var(--wui-layer-tabbar,400);position:sticky;bottom:0}@media (width<=640px){aside,.sidebar-wrapper{display:none}header{align-items:flex-start}.mobile-toggle{margin-top:var(--wui-space-2,8px);align-self:flex-start}}@media (prefers-reduced-motion:reduce){aside,.sidebar-resize-handle:before{transition:none}}\n";
3
3
  export { style_default as default };
4
4
 
5
5
  //# sourceMappingURL=style.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/layout/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n --wui-layout-visible-banner-height: 0px;\n\n display: block;\n}\n\n/* 页面级滚动容器。Banner 位于正文前,随页面一起滚动。 */\n.layout-page {\n display: flex;\n flex-direction: column;\n min-height: 100vh;\n min-height: 100dvh;\n}\n\n.layout-banner {\n position: relative;\n z-index: var(--wui-layer-banner, 30);\n}\n\n/* 参考 book-fe:正文区域负责撑高 sidebar 的 relative wrapper。 */\n.layout-body {\n display: flex;\n flex: 1;\n}\n\n.sidebar-wrapper {\n position: relative;\n align-self: stretch;\n}\n\n/* Layout 只提供可用高度;桌面端由 Consumer 决定 scrollport,移动端由 Drawer content 提供。 */\n.sidebar-viewport {\n flex: 1;\n min-height: 0;\n}\n\n/*\n * Banner 可见时,组件用它的当前可见高度缩短 aside;Banner 滚出后该值归零。\n * 因而圆角卡片始终完整可见,同时 sticky Sidebar 可在 Banner 消失后填满视口。\n */\naside {\n position: sticky;\n z-index: var(--wui-layer-sidebar, 50);\n top: 0;\n\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n\n max-width: 50vw;\n height: calc(100vh - var(--wui-layout-visible-banner-height));\n height: calc(100dvh - var(--wui-layout-visible-banner-height));\n max-height: calc(100vh - var(--wui-layout-visible-banner-height));\n max-height: calc(100dvh - var(--wui-layout-visible-banner-height));\n padding: var(--wui-space-2, 8px) 0 var(--wui-space-2, 8px) var(--wui-space-2, 8px);\n\n transition: width var(--wui-duration-layout, 200ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n\n .aside-panel {\n --wui-glass-corner-radius: var(--wui-layout-sidebar-radius, 24px);\n\n display: flex;\n flex: 1;\n flex-direction: column;\n\n min-height: 0;\n border-radius: var(--wui-layout-sidebar-radius, 24px);\n\n /* 深色下比 page 浅一档的专用表面 token。不直接复用 --wui-color-surface-overlay:\n 该 token 同时服务 dialog/drawer/toast,整体抬升会带偏不在本次范围内的表面。\n 浅色保持中性分层,无 theme 时回退到同一字面量。 */\n background-color: var(--wui-color-surface-sidebar, rgb(233 233 233 / 0.82));\n }\n\n .sidebar-toggle-area {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n }\n\n .sidebar-toggle {\n /*\n * 走可覆盖的 var:`.sidebar-toggle` 自身声明的 `--wui-button-width` 优先于从\n * 祖先继承的同名变量,app 侧无法直接改写它。要让 collapsed state 的 toggle 跟着 collapsed width\n * 一起变,只能由这里显式接一个出口。取值由 app 决定,本组件不假设 collapsed width。\n * 默认值 44px;app 侧不覆盖时就是这个宽度。\n */\n --wui-button-width: var(--wui-layout-sidebar-toggle-width, 44px);\n\n margin: var(--wui-space-2, 8px);\n }\n\n &.collapsed {\n .sidebar-viewport {\n overflow: hidden;\n }\n }\n}\n\n/*\n * 命中条贴 aside 右缘(约 12px 宽),hover 或拖拽中显示 3px accent 竖线。\n * aside 有 8px padding,handle 定位在 padding 区外缘,不遮挡 aside-panel 内容。\n */\n.sidebar-resize-handle {\n touch-action: none;\n cursor: col-resize;\n\n position: absolute;\n z-index: 1;\n top: 0;\n right: -6px;\n bottom: 0;\n\n width: 12px;\n}\n\n.sidebar-resize-handle::before {\n content: '';\n\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n translate: -50%;\n\n width: 3px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n\n opacity: 0;\n background: var(--wui-color-accent, #0071e3);\n\n transition: opacity var(--wui-duration-feedback, 100ms) ease;\n}\n\n.sidebar-resize-handle:hover::before,\n.sidebar-resize-handle:focus-visible::before,\naside.is-resizing .sidebar-resize-handle::before {\n opacity: 1;\n}\n\n/*\n * 键盘 focus 同时给出两样东西:竖条与 hover / 拖拽同形,ring 与其余组件同一套浅蓝\n * 样式。竖条说明「命中条在哪」,ring 说明「键盘落点在哪」——handle 宽 12px 且通高,\n * 命中区远大于 3px 竖条本身,只靠竖条不足以说明焦点位置。\n *\n * ring 沿用 --wui-focus-ring-width / --wui-color-focus-ring 两个 token。圆角取\n * control token 后 outline 呈细长胶囊,与 button 的胶囊 ring 同一形状语言;offset\n * 2px 让 ring 骑在 aside 右缘内外两侧,不被 aside 裁掉。\n */\n.sidebar-resize-handle:focus-visible {\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n outline: var(--wui-focus-ring-width, 3px) solid\n var(--wui-color-focus-ring, color-mix(in srgb, var(--wui-color-accent, #0071e3) 40%, transparent));\n outline-offset: 2px;\n}\n\n/* 拖拽中禁用宽度过渡,保证实时跟手。 */\naside.is-resizing {\n transition: none;\n}\n\n.layout-content {\n /*\n * 内容列的横向溢出由本层裁掉,避免撑出页面横向 scrollbar;它有 min-width: 0,\n * 是容纳横向溢出的位置。glow 伪元素已收在 header 盒内(见 header 的注释),\n * 不再依赖这层裁剪。\n *\n * clip 不创建滚动容器,因此不影响 header / footer 自身的 sticky;也不用 hidden——\n * 那会把本层变成滚动容器,sticky 随即失效。\n */\n overflow-x: clip;\n display: flex;\n flex: 1;\n flex-direction: column;\n\n min-width: 0;\n}\n\nheader {\n /*\n * isolation: isolate 把负层级的 .wui-glow 伪元素限定在本 Header 的局部 stacking\n * context,slot 内容自然位于其上。晕染本身来自共享样式(src/classes/glow.css),\n * header-glow 属性负责挂 class。\n *\n * 「纵向必须 visible 才留得住模糊尾巴与下拉 overlay 的溢出空间」仍然成立——\n * overflow-* 一旦生效就会裁剪该轴,visible 是这些内容的唯一溢出出口。\n */\n isolation: isolate;\n position: sticky;\n z-index: var(--wui-layer-header, 10);\n top: 0;\n\n display: flex;\n gap: var(--wui-space-2, 8px);\n align-items: center;\n}\n\n/* 移动端 Toggle 属于组件自身,不随 Consumer 的 header 内边距缩进;\n 默认 8px 使其脱离视口边缘,Consumer 可用同名变量对齐自身内容左缘。 */\n.mobile-toggle {\n margin-left: var(--wui-layout-mobile-toggle-inset, 8px);\n}\n\nmain {\n flex: 1;\n}\n\nfooter {\n position: sticky;\n z-index: var(--wui-layer-tabbar, 40);\n bottom: 0;\n}\n\n@media (width <= 640px) {\n aside,\n .sidebar-wrapper {\n display: none;\n }\n\n /* Slot 可能包含展开后的多行 header;Toggle 对齐首行,而不是被整段 header 高度居中。 */\n header {\n align-items: flex-start;\n }\n\n .mobile-toggle {\n align-self: flex-start;\n margin-top: var(--wui-space-2, 8px);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n aside {\n transition: none;\n }\n\n .sidebar-resize-handle::before {\n transition: none;\n }\n}\n"],"mappings":";AAAA,IAAA,gBAAA"}
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/layout/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n --wui-layout-visible-banner-height: 0px;\n --wui-internal-layout-viewport-height: var(--wui-layout-viewport-height, 100vh);\n\n display: block;\n}\n\n/* 页面级滚动容器。Banner 位于正文前,随页面一起滚动。 */\n.layout-page {\n display: flex;\n flex-direction: column;\n min-height: var(--wui-internal-layout-viewport-height);\n}\n\n.layout-banner {\n position: relative;\n z-index: var(--wui-layer-banner, 300);\n}\n\n/* 参考 book-fe:正文区域负责撑高 sidebar 的 relative wrapper。 */\n.layout-body {\n display: flex;\n flex: 1;\n}\n\n.sidebar-wrapper {\n position: relative;\n align-self: stretch;\n}\n\n/* Layout 只提供可用高度;桌面端由 Consumer 决定 scrollport,移动端由 Drawer content 提供。 */\n.sidebar-viewport {\n flex: 1;\n min-height: 0;\n}\n\n/*\n * Banner 可见时,组件用它的当前可见高度缩短 aside;Banner 滚出后该值归零。\n * 因而圆角卡片始终完整可见,同时 sticky Sidebar 可在 Banner 消失后填满视口。\n */\naside {\n position: sticky;\n z-index: var(--wui-layer-sidebar, 500);\n top: 0;\n\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n\n max-width: 50vw;\n height: calc(var(--wui-internal-layout-viewport-height) - var(--wui-layout-visible-banner-height));\n max-height: calc(var(--wui-internal-layout-viewport-height) - var(--wui-layout-visible-banner-height));\n padding: var(--wui-space-2, 8px) 0 var(--wui-space-2, 8px) var(--wui-space-2, 8px);\n\n transition: width var(--wui-duration-layout, 200ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n\n .aside-panel {\n --wui-glass-corner-radius: var(--wui-layout-sidebar-radius, 24px);\n\n display: flex;\n flex: 1;\n flex-direction: column;\n\n min-height: 0;\n border-radius: var(--wui-layout-sidebar-radius, 24px);\n\n /* 深色下比 page 浅一档的专用表面 token。不直接复用 --wui-color-surface-overlay:\n 该 token 同时服务 dialog/drawer/toast,整体抬升会带偏不在本次范围内的表面。\n 浅色保持中性分层,无 theme 时回退到同一字面量。 */\n background-color: var(--wui-color-surface-sidebar, rgb(233 233 233 / 0.82));\n }\n\n .sidebar-toggle-area {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n }\n\n .sidebar-toggle {\n /*\n * 走可覆盖的 var:`.sidebar-toggle` 自身声明的 `--wui-button-width` 优先于从\n * 祖先继承的同名变量,app 侧无法直接改写它。要让 collapsed state 的 toggle 跟着 collapsed width\n * 一起变,只能由这里显式接一个出口。取值由 app 决定,本组件不假设 collapsed width。\n * 默认值 36px;app 侧不覆盖时就是这个宽度。\n */\n --wui-button-width: var(--wui-layout-sidebar-toggle-width, 36px);\n\n margin: var(--wui-space-2, 8px);\n }\n\n &.collapsed {\n .sidebar-viewport {\n overflow: hidden;\n }\n }\n}\n\n@supports (height: 100dvh) {\n :host {\n --wui-internal-layout-viewport-height: var(--wui-layout-viewport-height, 100dvh);\n }\n}\n\n/*\n * 命中条贴 aside 右缘(约 12px 宽),hover 或拖拽中显示 3px accent 竖线。\n * aside 有 8px padding,handle 定位在 padding 区外缘,不遮挡 aside-panel 内容。\n */\n.sidebar-resize-handle {\n touch-action: none;\n cursor: col-resize;\n\n position: absolute;\n z-index: 1;\n top: 0;\n right: -6px;\n bottom: 0;\n\n width: 12px;\n}\n\n.sidebar-resize-handle::before {\n content: '';\n\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n translate: -50%;\n\n width: 3px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n\n opacity: 0;\n background: var(--wui-color-accent, #0071e3);\n\n transition: opacity var(--wui-duration-feedback, 100ms) ease;\n}\n\n.sidebar-resize-handle:hover::before,\n.sidebar-resize-handle:focus-visible::before,\naside.is-resizing .sidebar-resize-handle::before {\n opacity: 1;\n}\n\n/*\n * 键盘 focus 同时给出两样东西:竖条与 hover / 拖拽同形,ring 与其余组件同一套浅蓝\n * 样式。竖条说明「命中条在哪」,ring 说明「键盘落点在哪」——handle 宽 12px 且通高,\n * 命中区远大于 3px 竖条本身,只靠竖条不足以说明焦点位置。\n *\n * ring 沿用 --wui-focus-ring-width / --wui-color-focus-ring 两个 token。圆角取\n * control token 后 outline 呈细长胶囊,与 button 的胶囊 ring 同一形状语言;offset\n * 2px 让 ring 骑在 aside 右缘内外两侧,不被 aside 裁掉。\n */\n.sidebar-resize-handle:focus-visible {\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n outline: var(--wui-focus-ring-width, 3px) solid\n var(--wui-color-focus-ring, color-mix(in srgb, var(--wui-color-accent, #0071e3) 40%, transparent));\n outline-offset: 2px;\n}\n\n/* 拖拽中禁用宽度过渡,保证实时跟手。 */\naside.is-resizing {\n transition: none;\n}\n\n.layout-content {\n /*\n * 内容列的横向溢出由本层裁掉,避免撑出页面横向 scrollbar;它有 min-width: 0,\n * 是容纳横向溢出的位置。glow 伪元素已收在 header 盒内(见 header 的注释),\n * 不再依赖这层裁剪。\n *\n * clip 不创建滚动容器,因此不影响 header / footer 自身的 sticky;也不用 hidden——\n * 那会把本层变成滚动容器,sticky 随即失效。\n */\n overflow-x: clip;\n display: flex;\n flex: 1;\n flex-direction: column;\n\n min-width: 0;\n}\n\nheader {\n /*\n * isolation: isolate 把负层级的 .wui-glow 伪元素限定在本 Header 的局部 stacking\n * context,slot 内容自然位于其上。晕染本身来自共享样式(src/classes/glow.css),\n * header-glow 属性负责挂 class。\n *\n * 「纵向必须 visible 才留得住模糊尾巴与下拉 overlay 的溢出空间」仍然成立——\n * overflow-* 一旦生效就会裁剪该轴,visible 是这些内容的唯一溢出出口。\n */\n isolation: isolate;\n position: sticky;\n z-index: var(--wui-layer-header, 100);\n top: 0;\n\n display: flex;\n gap: var(--wui-space-2, 8px);\n align-items: center;\n}\n\n/* 移动端 Toggle 属于组件自身,不随 Consumer 的 header 内边距缩进;\n 默认 8px 使其脱离视口边缘,Consumer 可用同名变量对齐自身内容左缘。 */\n.mobile-toggle {\n margin-left: var(--wui-layout-mobile-toggle-inset, 8px);\n}\n\nmain {\n flex: 1;\n}\n\nfooter {\n position: sticky;\n z-index: var(--wui-layer-tabbar, 400);\n bottom: 0;\n}\n\n@media (width <= 640px) {\n aside,\n .sidebar-wrapper {\n display: none;\n }\n\n /* Slot 可能包含展开后的多行 header;Toggle 对齐首行,而不是被整段 header 高度居中。 */\n header {\n align-items: flex-start;\n }\n\n .mobile-toggle {\n align-self: flex-start;\n margin-top: var(--wui-space-2, 8px);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n aside {\n transition: none;\n }\n\n .sidebar-resize-handle::before {\n transition: none;\n }\n}\n"],"mappings":";AAAA,IAAA,gBAAA"}
@@ -5,9 +5,9 @@ import overlay_motion_default from "../../assets/overlay-motion.js";
5
5
  import { dispatchOpenChangeEvent } from "../../shared/open-state/index.js";
6
6
  import { defineAnchoredPanel } from "../../shared/overlay/anchored-panel.js";
7
7
  import { defineOpenOverlay } from "../../shared/overlay/open-overlay.js";
8
+ import { FLOATING_PLACEMENTS } from "../../shared/overlay/placement-props.js";
8
9
  import { defineOverlayPortal } from "../../shared/overlay/portal.js";
9
10
  import { UserChangeController } from "../../shared/events/user-change.js";
10
- import { FLOATING_PLACEMENTS } from "../../shared/overlay/placement-props.js";
11
11
  import style_default from "./style.js";
12
12
  import { LitElement, html, unsafeCSS } from "lit";
13
13
  import { customElement, property } from "lit/decorators.js";
@@ -1,5 +1,5 @@
1
1
  // src/components/popover/style.css?inline
2
- var style_default = "*,:before,:after{box-sizing:border-box}:host{display:inline-flex}:host([disabled]){cursor:not-allowed}.popover-trigger{display:inline-flex}.popover-anchor{display:inline-flex;position:relative}.popover-panel{--wui-internal-glass-shadow:var(--wui-shadow-panel,0 3px 9px #00000045);--wui-glass-corner-radius:var(--wui-radius-menu,18px);--wui-color-glass-ring:transparent;z-index:var(--wui-layer-inline-overlay,1);box-sizing:border-box;width:max-content;max-width:min(90vw,400px);padding:var(--wui-space-2,8px) var(--wui-space-3,12px);border-radius:var(--wui-radius-menu,18px);font-size:var(--wui-font-size,14px);line-height:var(--wui-line-height-normal,1.5);color:var(--wui-color-text,#1b1b1b);background-color:var(--wui-color-surface-menu,#fafafac2);position:absolute;top:-9999px;left:-9999px}.popover-panel:where([hidden]){display:none}.popover-panel:focus-visible{outline:var(--wui-focus-ring-width,3px) solid var(--wui-color-focus-ring,color-mix(in srgb, var(--wui-color-accent,#0071e3) 40%, transparent));outline-offset:2px}.popover-panel.portal{z-index:var(--wui-layer-menu,100);position:fixed}:host([portal]) .popover-panel{display:none}\n";
2
+ var style_default = "*,:before,:after{box-sizing:border-box}:host{display:inline-flex}:host([disabled]){cursor:not-allowed}.popover-trigger{display:inline-flex}.popover-anchor{display:inline-flex;position:relative}.popover-panel{--wui-internal-glass-shadow:var(--wui-shadow-panel,0 3px 9px #00000045);--wui-glass-corner-radius:var(--wui-radius-menu,18px);--wui-color-glass-ring:transparent;z-index:var(--wui-layer-inline-overlay,10);box-sizing:border-box;width:max-content;max-width:min(90vw,400px);padding:var(--wui-space-2,8px) var(--wui-space-3,12px);border-radius:var(--wui-radius-menu,18px);font-size:var(--wui-font-size,14px);line-height:var(--wui-line-height-normal,1.5);color:var(--wui-color-text,#1b1b1b);background-color:var(--wui-color-surface-menu,#fafafac2);position:absolute;top:-9999px;left:-9999px}.popover-panel:where([hidden]){display:none}.popover-panel:focus-visible{outline:var(--wui-focus-ring-width,3px) solid var(--wui-color-focus-ring,color-mix(in srgb, var(--wui-color-accent,#0071e3) 40%, transparent));outline-offset:2px}.popover-panel.portal{z-index:var(--wui-layer-menu,1000);position:fixed}:host([portal]) .popover-panel{display:none}\n";
3
3
  export { style_default as default };
4
4
 
5
5
  //# sourceMappingURL=style.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/popover/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: inline-flex;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n}\n\n.popover-trigger {\n display: inline-flex;\n}\n\n.popover-anchor {\n position: relative;\n display: inline-flex;\n}\n\n.popover-panel {\n --wui-internal-glass-shadow: var(--wui-shadow-panel, 0 3px 9px rgb(0 0 0 / 0.27));\n --wui-glass-corner-radius: var(--wui-radius-menu, 18px);\n\n /* panel 宽远大于角半径,环底色铺出的整圈铺底会在长边中段读成一条比四角更重的\n 直边描边;转 transparent 后只剩四段角弧,见 menu-portal.css 同处注释。 */\n --wui-color-glass-ring: transparent;\n position: absolute;\n z-index: var(--wui-layer-inline-overlay, 1);\n top: -9999px;\n left: -9999px;\n\n box-sizing: border-box;\n width: max-content;\n max-width: min(90vw, 400px);\n padding: var(--wui-space-2, 8px) var(--wui-space-3, 12px);\n border-radius: var(--wui-radius-menu, 18px);\n\n font-size: var(--wui-font-size, 14px);\n line-height: var(--wui-line-height-normal, 1.5);\n color: var(--wui-color-text, #1b1b1b);\n\n /* 玻璃背景:wui-glass 在 panel 自身,这里覆盖其默认玻璃底为 menu surface 色 */\n background-color: var(--wui-color-surface-menu, rgb(250 250 250 / 0.76));\n\n &:where([hidden]) {\n display: none;\n }\n\n &:focus-visible {\n outline: var(--wui-focus-ring-width, 3px) solid\n var(--wui-color-focus-ring, color-mix(in srgb, var(--wui-color-accent, #0071e3) 40%, transparent));\n outline-offset: 2px;\n }\n}\n\n.popover-panel.portal {\n position: fixed;\n z-index: var(--wui-layer-menu, 100);\n}\n\n:host([portal]) .popover-panel {\n display: none;\n}\n"],"mappings":";AAAA,IAAA,gBAAA"}
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/popover/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: inline-flex;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n}\n\n.popover-trigger {\n display: inline-flex;\n}\n\n.popover-anchor {\n position: relative;\n display: inline-flex;\n}\n\n.popover-panel {\n --wui-internal-glass-shadow: var(--wui-shadow-panel, 0 3px 9px rgb(0 0 0 / 0.27));\n --wui-glass-corner-radius: var(--wui-radius-menu, 18px);\n\n /* panel 宽远大于角半径,环底色铺出的整圈铺底会在长边中段读成一条比四角更重的\n 直边描边;转 transparent 后只剩四段角弧,见 menu-portal.css 同处注释。 */\n --wui-color-glass-ring: transparent;\n position: absolute;\n z-index: var(--wui-layer-inline-overlay, 10);\n top: -9999px;\n left: -9999px;\n\n box-sizing: border-box;\n width: max-content;\n max-width: min(90vw, 400px);\n padding: var(--wui-space-2, 8px) var(--wui-space-3, 12px);\n border-radius: var(--wui-radius-menu, 18px);\n\n font-size: var(--wui-font-size, 14px);\n line-height: var(--wui-line-height-normal, 1.5);\n color: var(--wui-color-text, #1b1b1b);\n\n /* 玻璃背景:wui-glass 在 panel 自身,这里覆盖其默认玻璃底为 menu surface 色 */\n background-color: var(--wui-color-surface-menu, rgb(250 250 250 / 0.76));\n\n &:where([hidden]) {\n display: none;\n }\n\n &:focus-visible {\n outline: var(--wui-focus-ring-width, 3px) solid\n var(--wui-color-focus-ring, color-mix(in srgb, var(--wui-color-accent, #0071e3) 40%, transparent));\n outline-offset: 2px;\n }\n}\n\n.popover-panel.portal {\n position: fixed;\n z-index: var(--wui-layer-menu, 1000);\n}\n\n:host([portal]) .popover-panel {\n display: none;\n}\n"],"mappings":";AAAA,IAAA,gBAAA"}
@@ -0,0 +1,30 @@
1
+ import { LitElement } from "lit";
2
+ /**
3
+ * 线性进度指示。宿主本身承担 `progressbar` 语义,numeric props 写入后始终按 `0..max` 暴露。
4
+ *
5
+ * @csspart base - 轨道与可选数值组成的网格
6
+ * @csspart track - 进度槽
7
+ * @csspart indicator - 已完成部分
8
+ * @csspart value - `show-value` 时的可见百分比文本
9
+ */
10
+ export declare class WebUiProgress extends LitElement {
11
+ static styles: import("lit").CSSResult;
12
+ set value(value: number);
13
+ get value(): number;
14
+ private _value;
15
+ set max(value: number);
16
+ get max(): number;
17
+ private _max;
18
+ indeterminate: boolean;
19
+ showValue: boolean;
20
+ connectedCallback(): void;
21
+ protected updated(): void;
22
+ private _syncAria;
23
+ render(): import("lit").TemplateResult<1>;
24
+ }
25
+ declare global {
26
+ interface HTMLElementTagNameMap {
27
+ 'web-ui-progress': WebUiProgress;
28
+ }
29
+ }
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/progress/index.ts"],"mappings":";;;;;;;;;qBAoBa,sBAAsB;SACjB,sBAAM;MAGlB,MAAM;MAWN;UAII;MAGJ,IAAI;MAYJ;UAII;EAEoC;EACyB;EAE5D;YAKU;UAUX;EAgBC,wBAAM;;QAqBT;YACI;IACR,mBAAmB"}
@@ -0,0 +1,103 @@
1
+ import __decorate from "../../_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/decorate.js";
2
+ import { normalizeNumber } from "../../shared/normalize/index.js";
3
+ import { resolveBuiltinLabel } from "../../shared/locale/builtin-labels.js";
4
+ import style_default from "./style.js";
5
+ import { LitElement, html, unsafeCSS } from "lit";
6
+ import { customElement, property } from "lit/decorators.js";
7
+ import { styleMap } from "lit/directives/style-map.js";
8
+ // src/components/progress/index.ts
9
+ const MAX_SAFE = Number.MAX_SAFE_INTEGER;
10
+ let WebUiProgress = class WebUiProgress extends LitElement {
11
+ constructor(..._args) {
12
+ super(..._args);
13
+ this._value = 0;
14
+ this._max = 100;
15
+ this.indeterminate = false;
16
+ this.showValue = false;
17
+ }
18
+ static {
19
+ this.styles = unsafeCSS(style_default);
20
+ }
21
+ set value(value) {
22
+ const normalized = normalizeNumber(value, 0, MAX_SAFE, 0);
23
+ if (this._value === normalized) return;
24
+ const old = this.value;
25
+ this._value = normalized;
26
+ this.requestUpdate("value", old);
27
+ if (this.getAttribute("value") !== String(normalized)) this.setAttribute("value", String(normalized));
28
+ }
29
+ get value() {
30
+ return Math.min(this._value, this._max);
31
+ }
32
+ set max(value) {
33
+ const normalized = typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.min(MAX_SAFE, value) : 100;
34
+ if (this._max === normalized) return;
35
+ const old = this._max;
36
+ this._max = normalized;
37
+ this.requestUpdate("max", old);
38
+ if (this.getAttribute("max") !== String(normalized)) this.setAttribute("max", String(normalized));
39
+ }
40
+ get max() {
41
+ return this._max;
42
+ }
43
+ connectedCallback() {
44
+ super.connectedCallback();
45
+ this._syncAria();
46
+ }
47
+ updated() {
48
+ if (this._value > this._max) {
49
+ const old = this._value;
50
+ this._value = this._max;
51
+ this.requestUpdate("value", old);
52
+ this.setAttribute("value", String(this._max));
53
+ }
54
+ this._syncAria();
55
+ }
56
+ _syncAria() {
57
+ this.setAttribute("role", "progressbar");
58
+ this.setAttribute("aria-valuemin", "0");
59
+ this.setAttribute("aria-valuemax", String(this.max));
60
+ if (this.indeterminate) this.removeAttribute("aria-valuenow");
61
+ else this.setAttribute("aria-valuenow", String(this.value));
62
+ if (!this.getAttribute("aria-label")?.trim() && !this.getAttribute("aria-labelledby")?.trim()) this.setAttribute("aria-label", resolveBuiltinLabel("progress"));
63
+ }
64
+ render() {
65
+ const percent = this.value / this.max * 100;
66
+ const valueText = this.indeterminate ? "…" : `${Math.round(percent)}%`;
67
+ return html`
68
+ <div class="wui-progress" part="base">
69
+ <div class="wui-progress-track" part="track">
70
+ <div
71
+ class="wui-progress-indicator"
72
+ part="indicator"
73
+ style=${styleMap({ "--wui-progress-percent": `${percent}%` })}
74
+ ></div>
75
+ </div>
76
+ <span class="wui-progress-value" part="value" aria-hidden="true" ?hidden=${!this.showValue}>
77
+ ${valueText}
78
+ </span>
79
+ </div>
80
+ `;
81
+ }
82
+ };
83
+ __decorate([property({
84
+ type: Number,
85
+ reflect: true
86
+ })], WebUiProgress.prototype, "value", null);
87
+ __decorate([property({
88
+ type: Number,
89
+ reflect: true
90
+ })], WebUiProgress.prototype, "max", null);
91
+ __decorate([property({
92
+ type: Boolean,
93
+ reflect: true
94
+ })], WebUiProgress.prototype, "indeterminate", void 0);
95
+ __decorate([property({
96
+ type: Boolean,
97
+ reflect: true,
98
+ attribute: "show-value"
99
+ })], WebUiProgress.prototype, "showValue", void 0);
100
+ WebUiProgress = __decorate([customElement("web-ui-progress")], WebUiProgress);
101
+ export { WebUiProgress };
102
+
103
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["style"],"sources":["../../../src/components/progress/index.ts"],"sourcesContent":["import { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { styleMap } from 'lit/directives/style-map.js'\n\nimport { resolveBuiltinLabel } from '@/shared/locale/builtin-labels'\nimport { normalizeNumber } from '@/shared/normalize'\n\nimport style from './style.css?inline'\n\nconst MAX_SAFE = Number.MAX_SAFE_INTEGER\n\n/**\n * 线性进度指示。宿主本身承担 `progressbar` 语义,numeric props 写入后始终按 `0..max` 暴露。\n *\n * @csspart base - 轨道与可选数值组成的网格\n * @csspart track - 进度槽\n * @csspart indicator - 已完成部分\n * @csspart value - `show-value` 时的可见百分比文本\n */\n@customElement('web-ui-progress')\nexport class WebUiProgress extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ type: Number, reflect: true })\n set value(value: number) {\n const normalized = normalizeNumber(value, 0, MAX_SAFE, 0)\n if (this._value === normalized) return\n const old = this.value\n this._value = normalized\n this.requestUpdate('value', old)\n if (this.getAttribute('value') !== String(normalized)) {\n this.setAttribute('value', String(normalized))\n }\n }\n\n get value(): number {\n return Math.min(this._value, this._max)\n }\n\n private _value = 0\n\n @property({ type: Number, reflect: true })\n set max(value: number) {\n const normalized =\n typeof value === 'number' && Number.isFinite(value) && value > 0 ? Math.min(MAX_SAFE, value) : 100\n if (this._max === normalized) return\n const old = this._max\n this._max = normalized\n this.requestUpdate('max', old)\n if (this.getAttribute('max') !== String(normalized)) {\n this.setAttribute('max', String(normalized))\n }\n }\n\n get max(): number {\n return this._max\n }\n\n private _max = 100\n\n @property({ type: Boolean, reflect: true }) indeterminate = false\n @property({ type: Boolean, reflect: true, attribute: 'show-value' }) showValue = false\n\n override connectedCallback() {\n super.connectedCallback()\n this._syncAria()\n }\n\n protected override updated() {\n if (this._value > this._max) {\n const old = this._value\n this._value = this._max\n this.requestUpdate('value', old)\n this.setAttribute('value', String(this._max))\n }\n this._syncAria()\n }\n\n private _syncAria() {\n this.setAttribute('role', 'progressbar')\n this.setAttribute('aria-valuemin', '0')\n this.setAttribute('aria-valuemax', String(this.max))\n\n if (this.indeterminate) {\n this.removeAttribute('aria-valuenow')\n } else {\n this.setAttribute('aria-valuenow', String(this.value))\n }\n\n if (!this.getAttribute('aria-label')?.trim() && !this.getAttribute('aria-labelledby')?.trim()) {\n this.setAttribute('aria-label', resolveBuiltinLabel('progress'))\n }\n }\n\n override render() {\n const percent = (this.value / this.max) * 100\n const valueText = this.indeterminate ? '…' : `${Math.round(percent)}%`\n\n return html`\n <div class=\"wui-progress\" part=\"base\">\n <div class=\"wui-progress-track\" part=\"track\">\n <div\n class=\"wui-progress-indicator\"\n part=\"indicator\"\n style=${styleMap({ '--wui-progress-percent': `${percent}%` })}\n ></div>\n </div>\n <span class=\"wui-progress-value\" part=\"value\" aria-hidden=\"true\" ?hidden=${!this.showValue}>\n ${valueText}\n </span>\n </div>\n `\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-progress': WebUiProgress\n }\n}\n"],"mappings":";;;;;;;;AASA,MAAM,WAAW,OAAO;AAWjB,IAAM,gBAAN,MAAM,sBAAsB,WAAW;;;EAmB3B,KAAA,SAAA;EAmBF,KAAA,OAAA;EAE6C,KAAA,gBAAA;EACqB,KAAA,YAAA;;;EAxCxD,KAAA,SAAA,UAAUA,aAAK;;CAExC,IACI,MAAM,OAAe;EACvB,MAAM,aAAa,gBAAgB,OAAO,GAAG,UAAU,CAAC;EACxD,IAAI,KAAK,WAAW,YAAY;EAChC,MAAM,MAAM,KAAK;EACjB,KAAK,SAAS;EACd,KAAK,cAAc,SAAS,GAAG;EAC/B,IAAI,KAAK,aAAa,OAAO,MAAM,OAAO,UAAU,GAClD,KAAK,aAAa,SAAS,OAAO,UAAU,CAAC;CAEjD;CAEA,IAAI,QAAgB;EAClB,OAAO,KAAK,IAAI,KAAK,QAAQ,KAAK,IAAI;CACxC;CAIA,IACI,IAAI,OAAe;EACrB,MAAM,aACJ,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KAAK,QAAQ,IAAI,KAAK,IAAI,UAAU,KAAK,IAAI;EACjG,IAAI,KAAK,SAAS,YAAY;EAC9B,MAAM,MAAM,KAAK;EACjB,KAAK,OAAO;EACZ,KAAK,cAAc,OAAO,GAAG;EAC7B,IAAI,KAAK,aAAa,KAAK,MAAM,OAAO,UAAU,GAChD,KAAK,aAAa,OAAO,OAAO,UAAU,CAAC;CAE/C;CAEA,IAAI,MAAc;EAChB,OAAO,KAAK;CACd;CAOA,oBAA6B;EAC3B,MAAM,kBAAkB;EACxB,KAAK,UAAU;CACjB;CAEA,UAA6B;EAC3B,IAAI,KAAK,SAAS,KAAK,MAAM;GAC3B,MAAM,MAAM,KAAK;GACjB,KAAK,SAAS,KAAK;GACnB,KAAK,cAAc,SAAS,GAAG;GAC/B,KAAK,aAAa,SAAS,OAAO,KAAK,IAAI,CAAC;EAC9C;EACA,KAAK,UAAU;CACjB;CAEA,YAAoB;EAClB,KAAK,aAAa,QAAQ,aAAa;EACvC,KAAK,aAAa,iBAAiB,GAAG;EACtC,KAAK,aAAa,iBAAiB,OAAO,KAAK,GAAG,CAAC;EAEnD,IAAI,KAAK,eACP,KAAK,gBAAgB,eAAe;OAEpC,KAAK,aAAa,iBAAiB,OAAO,KAAK,KAAK,CAAC;EAGvD,IAAI,CAAC,KAAK,aAAa,YAAY,CAAC,EAAE,KAAK,KAAK,CAAC,KAAK,aAAa,iBAAiB,CAAC,EAAE,KAAK,GAC1F,KAAK,aAAa,cAAc,oBAAoB,UAAU,CAAC;CAEnE;CAEA,SAAkB;EAChB,MAAM,UAAW,KAAK,QAAQ,KAAK,MAAO;EAC1C,MAAM,YAAY,KAAK,gBAAgB,MAAM,GAAG,KAAK,MAAM,OAAO,EAAE;EAEpE,OAAO,IAAI;;;;;;oBAMK,SAAS,EAAE,0BAA0B,GAAG,QAAQ,GAAG,CAAC,EAAE;;;mFAGS,CAAC,KAAK,UAAU;YACvF,UAAU;;;;CAIpB;AACF;AA1FG,WAAA,CAAA,SAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,cAAA,WAAA,SAAA,IAAA;AAkBxC,WAAA,CAAA,SAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,cAAA,WAAA,OAAA,IAAA;AAmBxC,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,cAAA,WAAA,iBAAA,KAAA,CAAA;AACzC,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,SAAS;CAAM,WAAW;AAAa,CAAC,CAAA,GAAA,cAAA,WAAA,aAAA,KAAA,CAAA;AA1CpE,gBAAA,WAAA,CAAA,cAAc,iBAAiB,CAAA,GAAA,aAAA"}
@@ -0,0 +1,5 @@
1
+ // src/components/progress/style.css?inline
2
+ var style_default = "*,:before,:after{box-sizing:border-box}:host{width:100%;min-width:0;display:block}.wui-progress{gap:var(--wui-space-2,8px);grid-template-columns:minmax(0,1fr) auto;align-items:center;display:grid}.wui-progress-track{width:100%;height:var(--wui-progress-height,8px);border-radius:var(--wui-progress-radius,var(--wui-radius-control,calc(infinity * 1px)));background-color:var(--wui-progress-track-color,var(--wui-color-surface-track,#e5e5ea));overflow:hidden}.wui-progress-indicator{width:var(--wui-progress-percent,0%);border-radius:inherit;background-color:var(--wui-progress-fill-color,var(--wui-color-accent,#0071e3));height:100%;transition:width var(--wui-duration-trigger,.16s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1))}:host([indeterminate]) .wui-progress-indicator{width:38%;animation:wui-progress-indeterminate var(--wui-progress-indeterminate-duration,var(--wui-duration-spinner,.8s)) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1)) infinite}.wui-progress-value{min-width:3ch;font-size:var(--wui-font-size-caption,12px);font-variant-numeric:tabular-nums;color:var(--wui-color-text,#1b1b1b);text-align:end;line-height:1}@keyframes wui-progress-indeterminate{0%{transform:translate(-120%)}to{transform:translate(360%)}}\n";
3
+ export { style_default as default };
4
+
5
+ //# sourceMappingURL=style.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/progress/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: block;\n width: 100%;\n min-width: 0;\n}\n\n.wui-progress {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n gap: var(--wui-space-2, 8px);\n align-items: center;\n}\n\n.wui-progress-track {\n overflow: hidden;\n\n width: 100%;\n height: var(--wui-progress-height, 8px);\n border-radius: var(--wui-progress-radius, var(--wui-radius-control, calc(infinity * 1px)));\n\n background-color: var(--wui-progress-track-color, var(--wui-color-surface-track, #e5e5ea));\n}\n\n.wui-progress-indicator {\n width: var(--wui-progress-percent, 0%);\n height: 100%;\n border-radius: inherit;\n\n background-color: var(--wui-progress-fill-color, var(--wui-color-accent, #0071e3));\n\n transition: width var(--wui-duration-trigger, 160ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n}\n\n:host([indeterminate]) .wui-progress-indicator {\n width: 38%;\n animation: wui-progress-indeterminate var(--wui-progress-indeterminate-duration, var(--wui-duration-spinner, 800ms))\n var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1)) infinite;\n}\n\n.wui-progress-value {\n min-width: 3ch;\n\n font-size: var(--wui-font-size-caption, 12px);\n font-variant-numeric: tabular-nums;\n line-height: 1;\n color: var(--wui-color-text, #1b1b1b);\n text-align: end;\n}\n\n@keyframes wui-progress-indeterminate {\n from {\n transform: translate3d(-120%, 0, 0);\n }\n\n to {\n transform: translate3d(360%, 0, 0);\n }\n}\n"],"mappings":";AAAA,IAAA,gBAAA"}
@@ -4,6 +4,8 @@ export declare class WebUiSegmentedTrigger extends LitElement {
4
4
  value: string;
5
5
  checked: boolean;
6
6
  disabled: boolean;
7
+ /** 纯图标 trigger 的可访问名;会转发到内部 `role="option"`。 */
8
+ ariaLabel: string | null;
7
9
  private readonly _groupManagement;
8
10
  private get _isDisabled();
9
11
  private get _isManagedByGroup();
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/segmented-trigger/index.ts"],"mappings":";qBAYa,8BAA8B;SACzB,sBAAM;EAEM;EACgB;EACA;mBAE3B;cAML;cAIA;UAIJ;UAUA;EAOC,wBAAM;WAsBE;IACf,QAAQ;;;QAIJ;YACI;IACR,4BAA4B"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/segmented-trigger/index.ts"],"mappings":";qBAaa,8BAA8B;SACzB,sBAAM;EAEM;EACgB;EACA;;EAEkB;mBAE7C;cAML;cAIA;UAIJ;UAUA;EAOC,wBAAM;WAuBE;IACf,QAAQ;;;QAIJ;YACI;IACR,4BAA4B"}
@@ -5,6 +5,7 @@ import style_default from "./style.js";
5
5
  import { LitElement, html, unsafeCSS } from "lit";
6
6
  import { customElement, property } from "lit/decorators.js";
7
7
  import { classMap } from "lit/directives/class-map.js";
8
+ import { ifDefined } from "lit/directives/if-defined.js";
8
9
  // src/components/segmented-trigger/index.ts
9
10
  installPointerFocusSuppression();
10
11
  let WebUiSegmentedTrigger = class WebUiSegmentedTrigger extends LitElement {
@@ -13,6 +14,7 @@ let WebUiSegmentedTrigger = class WebUiSegmentedTrigger extends LitElement {
13
14
  this.value = "";
14
15
  this.checked = false;
15
16
  this.disabled = false;
17
+ this.ariaLabel = null;
16
18
  this._groupManagement = defineGroupManaged(this, {
17
19
  context: selectionGroupContextKey,
18
20
  requestUpdate: () => this.requestUpdate(),
@@ -57,6 +59,7 @@ let WebUiSegmentedTrigger = class WebUiSegmentedTrigger extends LitElement {
57
59
  class=${classMap(cls)}
58
60
  tabindex=${this._isDisabled || this._groupManagement.getContext()?.tabStop === false ? "-1" : "0"}
59
61
  role="option"
62
+ aria-label=${ifDefined(this.ariaLabel ?? void 0)}
60
63
  aria-selected=${String(this.checked)}
61
64
  aria-disabled=${String(this._isDisabled)}
62
65
  @click=${this.handleClick}
@@ -76,6 +79,10 @@ __decorate([property({
76
79
  type: Boolean,
77
80
  reflect: true
78
81
  })], WebUiSegmentedTrigger.prototype, "disabled", void 0);
82
+ __decorate([property({
83
+ type: String,
84
+ attribute: "aria-label"
85
+ })], WebUiSegmentedTrigger.prototype, "ariaLabel", void 0);
79
86
  WebUiSegmentedTrigger = __decorate([customElement("web-ui-segmented-trigger")], WebUiSegmentedTrigger);
80
87
  export { WebUiSegmentedTrigger };
81
88
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["style"],"sources":["../../../src/components/segmented-trigger/index.ts"],"sourcesContent":["import { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\n\nimport { installPointerFocusSuppression } from '@/shared/focus/pointer-focus'\nimport { defineGroupManaged, selectionGroupContextKey, type SelectionGroupContext } from '@/shared/group-management'\n\nimport style from './style.css?inline'\n\ninstallPointerFocusSuppression()\n\n@customElement('web-ui-segmented-trigger')\nexport class WebUiSegmentedTrigger extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ type: String }) value = ''\n @property({ type: Boolean, reflect: true }) checked = false\n @property({ type: Boolean, reflect: true }) disabled = false\n\n private readonly _groupManagement = defineGroupManaged<SelectionGroupContext>(this, {\n context: selectionGroupContextKey,\n requestUpdate: () => this.requestUpdate(),\n equals: (a, b) => a?.disabled === b?.disabled && a?.tabStop === b?.tabStop\n }).make()\n\n private get _isDisabled(): boolean {\n return this.disabled || this._groupManagement.getContext()?.disabled === true\n }\n\n private get _isManagedByGroup(): boolean {\n return this._groupManagement.getContext() !== undefined\n }\n\n private handleClick() {\n if (this._isDisabled || this.checked) return\n this.checked = true\n // group-managed 时事件不冒泡/不组合,由 segmented 统一派发一次 host 事件\n const opts: EventInit = this._isManagedByGroup\n ? { bubbles: false, composed: false }\n : { bubbles: true, composed: true }\n this.dispatchEvent(new Event('change', opts))\n }\n\n private handleKeyDown(e: KeyboardEvent) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n this.handleClick()\n }\n }\n\n override render() {\n const cls = {\n 'wui-segmented-trigger': true,\n 'is-checked': this.checked,\n 'is-disabled': this._isDisabled\n }\n\n return html`\n <div\n class=${classMap(cls)}\n tabindex=${this._isDisabled || this._groupManagement.getContext()?.tabStop === false ? '-1' : '0'}\n role=\"option\"\n aria-selected=${String(this.checked)}\n aria-disabled=${String(this._isDisabled)}\n @click=${this.handleClick}\n @keydown=${this.handleKeyDown}\n >\n <slot></slot>\n </div>\n `\n }\n\n declare readonly $events: {\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-segmented-trigger': WebUiSegmentedTrigger\n }\n}\n"],"mappings":";;;;;;;;AASA,+BAA+B;AAGxB,IAAM,wBAAN,MAAM,8BAA8B,WAAW;;;EAGhB,KAAA,QAAA;EACkB,KAAA,UAAA;EACC,KAAA,WAAA;EAEnB,KAAA,mBAAA,mBAA0C,MAAM;GAClF,SAAS;GACT,qBAAqB,KAAK,cAAc;GACxC,SAAS,GAAG,MAAM,GAAG,aAAa,GAAG,YAAY,GAAG,YAAY,GAAG;EACrE,CAAC,CAAC,CAAC,KAAK;;;EAViB,KAAA,SAAA,UAAUA,aAAK;;CAYxC,IAAY,cAAuB;EACjC,OAAO,KAAK,YAAY,KAAK,iBAAiB,WAAW,CAAC,EAAE,aAAa;CAC3E;CAEA,IAAY,oBAA6B;EACvC,OAAO,KAAK,iBAAiB,WAAW,MAAM,KAAA;CAChD;CAEA,cAAsB;EACpB,IAAI,KAAK,eAAe,KAAK,SAAS;EACtC,KAAK,UAAU;EAEf,MAAM,OAAkB,KAAK,oBACzB;GAAE,SAAS;GAAO,UAAU;EAAM,IAClC;GAAE,SAAS;GAAM,UAAU;EAAK;EACpC,KAAK,cAAc,IAAI,MAAM,UAAU,IAAI,CAAC;CAC9C;CAEA,cAAsB,GAAkB;EACtC,IAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;GACtC,EAAE,eAAe;GACjB,KAAK,YAAY;EACnB;CACF;CAEA,SAAkB;EAChB,MAAM,MAAM;GACV,yBAAyB;GACzB,cAAc,KAAK;GACnB,eAAe,KAAK;EACtB;EAEA,OAAO,IAAI;;gBAEC,SAAS,GAAG,EAAE;mBACX,KAAK,eAAe,KAAK,iBAAiB,WAAW,CAAC,EAAE,YAAY,QAAQ,OAAO,IAAI;;wBAElF,OAAO,KAAK,OAAO,EAAE;wBACrB,OAAO,KAAK,WAAW,EAAE;iBAChC,KAAK,YAAY;mBACf,KAAK,cAAc;;;;;CAKpC;AAKF;AA5DG,WAAA,CAAA,SAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,sBAAA,WAAA,SAAA,KAAA,CAAA;AACzB,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,sBAAA,WAAA,WAAA,KAAA,CAAA;AACzC,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,sBAAA,WAAA,YAAA,KAAA,CAAA;AAN3C,wBAAA,WAAA,CAAA,cAAc,0BAA0B,CAAA,GAAA,qBAAA"}
1
+ {"version":3,"file":"index.js","names":["style"],"sources":["../../../src/components/segmented-trigger/index.ts"],"sourcesContent":["import { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\n\nimport { installPointerFocusSuppression } from '@/shared/focus/pointer-focus'\nimport { defineGroupManaged, selectionGroupContextKey, type SelectionGroupContext } from '@/shared/group-management'\n\nimport style from './style.css?inline'\n\ninstallPointerFocusSuppression()\n\n@customElement('web-ui-segmented-trigger')\nexport class WebUiSegmentedTrigger extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ type: String }) value = ''\n @property({ type: Boolean, reflect: true }) checked = false\n @property({ type: Boolean, reflect: true }) disabled = false\n /** 纯图标 trigger 的可访问名;会转发到内部 `role=\"option\"`。 */\n @property({ type: String, attribute: 'aria-label' }) override ariaLabel: string | null = null\n\n private readonly _groupManagement = defineGroupManaged<SelectionGroupContext>(this, {\n context: selectionGroupContextKey,\n requestUpdate: () => this.requestUpdate(),\n equals: (a, b) => a?.disabled === b?.disabled && a?.tabStop === b?.tabStop\n }).make()\n\n private get _isDisabled(): boolean {\n return this.disabled || this._groupManagement.getContext()?.disabled === true\n }\n\n private get _isManagedByGroup(): boolean {\n return this._groupManagement.getContext() !== undefined\n }\n\n private handleClick() {\n if (this._isDisabled || this.checked) return\n this.checked = true\n // group-managed 时事件不冒泡/不组合,由 segmented 统一派发一次 host 事件\n const opts: EventInit = this._isManagedByGroup\n ? { bubbles: false, composed: false }\n : { bubbles: true, composed: true }\n this.dispatchEvent(new Event('change', opts))\n }\n\n private handleKeyDown(e: KeyboardEvent) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n this.handleClick()\n }\n }\n\n override render() {\n const cls = {\n 'wui-segmented-trigger': true,\n 'is-checked': this.checked,\n 'is-disabled': this._isDisabled\n }\n\n return html`\n <div\n class=${classMap(cls)}\n tabindex=${this._isDisabled || this._groupManagement.getContext()?.tabStop === false ? '-1' : '0'}\n role=\"option\"\n aria-label=${ifDefined(this.ariaLabel ?? undefined)}\n aria-selected=${String(this.checked)}\n aria-disabled=${String(this._isDisabled)}\n @click=${this.handleClick}\n @keydown=${this.handleKeyDown}\n >\n <slot></slot>\n </div>\n `\n }\n\n declare readonly $events: {\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-segmented-trigger': WebUiSegmentedTrigger\n }\n}\n"],"mappings":";;;;;;;;;AAUA,+BAA+B;AAGxB,IAAM,wBAAN,MAAM,8BAA8B,WAAW;;;EAGhB,KAAA,QAAA;EACkB,KAAA,UAAA;EACC,KAAA,WAAA;EAEkC,KAAA,YAAA;EAErD,KAAA,mBAAA,mBAA0C,MAAM;GAClF,SAAS;GACT,qBAAqB,KAAK,cAAc;GACxC,SAAS,GAAG,MAAM,GAAG,aAAa,GAAG,YAAY,GAAG,YAAY,GAAG;EACrE,CAAC,CAAC,CAAC,KAAK;;;EAZiB,KAAA,SAAA,UAAUA,aAAK;;CAcxC,IAAY,cAAuB;EACjC,OAAO,KAAK,YAAY,KAAK,iBAAiB,WAAW,CAAC,EAAE,aAAa;CAC3E;CAEA,IAAY,oBAA6B;EACvC,OAAO,KAAK,iBAAiB,WAAW,MAAM,KAAA;CAChD;CAEA,cAAsB;EACpB,IAAI,KAAK,eAAe,KAAK,SAAS;EACtC,KAAK,UAAU;EAEf,MAAM,OAAkB,KAAK,oBACzB;GAAE,SAAS;GAAO,UAAU;EAAM,IAClC;GAAE,SAAS;GAAM,UAAU;EAAK;EACpC,KAAK,cAAc,IAAI,MAAM,UAAU,IAAI,CAAC;CAC9C;CAEA,cAAsB,GAAkB;EACtC,IAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;GACtC,EAAE,eAAe;GACjB,KAAK,YAAY;EACnB;CACF;CAEA,SAAkB;EAChB,MAAM,MAAM;GACV,yBAAyB;GACzB,cAAc,KAAK;GACnB,eAAe,KAAK;EACtB;EAEA,OAAO,IAAI;;gBAEC,SAAS,GAAG,EAAE;mBACX,KAAK,eAAe,KAAK,iBAAiB,WAAW,CAAC,EAAE,YAAY,QAAQ,OAAO,IAAI;;qBAErF,UAAU,KAAK,aAAa,KAAA,CAAS,EAAE;wBACpC,OAAO,KAAK,OAAO,EAAE;wBACrB,OAAO,KAAK,WAAW,EAAE;iBAChC,KAAK,YAAY;mBACf,KAAK,cAAc;;;;;CAKpC;AAKF;AA/DG,WAAA,CAAA,SAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,sBAAA,WAAA,SAAA,KAAA,CAAA;AACzB,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,sBAAA,WAAA,WAAA,KAAA,CAAA;AACzC,WAAA,CAAA,SAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,sBAAA,WAAA,YAAA,KAAA,CAAA;AAEzC,WAAA,CAAA,SAAS;CAAE,MAAM;CAAQ,WAAW;AAAa,CAAC,CAAA,GAAA,sBAAA,WAAA,aAAA,KAAA,CAAA;AARpD,wBAAA,WAAA,CAAA,cAAc,0BAA0B,CAAA,GAAA,qBAAA"}