@greypan/web-ui 11.0.0 → 11.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/README.CN.md +105 -41
  2. package/README.md +69 -0
  3. package/dist/assets/overlay-motion.js +1 -1
  4. package/dist/assets/overlay-motion.js.map +1 -1
  5. package/dist/assets/selection-control.js +1 -1
  6. package/dist/assets/selection-control.js.map +1 -1
  7. package/dist/classes/glass.js +6 -0
  8. package/dist/classes/glass.js.map +1 -0
  9. package/dist/classes/glow.js +6 -0
  10. package/dist/classes/glow.js.map +1 -0
  11. package/dist/classes/style.css +1 -0
  12. package/dist/components/autocomplete/index.d.ts +7 -0
  13. package/dist/components/autocomplete/index.js +33 -13
  14. package/dist/components/autocomplete/index.js.map +1 -1
  15. package/dist/components/autocomplete/style.js +1 -1
  16. package/dist/components/autocomplete/style.js.map +1 -1
  17. package/dist/components/avatar/index.js +2 -2
  18. package/dist/components/avatar/index.js.map +1 -1
  19. package/dist/components/avatar/style.js +1 -1
  20. package/dist/components/avatar/style.js.map +1 -1
  21. package/dist/components/back-top/index.d.ts +4 -0
  22. package/dist/components/back-top/index.js +60 -22
  23. package/dist/components/back-top/index.js.map +1 -1
  24. package/dist/components/back-top/style.js +1 -1
  25. package/dist/components/back-top/style.js.map +1 -1
  26. package/dist/components/badge/index.d.ts +3 -0
  27. package/dist/components/badge/index.js +29 -14
  28. package/dist/components/badge/index.js.map +1 -1
  29. package/dist/components/badge/style.js +1 -1
  30. package/dist/components/badge/style.js.map +1 -1
  31. package/dist/components/button/index.d.ts +3 -0
  32. package/dist/components/button/index.js +27 -18
  33. package/dist/components/button/index.js.map +1 -1
  34. package/dist/components/button/style.js +1 -1
  35. package/dist/components/button/style.js.map +1 -1
  36. package/dist/components/button-group/index.d.ts +3 -0
  37. package/dist/components/button-group/index.js +15 -2
  38. package/dist/components/button-group/index.js.map +1 -1
  39. package/dist/components/button-group/style.js +1 -1
  40. package/dist/components/button-group/style.js.map +1 -1
  41. package/dist/components/checkbox/index.js +6 -6
  42. package/dist/components/checkbox/index.js.map +1 -1
  43. package/dist/components/checkbox/style.js +1 -1
  44. package/dist/components/checkbox/style.js.map +1 -1
  45. package/dist/components/collapse/index.d.ts +15 -7
  46. package/dist/components/collapse/index.js +44 -26
  47. package/dist/components/collapse/index.js.map +1 -1
  48. package/dist/components/collapse/style.js +1 -1
  49. package/dist/components/collapse/style.js.map +1 -1
  50. package/dist/components/dialog/index.d.ts +1 -0
  51. package/dist/components/dialog/index.js +5 -2
  52. package/dist/components/dialog/index.js.map +1 -1
  53. package/dist/components/dialog/style.js +1 -1
  54. package/dist/components/dialog/style.js.map +1 -1
  55. package/dist/components/drawer/index.js +1 -1
  56. package/dist/components/drawer/index.js.map +1 -1
  57. package/dist/components/dropdown/index.d.ts +16 -0
  58. package/dist/components/dropdown/index.js +147 -18
  59. package/dist/components/dropdown/index.js.map +1 -1
  60. package/dist/components/dropdown-item/style.js +1 -1
  61. package/dist/components/dropdown-item/style.js.map +1 -1
  62. package/dist/components/editable-text/index.js +7 -3
  63. package/dist/components/editable-text/index.js.map +1 -1
  64. package/dist/components/editable-text/style.js +1 -1
  65. package/dist/components/editable-text/style.js.map +1 -1
  66. package/dist/components/empty/index.d.ts +10 -22
  67. package/dist/components/empty/index.js +19 -35
  68. package/dist/components/empty/index.js.map +1 -1
  69. package/dist/components/empty/style.js +1 -1
  70. package/dist/components/empty/style.js.map +1 -1
  71. package/dist/components/image-preview/index.js +2 -2
  72. package/dist/components/image-preview/index.js.map +1 -1
  73. package/dist/components/input/index.d.ts +1 -0
  74. package/dist/components/input/index.js +5 -2
  75. package/dist/components/input/index.js.map +1 -1
  76. package/dist/components/input/style.js +1 -1
  77. package/dist/components/input/style.js.map +1 -1
  78. package/dist/components/input-number/index.d.ts +31 -1
  79. package/dist/components/input-number/index.js +281 -22
  80. package/dist/components/input-number/index.js.map +1 -1
  81. package/dist/components/input-number/style.js +1 -1
  82. package/dist/components/input-number/style.js.map +1 -1
  83. package/dist/components/layout/index.js +44 -39
  84. package/dist/components/layout/index.js.map +1 -1
  85. package/dist/components/layout/style.js +1 -1
  86. package/dist/components/layout/style.js.map +1 -1
  87. package/dist/components/middle-ellipsis/index.d.ts +8 -1
  88. package/dist/components/middle-ellipsis/index.js +68 -10
  89. package/dist/components/middle-ellipsis/index.js.map +1 -1
  90. package/dist/components/option/style.js +1 -1
  91. package/dist/components/option/style.js.map +1 -1
  92. package/dist/components/popover/index.js +1 -1
  93. package/dist/components/popover/index.js.map +1 -1
  94. package/dist/components/popover/style.js +1 -1
  95. package/dist/components/popover/style.js.map +1 -1
  96. package/dist/components/radio/index.d.ts +5 -0
  97. package/dist/components/radio/index.js +33 -23
  98. package/dist/components/radio/index.js.map +1 -1
  99. package/dist/components/radio/interaction.d.ts +4 -0
  100. package/dist/components/radio/interaction.js +17 -0
  101. package/dist/components/radio/interaction.js.map +1 -0
  102. package/dist/components/radio/style.js +1 -1
  103. package/dist/components/radio/style.js.map +1 -1
  104. package/dist/components/radio-group/index.d.ts +22 -0
  105. package/dist/components/radio-group/index.js +219 -28
  106. package/dist/components/radio-group/index.js.map +1 -1
  107. package/dist/components/segmented/index.d.ts +2 -0
  108. package/dist/components/segmented/index.js +74 -18
  109. package/dist/components/segmented/index.js.map +1 -1
  110. package/dist/components/segmented/style.js +1 -1
  111. package/dist/components/segmented/style.js.map +1 -1
  112. package/dist/components/segmented-trigger/index.js +3 -2
  113. package/dist/components/segmented-trigger/index.js.map +1 -1
  114. package/dist/components/segmented-trigger/style.js +1 -1
  115. package/dist/components/segmented-trigger/style.js.map +1 -1
  116. package/dist/components/select/index.d.ts +8 -0
  117. package/dist/components/select/index.js +76 -25
  118. package/dist/components/select/index.js.map +1 -1
  119. package/dist/components/select/style.js +1 -1
  120. package/dist/components/select/style.js.map +1 -1
  121. package/dist/components/slider/index.d.ts +15 -0
  122. package/dist/components/slider/index.js +175 -39
  123. package/dist/components/slider/index.js.map +1 -1
  124. package/dist/components/slider/style.js +1 -1
  125. package/dist/components/slider/style.js.map +1 -1
  126. package/dist/components/spinner/index.d.ts +15 -0
  127. package/dist/components/spinner/index.js +79 -22
  128. package/dist/components/spinner/index.js.map +1 -1
  129. package/dist/components/spinner/style.js +1 -1
  130. package/dist/components/spinner/style.js.map +1 -1
  131. package/dist/components/svg-draw-lines/index.js +48 -12
  132. package/dist/components/svg-draw-lines/index.js.map +1 -1
  133. package/dist/components/switch/index.d.ts +11 -3
  134. package/dist/components/switch/index.js +57 -53
  135. package/dist/components/switch/index.js.map +1 -1
  136. package/dist/components/switch/style.js +1 -1
  137. package/dist/components/switch/style.js.map +1 -1
  138. package/dist/components/textarea/index.js +2 -2
  139. package/dist/components/textarea/index.js.map +1 -1
  140. package/dist/components/textarea/style.js +1 -1
  141. package/dist/components/textarea/style.js.map +1 -1
  142. package/dist/components/theme/index.d.ts +1 -0
  143. package/dist/components/theme/index.js +26 -15
  144. package/dist/components/theme/index.js.map +1 -1
  145. package/dist/components/theme/style.js +1 -1
  146. package/dist/components/theme/style.js.map +1 -1
  147. package/dist/components/toast/style.js +1 -1
  148. package/dist/components/toast/style.js.map +1 -1
  149. package/dist/components/toast/toast.js +1 -1
  150. package/dist/components/toast/toast.js.map +1 -1
  151. package/dist/components/tooltip/index.d.ts +1 -0
  152. package/dist/components/tooltip/index.js +10 -5
  153. package/dist/components/tooltip/index.js.map +1 -1
  154. package/dist/icons/generated/andrsrxn-inbox.d.ts +6 -0
  155. package/dist/icons/generated/andrsrxn-inbox.js +10 -0
  156. package/dist/icons/generated/andrsrxn-inbox.js.map +1 -0
  157. package/dist/icons/index.d.ts +1 -0
  158. package/dist/icons/index.js +75 -74
  159. package/dist/shared/focus/pointer-focus.js +12 -10
  160. package/dist/shared/focus/pointer-focus.js.map +1 -1
  161. package/dist/shared/gesture/drag-gesture.js +4 -4
  162. package/dist/shared/gesture/drag-gesture.js.map +1 -1
  163. package/dist/shared/group-management/index.js +43 -18
  164. package/dist/shared/group-management/index.js.map +1 -1
  165. package/dist/shared/menu-portal/menu-portal.js +1 -1
  166. package/dist/shared/menu-portal/menu-portal.js.map +1 -1
  167. package/dist/shared/overlay/overlay-root.js +1 -1
  168. package/dist/shared/overlay/overlay-root.js.map +1 -1
  169. package/dist/shared/overlay/overlay.js +7 -1
  170. package/dist/shared/overlay/overlay.js.map +1 -1
  171. package/package.json +5 -1
  172. package/dist/assets/glass.js +0 -6
  173. package/dist/assets/glass.js.map +0 -1
@@ -1,28 +1,33 @@
1
1
  import e from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
2
2
  import "../icon/index.js";
3
- import t from "../../assets/glass.js";
3
+ import t from "../../classes/glass.js";
4
4
  import n from "../../icons/generated/radix-icons-panel-left-minimized.js";
5
5
  import "../button/index.js";
6
- import { attachDragGesture as r } from "../../shared/gesture/drag-gesture.js";
7
- import { clamp as i } from "../../shared/gesture/physics.js";
6
+ import r from "../../classes/glow.js";
7
+ import { attachDragGesture as i } from "../../shared/gesture/drag-gesture.js";
8
+ import { clamp as a } from "../../shared/gesture/physics.js";
8
9
  import "../drawer/index.js";
9
- import { VisibleAreaController as a, defineVisibleAreaTracker as o } from "../../shared/visible-area/index.js";
10
- import s from "./style.js";
11
- import { LitElement as c, html as l, nothing as u, unsafeCSS as d } from "lit";
12
- import { customElement as f, property as p, state as m } from "lit/decorators.js";
13
- import { classMap as h } from "lit/directives/class-map.js";
14
- import { styleMap as g } from "lit/directives/style-map.js";
10
+ import { VisibleAreaController as o, defineVisibleAreaTracker as s } from "../../shared/visible-area/index.js";
11
+ import c from "./style.js";
12
+ import { LitElement as l, html as u, nothing as d, unsafeCSS as f } from "lit";
13
+ import { customElement as p, property as m, state as h } from "lit/decorators.js";
14
+ import { classMap as g } from "lit/directives/class-map.js";
15
+ import { styleMap as _ } from "lit/directives/style-map.js";
15
16
  //#region src/components/layout/index.ts
16
- var _ = "(width <= 640px)", v = class extends c {
17
+ var v = "(width <= 640px)", y = class extends l {
17
18
  constructor(...e) {
18
19
  super(...e), this.sidebarCollapsed = !1, this.sidebarWidth = "240px", this.collapsedWidth = "68px", this.sidebarResizable = !1, this.sidebarMinWidth = "", this.sidebarMaxWidth = "", this.sidebarOpen = !1, this.headerGlow = !1, this._mobile = !1, this._mobileQuery = null, this._onMobileQueryChange = (e) => {
19
20
  this._syncMobile(e.matches);
20
- }, this._visibleBanner = o({ onVisibleAreaChange: (e) => {
21
+ }, this._visibleBanner = s({ onVisibleAreaChange: (e) => {
21
22
  this.style.setProperty("--wui-layout-visible-banner-height", `${e.height}px`);
22
- } }).make(), this._visibleBannerController = new a(this, this._visibleBanner), this._resizeGesture = null, this._resizeStartWidth = 0, this._resizeWidth = null;
23
+ } }).make(), this._visibleBannerController = new o(this, this._visibleBanner), this._resizeGesture = null, this._resizeStartWidth = 0, this._resizeWidth = null;
23
24
  }
24
25
  static {
25
- this.styles = [d(t), d(s)];
26
+ this.styles = [
27
+ f(t),
28
+ f(r),
29
+ f(c)
30
+ ];
26
31
  }
27
32
  get mobile() {
28
33
  return this._mobile;
@@ -84,10 +89,10 @@ var _ = "(width <= 640px)", v = class extends c {
84
89
  _handleResizePointerDown(e) {
85
90
  if (!this.sidebarResizable || this.sidebarCollapsed || this._mobile || this._isResizing() || e.isPrimary === !1) return;
86
91
  let t = this.renderRoot.querySelector("aside");
87
- t && (this._resizeStartWidth = this._parsePx(window.getComputedStyle(t).width, this._parsePx(this.sidebarWidth, 240)), this._resizeWidth = null, t.classList.add("is-resizing"), this._resizeGesture = r(e, {
92
+ t && (this._resizeStartWidth = this._parsePx(window.getComputedStyle(t).width, this._parsePx(this.sidebarWidth, 240)), this._resizeWidth = null, t.classList.add("is-resizing"), this._resizeGesture = i(e, {
88
93
  axis: "x",
89
94
  onMove: (e) => {
90
- let t = this._resizeStartWidth + e.deltaX, n = i(t, this._resolveSidebarMinWidth(), this._resolveSidebarMaxWidth());
95
+ let t = this._resizeStartWidth + e.deltaX, n = a(t, this._resolveSidebarMinWidth(), this._resolveSidebarMaxWidth());
91
96
  this._resizeWidth = `${Math.round(n)}px`;
92
97
  },
93
98
  onEnd: () => {
@@ -145,7 +150,7 @@ var _ = "(width <= 640px)", v = class extends c {
145
150
  e.preventDefault(), this._resizeWidth = `${Math.round(o)}px`;
146
151
  }
147
152
  _syncMobileViewportSubscription() {
148
- this._mobileQuery?.removeEventListener("change", this._onMobileQueryChange), this._mobileQuery = typeof window.matchMedia == "function" ? window.matchMedia(_) : null, this._mobileQuery?.addEventListener("change", this._onMobileQueryChange), this._syncMobile(this._mobileQuery?.matches ?? !1);
153
+ this._mobileQuery?.removeEventListener("change", this._onMobileQueryChange), this._mobileQuery = typeof window.matchMedia == "function" ? window.matchMedia(v) : null, this._mobileQuery?.addEventListener("change", this._onMobileQueryChange), this._syncMobile(this._mobileQuery?.matches ?? !1);
149
154
  }
150
155
  connectedCallback() {
151
156
  super.connectedCallback(), this._syncMobileViewportSubscription(), this.updateComplete.then(() => {
@@ -166,7 +171,7 @@ var _ = "(width <= 640px)", v = class extends c {
166
171
  this._visibleBanner.setTarget(e ? t : null);
167
172
  }
168
173
  render() {
169
- let e = !this.sidebarCollapsed, t = this._mobile ? {} : { width: this._resizeWidth ?? (this.sidebarCollapsed ? this.collapsedWidth : this.sidebarWidth) }, r = e ? "Collapse sidebar" : "Expand sidebar", i = l`
174
+ let e = !this.sidebarCollapsed, t = this._mobile ? {} : { width: this._resizeWidth ?? (this.sidebarCollapsed ? this.collapsedWidth : this.sidebarWidth) }, r = e ? "Collapse sidebar" : "Expand sidebar", i = u`
170
175
  <web-ui-button
171
176
  class="sidebar-toggle"
172
177
  icon
@@ -176,22 +181,22 @@ var _ = "(width <= 640px)", v = class extends c {
176
181
  >
177
182
  <web-ui-icon .icon=${n}></web-ui-icon>
178
183
  </web-ui-button>
179
- `, a = l`
184
+ `, a = u`
180
185
  <div class="sidebar-viewport">
181
186
  <slot name="sidebar"></slot>
182
187
  </div>
183
- `, o = l`
188
+ `, o = u`
184
189
  <div class="layout-body">
185
190
  <div class="sidebar-wrapper">
186
191
  <aside
187
- class="${h({ collapsed: !e })}"
188
- style="${g(t)}"
192
+ class="${g({ collapsed: !e })}"
193
+ style="${_(t)}"
189
194
  >
190
195
  <div class="aside-panel wui-glass">
191
196
  ${a}
192
197
  <div class="sidebar-toggle-area">${i}</div>
193
198
  </div>
194
- ${this.sidebarResizable && !this.sidebarCollapsed ? l`
199
+ ${this.sidebarResizable && !this.sidebarCollapsed ? u`
195
200
  <div
196
201
  class="sidebar-resize-handle"
197
202
  role="separator"
@@ -201,20 +206,20 @@ var _ = "(width <= 640px)", v = class extends c {
201
206
  @pointerdown=${this._handleResizePointerDown}
202
207
  @keydown=${this._handleResizeKeydown}
203
208
  ></div>
204
- ` : u}
209
+ ` : d}
205
210
  </aside>
206
211
  </div>
207
212
  <div class="layout-content">
208
- <header>
213
+ <header class="${g({ "wui-glow": this.headerGlow })}">
209
214
  <slot name="header"></slot>
210
215
  </header>
211
216
  <main><slot></slot></main>
212
217
  <footer><slot name="tabbar"></slot></footer>
213
218
  </div>
214
219
  </div>
215
- `, s = l`
220
+ `, s = u`
216
221
  <div class="layout-content">
217
- <header>
222
+ <header class="${g({ "wui-glow": this.headerGlow })}">
218
223
  <web-ui-button
219
224
  class="mobile-toggle"
220
225
  icon
@@ -237,7 +242,7 @@ var _ = "(width <= 640px)", v = class extends c {
237
242
  draggable
238
243
  dialog-label="Main navigation"
239
244
  @open-change="${this._onDrawerChange}"
240
- style="${g({
245
+ style="${_({
241
246
  "--wui-drawer-width": this.sidebarWidth,
242
247
  "--wui-drawer-radius": "var(--wui-layout-sidebar-radius, 24px)"
243
248
  })}"
@@ -245,7 +250,7 @@ var _ = "(width <= 640px)", v = class extends c {
245
250
  ${a}
246
251
  </web-ui-drawer>
247
252
  `;
248
- return l`
253
+ return u`
249
254
  <div class="layout-page">
250
255
  <div class="layout-banner">
251
256
  <slot name="banner" @slotchange="${this._onBannerSlotChange}"></slot>
@@ -255,44 +260,44 @@ var _ = "(width <= 640px)", v = class extends c {
255
260
  `;
256
261
  }
257
262
  };
258
- e([p({
263
+ e([m({
259
264
  type: Boolean,
260
265
  attribute: "sidebar-collapsed",
261
266
  reflect: !0
262
- })], v.prototype, "sidebarCollapsed", void 0), e([p({
267
+ })], y.prototype, "sidebarCollapsed", void 0), e([m({
263
268
  type: String,
264
269
  attribute: "sidebar-width",
265
270
  reflect: !0
266
- })], v.prototype, "sidebarWidth", void 0), e([p({
271
+ })], y.prototype, "sidebarWidth", void 0), e([m({
267
272
  type: String,
268
273
  attribute: "collapsed-width",
269
274
  reflect: !0
270
- })], v.prototype, "collapsedWidth", void 0), e([p({
275
+ })], y.prototype, "collapsedWidth", void 0), e([m({
271
276
  type: Boolean,
272
277
  attribute: "sidebar-resizable",
273
278
  reflect: !0
274
- })], v.prototype, "sidebarResizable", void 0), e([p({
279
+ })], y.prototype, "sidebarResizable", void 0), e([m({
275
280
  type: String,
276
281
  attribute: "sidebar-min-width",
277
282
  reflect: !0
278
- })], v.prototype, "sidebarMinWidth", void 0), e([p({
283
+ })], y.prototype, "sidebarMinWidth", void 0), e([m({
279
284
  type: String,
280
285
  attribute: "sidebar-max-width",
281
286
  reflect: !0
282
- })], v.prototype, "sidebarMaxWidth", void 0), e([p({
287
+ })], y.prototype, "sidebarMaxWidth", void 0), e([m({
283
288
  type: Boolean,
284
289
  attribute: "sidebar-open",
285
290
  reflect: !0
286
- })], v.prototype, "sidebarOpen", void 0), e([p({
291
+ })], y.prototype, "sidebarOpen", void 0), e([m({
287
292
  type: Boolean,
288
293
  attribute: "header-glow",
289
294
  reflect: !0
290
- })], v.prototype, "headerGlow", void 0), e([p({
295
+ })], y.prototype, "headerGlow", void 0), e([m({
291
296
  type: Boolean,
292
297
  attribute: "mobile",
293
298
  reflect: !0
294
- })], v.prototype, "_mobile", void 0), e([m()], v.prototype, "_resizeWidth", void 0), v = e([f("web-ui-layout")], v);
299
+ })], y.prototype, "_mobile", void 0), e([h()], y.prototype, "_resizeWidth", void 0), y = e([p("web-ui-layout")], y);
295
300
  //#endregion
296
- export { v as WebUiLayout };
301
+ export { y as WebUiLayout };
297
302
 
298
303
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/layout/index.ts"],"sourcesContent":["import '@/components/button'\nimport '@/components/drawer'\nimport '@/components/icon'\n\nimport { html, LitElement, nothing, 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 '@/assets/glass.css?inline'\nimport { radixIconsPanelLeftMinimized } from '@/icons'\nimport { attachDragGesture, clamp, type DragGestureHandle } from '@/shared/gesture'\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\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 决定其内部固定区域与滚动容器\n * @slot tabbar - 底部 tabbar 区域\n * @slot default - 主内容区\n */\n@customElement('web-ui-layout')\nexport class WebUiLayout extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\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 /*\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 与视口跨越移动端断点(handle 随桌面 layout 卸载)共用此收尾。\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 override render() {\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 = isSidebarOpen ? 'Collapse sidebar' : 'Expand sidebar'\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 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=\"Resize sidebar\"\n @pointerdown=${this._handleResizePointerDown}\n @keydown=${this._handleResizeKeydown}\n ></div>\n `\n : nothing\n }\n </aside>\n </div>\n <div class=\"layout-content\">\n <header>\n <slot name=\"header\"></slot>\n </header>\n <main><slot></slot></main>\n <footer><slot name=\"tabbar\"></slot></footer>\n </div>\n </div>\n `\n\n const mobileLayout = html`\n <div class=\"layout-content\">\n <header>\n <web-ui-button\n class=\"mobile-toggle\"\n icon\n variant=\"glass\"\n @click=\"${this._toggleSidebar}\"\n aria-label=\"Open navigation menu\"\n >\n <web-ui-icon .icon=${radixIconsPanelLeftMinimized}></web-ui-icon>\n </web-ui-button>\n <slot name=\"header\"></slot>\n </header>\n <main><slot></slot></main>\n <footer><slot name=\"tabbar\"></slot></footer>\n </div>\n\n <web-ui-drawer\n placement=\"left\"\n ?open=\"${this.sidebarOpen}\"\n controlled\n draggable\n dialog-label=\"Main navigation\"\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 return html`\n <div class=\"layout-page\">\n <div class=\"layout-banner\">\n <slot name=\"banner\" @slotchange=\"${this._onBannerSlotChange}\"></slot>\n </div>\n ${this._mobile ? mobileLayout : desktopLayout}\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":[],"sources":["../../../src/components/layout/index.ts"],"sourcesContent":["import '@/components/button'\nimport '@/components/drawer'\nimport '@/components/icon'\n\nimport { html, LitElement, nothing, 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 { 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\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 决定其内部固定区域与滚动容器\n * @slot tabbar - 底部 tabbar 区域\n * @slot default - 主内容区\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 的持久布局偏好。\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 /*\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 与视口跨越移动端断点(handle 随桌面 layout 卸载)共用此收尾。\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 override render() {\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 = isSidebarOpen ? 'Collapse sidebar' : 'Expand sidebar'\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 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=\"Resize sidebar\"\n @pointerdown=${this._handleResizePointerDown}\n @keydown=${this._handleResizeKeydown}\n ></div>\n `\n : nothing\n }\n </aside>\n </div>\n <div class=\"layout-content\">\n <header class=\"${classMap({ 'wui-glow': this.headerGlow })}\">\n <slot name=\"header\"></slot>\n </header>\n <main><slot></slot></main>\n <footer><slot name=\"tabbar\"></slot></footer>\n </div>\n </div>\n `\n\n const mobileLayout = html`\n <div class=\"layout-content\">\n <header class=\"${classMap({ 'wui-glow': this.headerGlow })}\">\n <web-ui-button\n class=\"mobile-toggle\"\n icon\n variant=\"glass\"\n @click=\"${this._toggleSidebar}\"\n aria-label=\"Open navigation menu\"\n >\n <web-ui-icon .icon=${radixIconsPanelLeftMinimized}></web-ui-icon>\n </web-ui-button>\n <slot name=\"header\"></slot>\n </header>\n <main><slot></slot></main>\n <footer><slot name=\"tabbar\"></slot></footer>\n </div>\n\n <web-ui-drawer\n placement=\"left\"\n ?open=\"${this.sidebarOpen}\"\n controlled\n draggable\n dialog-label=\"Main navigation\"\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 return html`\n <div class=\"layout-page\">\n <div class=\"layout-banner\">\n <slot name=\"banner\" @slotchange=\"${this._onBannerSlotChange}\"></slot>\n </div>\n ${this._mobile ? mobileLayout : desktopLayout}\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
  //#region src/components/layout/style.css?inline
2
- var e = "*,: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,#08f);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,#08f) 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)}:host([header-glow]) header:before{pointer-events:none;content:\"\";z-index:-1;background:linear-gradient(to bottom, var(--wui-color-page,#fff), transparent);margin:-10px;position:absolute;inset:0}:host([header-glow]) header:after{pointer-events:none;content:\"\";z-index:-1;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);margin:0 -30px -30px;position:absolute;inset:0;-webkit-mask-image:linear-gradient(#000 0% 45%,#0000 100%);mask-image:linear-gradient(#000 0% 45%,#0000 100%);-webkit-mask-size:100% 100%;mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}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}}";
2
+ var e = "*,: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}}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -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 * 默认值与历史硬编码一致,不设置时行为不变。\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, #08f);\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, #08f) 40%, transparent));\n outline-offset: 2px;\n}\n\n/* 拖拽中禁用宽度过渡,保证实时跟手。 */\naside.is-resizing {\n transition: none;\n}\n\n.layout-content {\n /*\n * glow 的两层伪元素用负 margin 向外扩展(见 header::before / ::after),会溢出内容列\n * 并撑出页面横向 scrollbar(移动端约 30px),横向裁剪因此归属本层。它有 min-width: 0,\n * 是容纳横向溢出的位置。\n * clip 不创建滚动容器,因此不影响 header / footer 自身的 sticky;也不用 hidden——\n * 那会把本层变成滚动容器,sticky 随即失效。\n *\n * 这一层不负责纵向羽化,也切不到它:glow 的下缘断层来自 header 自身的裁剪上下文\n * (见 header 的注释),真 Safari 上把本层改成 overflow-x: visible / unset\n * 与保持 clip 的截图逐像素相同。\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 既把负层级 glow 限定在本 Header 的局部 stacking context\n * (slot 内容自然位于其上),也顺带使本层成为该上下文的裁剪边界:z-index: -1 的\n * glow 伪元素溢出 header 盒的部分会被切掉。横向裁剪上移到 .layout-content。\n *\n * 「纵向必须 visible 才留得住下拉 overlay 的溢出空间」仍然成立——overflow-* 一旦生效\n * 就会裁剪该轴,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\n/*\n * Header 的装饰性背景晕染\n * ========================\n *\n * 两层伪元素随 header 的实际盒子尺寸和 sticky 位置变化,自然适配 header slot 的\n * 高度与位置,无需单独维护一层 DOM。::before 是晕染色本身,::after 在更外一圈叠一层\n * 真实模糊(backdrop-filter)。职责分开后,模糊强度与晕染浓度互不牵制。\n *\n * 两层都用负 margin 撑开,而不是 transform: scale。渐变完全画在盒内,底缘在\n * 触及 header 盒之前就归零,盒外那一段不带可见不透明度。\n *\n * ::after 只向下、向两侧撑开,**不向上越出 header 盒上缘**(横向 -30px 由祖先\n * .layout-content 的 overflow-x: clip 裁掉)。原因是 WebKit 会把元素盒在视口之外\n * 那一段的**视口外画面**也采进 backdrop:sticky header 贴住视口顶端时它上缘就是\n * 视口上缘,向上越出的 30px 正落在视口外,Safari 于是把那条的颜色——文档顶端以上\n * 的浏览器/窗口底色,或滚动上去的页面内容——模糊后晕染进 header 顶缘。系统暗色 +\n * 页面浅色时那条底色是黑的,页顶就出现一条黑色阴影(#197)。Chrome 不采样视口外,\n * 所以只在 Safari 复现;`clip-path` 也拦不住(裁的是绘制范围,不是采样范围),\n * 必须让盒子本身不越界。::before 是纯渐变、没有 backdrop-filter,不采样,保留\n * 原有的 -10px。\n *\n * ::after 的 backdrop-filter 按规范会把模糊延伸到盒外,而 `isolation: isolate` 让\n * header 成为 z-index: -1 伪元素的裁剪边界,WebKit 在这条边界上会切掉盒外那段\n * (真 Safari 实测 11 级台阶)。mask 把模糊沿高度收到 0,但**残余不为 0**:它的盒\n * 比 header 高 30px,mask 在盒高 45% 之前保持不透明、之后线性归零。45% 是折算来的:\n * 旧盒高 H+60、截止 60%,衰减起点在 header 顶下 0.6H+6;新盒高 H+30,同一绝对起点\n * 换算成 (0.6H+6)/(H+30),header 高 40px 时 43%、110px 时 51%,故取中段的 45%。实测\n * 衰减段(header 底缘以下)在 header 高 52px(demo 的取值)与改动前逐像素一致,\n * 40~110px 全区间衰减段差异 ≤1/255;header 盒内因衰减起点上移有几级差别(≤9/255)。\n * 改回 60% 会把 header 底缘推到衰减段起点(H=52 时该处 alpha 由 0.67 升到 0.91),\n * header 正下方的模糊明显加重。\n *\n * 端点用裸 `transparent`(即 rgb(0 0 0 / 0))而不是\n * `color-mix(in srgb, var(--wui-color-page) 100%, transparent)`:混色按预乘 alpha\n * 进行,后者算出来是完全不透明的原色(alpha = 1×1 + 0×0 = 1),终点没有羽化,实测\n * 台阶反而从 11 级恶化到 152 级。裸 transparent 在渐变插值中同样按预乘处理,各引擎\n * 一致淡到 0,且不引入新色相。\n */\n:host([header-glow]) header::before {\n pointer-events: none;\n content: '';\n\n position: absolute;\n z-index: -1;\n inset: 0;\n\n margin: -10px;\n\n background: linear-gradient(to bottom, var(--wui-color-page, #fff), transparent);\n}\n\n:host([header-glow]) header::after {\n pointer-events: none;\n content: '';\n\n position: absolute;\n z-index: -1;\n inset: 0;\n\n margin: 0 -30px -30px;\n\n backdrop-filter: blur(4px);\n\n mask-image: linear-gradient(to bottom, black 0%, black 45%, transparent 100%);\n mask-repeat: no-repeat;\n mask-size: 100% 100%;\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":""}
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 * 默认值与历史硬编码一致,不设置时行为不变。\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":""}
@@ -36,14 +36,20 @@ export declare class WebUiMiddleEllipsis extends LitElement {
36
36
  private _markerProbe?;
37
37
  /** 最近一次切分依据的可用宽度(px);-1 表示还没量到过布局。 */
38
38
  private _available;
39
+ /** 最近一次 ResizeObserver 测得的宿主内容盒宽度;收敛守卫跳过拟合时也继续更新。 */
40
+ private _observedWidth;
39
41
  /** 当前渲染出来的文本实际占宽(px)。收敛守卫靠它判断现有输出是否已经放得下。 */
40
42
  private _rendered;
41
- /** 输入变过、还没按新输入切过。 */
43
+ /** 输入或字体度量变过、还没重新拟合。 */
42
44
  private _dirty;
43
45
  private _refineRound;
46
+ private _observedMeasure?;
44
47
  private readonly _range;
45
48
  private readonly _width;
49
+ private readonly _onMeasurementChange;
46
50
  private readonly _probe;
51
+ connectedCallback(): void;
52
+ disconnectedCallback(): void;
47
53
  willUpdate(changed: PropertyValues<this>): void;
48
54
  updated(): void;
49
55
  /**
@@ -89,6 +95,7 @@ export declare class WebUiMiddleEllipsis extends LitElement {
89
95
  private _settle;
90
96
  /** 把全文装进隐藏量具。它没有 Lit 绑定,改它不会触发重渲染。 */
91
97
  private _syncMeasure;
98
+ private _observeMeasure;
92
99
  /** `[from, to)` 这段原文在量具里的渲染宽度。空区间恒为 0——Range 塌缩矩形的宽度不可靠。 */
93
100
  private _rangeWidth;
94
101
  render(): import('lit').TemplateResult<1>;
@@ -7,9 +7,57 @@ import { LitElement as a, html as o, unsafeCSS as s } from "lit";
7
7
  import { customElement as c, property as l, query as u } from "lit/decorators.js";
8
8
  import { ifDefined as d } from "lit/directives/if-defined.js";
9
9
  //#region src/components/middle-ellipsis/index.ts
10
- var f = 0, p = 100, m = 50, h = .5, g = 3, _ = .5, v = class extends a {
10
+ var f = 0, p = 100, m = 50, h = .5, g = 3, _ = .5, v = /* @__PURE__ */ new WeakMap(), y, b = /* @__PURE__ */ new Set(), x;
11
+ function S() {
12
+ for (let e of b) e();
13
+ }
14
+ function C(e) {
15
+ let t = document.fonts;
16
+ t && (x || (x = t, x.addEventListener("loadingdone", S)), b.add(e));
17
+ }
18
+ function w(e) {
19
+ b.delete(e), !(b.size > 0 || !x) && (x.removeEventListener("loadingdone", S), x = void 0);
20
+ }
21
+ function T() {
22
+ return typeof ResizeObserver > "u" ? null : y || (y = new ResizeObserver((e) => {
23
+ for (let t of e) {
24
+ let e = v.get(t.target);
25
+ if (!e) continue;
26
+ let n = t.target.textContent, r = t.contentRect.width;
27
+ if (e.text !== n) {
28
+ e.text = n, e.width = r;
29
+ continue;
30
+ }
31
+ if (e.width < 0) {
32
+ e.width = r;
33
+ continue;
34
+ }
35
+ e.width !== r && (e.width = r, e.notify());
36
+ }
37
+ }), y);
38
+ }
39
+ function E(e, t) {
40
+ v.set(e, {
41
+ notify: t,
42
+ text: e.textContent,
43
+ width: -1
44
+ }), C(t);
45
+ let n = T();
46
+ n && n.observe(e, { box: "content-box" });
47
+ }
48
+ function D(e) {
49
+ let t = v.get(e);
50
+ t && w(t.notify), v.delete(e), y?.unobserve(e);
51
+ }
52
+ function O(e, t) {
53
+ let n = v.get(e);
54
+ n && (n.text = t, n.width = e.getBoundingClientRect().width);
55
+ }
56
+ var k = class extends a {
11
57
  constructor(...e) {
12
- super(...e), this.text = "", this._marker = "…", this._markerPosition = m, this._display = "", this._truncated = !1, this._available = -1, this._rendered = 0, this._dirty = !0, this._refineRound = 0, this._range = document.createRange(), this._width = new n(this, (e) => this._onWidth(e)), this._probe = {
58
+ super(...e), this.text = "", this._marker = "…", this._markerPosition = m, this._display = "", this._truncated = !1, this._available = -1, this._observedWidth = -1, this._rendered = 0, this._dirty = !0, this._refineRound = 0, this._range = document.createRange(), this._width = new n(this, (e) => this._onWidth(e)), this._onMeasurementChange = () => {
59
+ this._refineRound = 0, this._dirty = !0, this._apply(this._observedWidth > 0 ? this._observedWidth : this._currentWidth());
60
+ }, this._probe = {
13
61
  prefix: (e) => this._rangeWidth(0, e),
14
62
  suffix: (e) => this._rangeWidth(this.text.length - e, this.text.length),
15
63
  marker: () => this._markerProbe?.getBoundingClientRect().width ?? 0
@@ -40,13 +88,19 @@ var f = 0, p = 100, m = 50, h = .5, g = 3, _ = .5, v = class extends a {
40
88
  get markerPosition() {
41
89
  return this._markerPosition;
42
90
  }
91
+ connectedCallback() {
92
+ super.connectedCallback(), this._available >= 0 && (this._dirty = !0, this._refineRound = 0), this._observeMeasure();
93
+ }
94
+ disconnectedCallback() {
95
+ this._observedMeasure &&= (D(this._observedMeasure), void 0), super.disconnectedCallback();
96
+ }
43
97
  willUpdate(e) {
44
98
  (e.has("text") || e.has("marker") || e.has("markerPosition")) && (this._dirty = !0, this._display = this.text, this._truncated = !1);
45
99
  }
46
100
  updated() {
47
101
  let e = this._textEl;
48
- if (e) {
49
- if (this._rendered = e.getBoundingClientRect().width, this._dirty) {
102
+ if (e && this.isConnected) {
103
+ if (this._observeMeasure(), this._rendered = e.getBoundingClientRect().width, this._dirty) {
50
104
  this._refineRound = 0, this._defer(() => this._apply(this._currentWidth()));
51
105
  return;
52
106
  }
@@ -62,7 +116,7 @@ var f = 0, p = 100, m = 50, h = .5, g = 3, _ = .5, v = class extends a {
62
116
  }
63
117
  }
64
118
  _onWidth(e) {
65
- e > 0 && (!this._dirty && (this._available >= 0 && Math.abs(e - this._available) <= _ || this._available >= 0 && e < this._available && this._rendered <= e + h) || (this._refineRound = 0, this._apply(e)));
119
+ this._observedWidth = e, e > 0 && (!this._dirty && (this._available >= 0 && Math.abs(e - this._available) <= _ || this._available >= 0 && e < this._available && this._rendered <= e + h) || (this._refineRound = 0, this._apply(e)));
66
120
  }
67
121
  _defer(e) {
68
122
  queueMicrotask(e);
@@ -81,7 +135,11 @@ var f = 0, p = 100, m = 50, h = .5, g = 3, _ = .5, v = class extends a {
81
135
  }
82
136
  _syncMeasure() {
83
137
  let e = this._measure;
84
- e && e.textContent !== this.text && (e.textContent = this.text);
138
+ e && e.textContent !== this.text && (e.textContent = this.text, O(e, this.text));
139
+ }
140
+ _observeMeasure() {
141
+ let e = this._measure;
142
+ e && e !== this._observedMeasure && (this._observedMeasure && D(this._observedMeasure), this._observedMeasure = e, E(e, this._onMeasurementChange));
85
143
  }
86
144
  _rangeWidth(e, t) {
87
145
  if (t <= e) return 0;
@@ -98,15 +156,15 @@ var f = 0, p = 100, m = 50, h = .5, g = 3, _ = .5, v = class extends a {
98
156
  `;
99
157
  }
100
158
  };
101
- e([l({ type: String })], v.prototype, "text", void 0), e([l({
159
+ e([l({ type: String })], k.prototype, "text", void 0), e([l({
102
160
  type: String,
103
161
  reflect: !0
104
- })], v.prototype, "marker", null), e([l({
162
+ })], k.prototype, "marker", null), e([l({
105
163
  type: Number,
106
164
  reflect: !0,
107
165
  attribute: "marker-position"
108
- })], v.prototype, "markerPosition", null), e([u(".root")], v.prototype, "_root", void 0), e([u(".text")], v.prototype, "_textEl", void 0), e([u(".measure")], v.prototype, "_measure", void 0), e([u(".marker-probe")], v.prototype, "_markerProbe", void 0), v = e([c("web-ui-middle-ellipsis")], v);
166
+ })], k.prototype, "markerPosition", null), e([u(".root")], k.prototype, "_root", void 0), e([u(".text")], k.prototype, "_textEl", void 0), e([u(".measure")], k.prototype, "_measure", void 0), e([u(".marker-probe")], k.prototype, "_markerProbe", void 0), k = e([c("web-ui-middle-ellipsis")], k);
109
167
  //#endregion
110
- export { v as WebUiMiddleEllipsis };
168
+ export { k as WebUiMiddleEllipsis };
111
169
 
112
170
  //# sourceMappingURL=index.js.map