@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,4 +1,4 @@
1
- import { LitElement } from 'lit';
1
+ import { LitElement, PropertyValues } from 'lit';
2
2
  declare const WebUiSwitch_base: {
3
3
  new (...args: any[]): {
4
4
  formResetCallback(): void;
@@ -366,6 +366,8 @@ declare const WebUiSwitch_base: {
366
366
  export declare class WebUiSwitch extends WebUiSwitch_base {
367
367
  static styles: import('lit').CSSResult[];
368
368
  private _checked;
369
+ /** label 没有可渲染内容时收掉 track 与 label 之间的 gap。 */
370
+ private _labelEmpty;
369
371
  get checked(): boolean;
370
372
  set checked(v: boolean);
371
373
  name: string;
@@ -381,14 +383,20 @@ export declare class WebUiSwitch extends WebUiSwitch_base {
381
383
  private _maxTravel;
382
384
  private _startOffset;
383
385
  private _dragGestureHandle;
384
- private readonly _fieldId;
386
+ private _tabIndexBeforeDisabled;
387
+ private _isTabIndexSuppressed;
388
+ private readonly _handleHostClick;
389
+ private readonly _handleHostKeydown;
390
+ connectedCallback(): void;
385
391
  disconnectedCallback(): void;
392
+ updated(changedProperties: PropertyValues<this>): void;
393
+ private _syncAccessibility;
386
394
  private get _isDisabled();
387
395
  private readonly _formAssociation;
388
396
  private readonly _formAssociationController;
397
+ private readonly _labelEmptiness;
389
398
  private _syncValidity;
390
399
  private handleClick;
391
- private handleNativeChange;
392
400
  private handleKeydown;
393
401
  private _applyChecked;
394
402
  private handlePointerDown;
@@ -1,10 +1,10 @@
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/lucide-loader-circle.js";
5
5
  import { installPointerFocusSuppression as r } from "../../shared/focus/pointer-focus.js";
6
6
  import { FormAssociated as i, FormAssociationController as a, defineFormAssociation as o } from "../../shared/form-association/index.js";
7
- import { createFieldId as s } from "../../shared/form-association/field-id.js";
7
+ import { LabelEmptinessController as s } from "../../shared/label-emptiness/index.js";
8
8
  import { attachDragGesture as c } from "../../shared/gesture/drag-gesture.js";
9
9
  import { clamp as l, normalizeProgress as u } from "../../shared/gesture/physics.js";
10
10
  import d from "./style.js";
@@ -16,7 +16,7 @@ import { styleMap as y } from "lit/directives/style-map.js";
16
16
  r();
17
17
  var b = class extends i(f) {
18
18
  constructor(...e) {
19
- super(...e), this._checked = !1, this.name = "", this.value = "", this.disabled = !1, this.required = !1, this.loading = !1, this._pressed = !1, this._isDragging = !1, this._dragOffset = 0, this._dragProgress = 0, this._dragged = !1, this._maxTravel = 20, this._startOffset = 0, this._dragGestureHandle = null, this._fieldId = s("web-ui-switch"), this._formAssociation = o({
19
+ super(...e), this._checked = !1, this._labelEmpty = !1, this.name = "", this.value = "", this.disabled = !1, this.required = !1, this.loading = !1, this._pressed = !1, this._isDragging = !1, this._dragOffset = 0, this._dragProgress = 0, this._dragged = !1, this._maxTravel = 20, this._startOffset = 0, this._dragGestureHandle = null, this._tabIndexBeforeDisabled = null, this._isTabIndexSuppressed = !1, this._handleHostClick = () => this.handleClick(), this._handleHostKeydown = (e) => this.handleKeydown(e), this._formAssociation = o({
20
20
  host: this,
21
21
  initialize: () => {
22
22
  this.hasAttribute("checked") && (this._checked = !0);
@@ -31,7 +31,9 @@ var b = class extends i(f) {
31
31
  typeof e == "string" && (this.checked = e === "true");
32
32
  },
33
33
  syncValidity: () => this._syncValidity()
34
- }).make(), this._formAssociationController = new a(this, this._formAssociation);
34
+ }).make(), this._formAssociationController = new a(this, this._formAssociation), this._labelEmptiness = new s(this, ".wui-switch-label", (e) => {
35
+ this._labelEmpty = e;
36
+ });
35
37
  }
36
38
  static {
37
39
  this.styles = [m(t), m(d)];
@@ -43,8 +45,19 @@ var b = class extends i(f) {
43
45
  let t = this._checked;
44
46
  this._checked = e, this._formAssociation.sync(), this.requestUpdate("checked", t);
45
47
  }
48
+ connectedCallback() {
49
+ super.connectedCallback(), this.addEventListener("click", this._handleHostClick), this.addEventListener("keydown", this._handleHostKeydown), this._syncAccessibility();
50
+ }
46
51
  disconnectedCallback() {
47
- super.disconnectedCallback(), this._dragGestureHandle?.destroy(), this._dragGestureHandle = null;
52
+ super.disconnectedCallback(), this.removeEventListener("click", this._handleHostClick), this.removeEventListener("keydown", this._handleHostKeydown), this._dragGestureHandle?.destroy(), this._dragGestureHandle = null;
53
+ }
54
+ updated(e) {
55
+ super.updated(e), this._syncAccessibility();
56
+ }
57
+ _syncAccessibility() {
58
+ this.setAttribute("role", "switch"), this.setAttribute("aria-checked", String(this._checked));
59
+ let e = this._isDisabled || this.loading;
60
+ this.setAttribute("aria-disabled", String(e)), e ? (this._isTabIndexSuppressed ? this.getAttribute("tabindex") !== "-1" && (this._tabIndexBeforeDisabled = this.getAttribute("tabindex")) : (this._tabIndexBeforeDisabled = this.getAttribute("tabindex"), this._isTabIndexSuppressed = !0), this.tabIndex = -1) : this._isTabIndexSuppressed ? (this._tabIndexBeforeDisabled === null ? this.tabIndex = 0 : this.setAttribute("tabindex", this._tabIndexBeforeDisabled), this._isTabIndexSuppressed = !1) : this.hasAttribute("tabindex") || (this.tabIndex = 0);
48
61
  }
49
62
  get _isDisabled() {
50
63
  return this.disabled || this._formAssociation.isFormDisabled();
@@ -53,26 +66,15 @@ var b = class extends i(f) {
53
66
  let e = this._formAssociation.getInternals();
54
67
  e && typeof e.setValidity == "function" && (this._isDisabled || !this.required || this._checked ? e.setValidity({}) : e.setValidity({ valueMissing: !0 }, "Enable this option"));
55
68
  }
56
- handleClick(e) {
57
- if (!(e.target instanceof HTMLInputElement)) {
58
- if (e.preventDefault(), this._dragged) {
59
- this._dragged = !1;
60
- return;
61
- }
62
- this._isDisabled || this.loading || this._applyChecked(!this._checked);
63
- }
64
- }
65
- handleNativeChange(e) {
66
- if (e.target instanceof HTMLInputElement) {
67
- if (this._dragged) {
68
- this._dragged = !1;
69
- return;
70
- }
71
- this._applyChecked(e.target.checked, !1);
69
+ handleClick() {
70
+ if (this._dragged) {
71
+ this._dragged = !1;
72
+ return;
72
73
  }
74
+ this._isDisabled || this.loading || this._applyChecked(!this._checked);
73
75
  }
74
76
  handleKeydown(e) {
75
- e.key === "Enter" && (e.preventDefault(), !(this._isDisabled || this.loading) && this._applyChecked(!this._checked));
77
+ (e.key === " " || e.key === "Enter") && (e.preventDefault(), !(e.repeat || this._isDisabled || this.loading) && this._applyChecked(!this._checked));
76
78
  }
77
79
  _applyChecked(e, t = !0) {
78
80
  let n = this._checked;
@@ -129,47 +131,49 @@ var b = class extends i(f) {
129
131
  this._isDragging || (this._pressed = !1);
130
132
  }
131
133
  render() {
132
- let e = {
134
+ let e = this._isDragging ? this._dragProgress >= .5 : this._checked, t = {
135
+ "wui-switch-content": !0,
136
+ "is-label-empty": this._labelEmpty
137
+ }, r = {
133
138
  "wui-switch-track": !0,
134
- "is-open": this._isDragging ? this._dragProgress >= .5 : this._checked,
139
+ "is-open": e,
135
140
  "is-dragging": this._isDragging,
136
141
  "is-disabled": this._isDisabled || this.loading
137
- }, t = {
142
+ }, i = {
143
+ "wui-switch-label": !0,
144
+ "is-disabled": this._isDisabled || this.loading
145
+ }, a = {
138
146
  "wui-switch-thumb": !0,
139
147
  "wui-glass": this._pressed || this._isDragging,
140
148
  "is-pressed": this._pressed || this._isDragging
141
- }, r = this._isDragging ? { "--wui-switch-drag-offset": `${this._dragOffset}px` } : {};
149
+ }, o = this._isDragging ? { "--wui-switch-drag-offset": `${this._dragOffset}px` } : {};
142
150
  return p`
143
- <label
144
- class=${v(e)}
145
- style=${y(r)}
146
- role="switch"
147
- aria-checked=${String(this._checked)}
148
- @click=${this.handleClick}
149
- @pointerdown=${this.handlePointerDown}
150
- @pointerup=${this.handlePointerUp}
151
- @pointercancel=${this.handlePointerUp}
152
- @pointerleave=${this.handlePointerLeave}
153
- >
154
- <input
155
- id=${this._fieldId}
156
- type="checkbox"
157
- .checked=${this._checked}
158
- ?disabled=${this._isDisabled || this.loading}
159
- class="sr-only"
160
- @change=${this.handleNativeChange}
161
- @keydown=${this.handleKeydown}
162
- />
163
- <div class=${v(t)}>
164
- ${this.loading ? p`<div class="wui-switch-loading">
165
- <web-ui-icon .icon=${n} size="14" color="#08f" spin></web-ui-icon>
166
- </div>` : ""}
167
- </div>
168
- </label>
151
+ <span class=${v(t)}>
152
+ <span
153
+ class=${v(r)}
154
+ style=${y(o)}
155
+ @pointerdown=${this.handlePointerDown}
156
+ @pointerup=${this.handlePointerUp}
157
+ @pointercancel=${this.handlePointerUp}
158
+ @pointerleave=${this.handlePointerLeave}
159
+ >
160
+ <span class=${v(a)}>
161
+ ${this.loading ? p`<span class="wui-switch-loading">
162
+ <web-ui-icon
163
+ .icon=${n}
164
+ size="14"
165
+ color="var(--wui-color-accent, #0071e3)"
166
+ spin
167
+ ></web-ui-icon>
168
+ </span>` : ""}
169
+ </span>
170
+ </span>
171
+ <span class=${v(i)}><slot></slot></span>
172
+ </span>
169
173
  `;
170
174
  }
171
175
  };
172
- e([_()], b.prototype, "_checked", void 0), e([g({
176
+ e([_()], b.prototype, "_checked", void 0), e([_()], b.prototype, "_labelEmpty", void 0), e([g({
173
177
  type: String,
174
178
  reflect: !0
175
179
  })], b.prototype, "name", void 0), e([g({ type: String })], b.prototype, "value", void 0), e([g({
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/switch/index.ts"],"sourcesContent":["import '@/components/icon'\n\nimport { html, LitElement, 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 { lucideLoaderCircle } from '@/icons'\nimport { installPointerFocusSuppression } from '@/shared/focus/pointer-focus'\nimport { FormAssociated, defineFormAssociation, FormAssociationController } from '@/shared/form-association'\nimport { createFieldId } from '@/shared/form-association/field-id'\nimport { attachDragGesture, type DragGestureHandle } from '@/shared/gesture/drag-gesture'\nimport { clamp, normalizeProgress } from '@/shared/gesture/physics'\n\nimport style from './style.css?inline'\n\ninstallPointerFocusSuppression()\n\n@customElement('web-ui-switch')\nexport class WebUiSwitch extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n @state() private _checked = false\n\n get checked(): boolean {\n return this._checked\n }\n\n set checked(v: boolean) {\n const old = this._checked\n this._checked = v\n this._formAssociation.sync()\n this.requestUpdate('checked', old)\n }\n\n @property({ type: String, reflect: true }) name = ''\n @property({ type: String }) value = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) required = false\n @property({ type: Boolean, reflect: true }) loading = false\n @state() private _pressed = false\n @state() private _isDragging = false\n @state() private _dragOffset = 0\n @state() private _dragProgress = 0\n\n private _dragged = false\n private _maxTravel = 20\n private _startOffset = 0\n private _dragGestureHandle: DragGestureHandle | null = null\n private readonly _fieldId = createFieldId('web-ui-switch')\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._dragGestureHandle?.destroy()\n this._dragGestureHandle = null\n }\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n private readonly _formAssociation = defineFormAssociation<boolean>({\n host: this,\n initialize: () => {\n if (this.hasAttribute('checked')) this._checked = true\n },\n getState: () => this._checked,\n setState: checked => {\n this.checked = checked\n },\n getFormValue: () => (this._checked ? this.value || 'on' : null),\n getFormState: () => String(this._checked),\n restoreState: state => {\n if (typeof state === 'string') this.checked = state === 'true'\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n private _syncValidity() {\n const internals = this._formAssociation.getInternals()\n if (!internals || typeof internals.setValidity !== 'function') return\n if (this._isDisabled || !this.required || this._checked) internals.setValidity({})\n else internals.setValidity({ valueMissing: true }, 'Enable this option')\n }\n\n // 阻止 label 默认行为,避免原生 checkbox 重复触发点击。\n // 但内层 input 自己触发的 click(空格/辅助技术的原生激活)必须放行:\n // 它已经完成勾选,再 preventDefault 会把原生激活撤销,造成\n // 「宿主 checked=true、内层 checkbox=false」的分裂。\n private handleClick(e: Event) {\n if (e.target instanceof HTMLInputElement) return\n e.preventDefault()\n if (this._dragged) {\n this._dragged = false\n return\n }\n if (this._isDisabled || this.loading) return\n this._applyChecked(!this._checked)\n }\n\n // 原生激活(空格、辅助技术)已完成勾选,这里只把状态同步过来。\n // 原生 input 事件本身是 composed,已经冒泡到宿主,不能再补一个;\n // 原生 change 不是 composed,过不了 shadow 边界,需要由组件转发。\n private handleNativeChange(e: Event) {\n if (!(e.target instanceof HTMLInputElement)) return\n if (this._dragged) {\n this._dragged = false\n return\n }\n this._applyChecked(e.target.checked, false)\n }\n\n // 原生 checkbox 不响应 Enter,补上 role=\"switch\" 应有的开合语义\n private handleKeydown(e: KeyboardEvent) {\n if (e.key !== 'Enter') return\n e.preventDefault()\n if (this._isDisabled || this.loading) return\n this._applyChecked(!this._checked)\n }\n\n private _applyChecked(next: boolean, emitInput = true) {\n const old = this._checked\n if (next === old) return\n this._checked = next\n this._formAssociation.sync()\n this.requestUpdate('checked', old)\n if (emitInput) this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private handlePointerDown(e: PointerEvent) {\n if (this._isDisabled || this.loading) return\n this._pressed = true\n\n const track = this.renderRoot.querySelector<HTMLElement>('.wui-switch-track')\n if (track) {\n const trackWidth = track.getBoundingClientRect().width || 40\n // 拖拽时 thumb 的 pressed 布局盒为 20px(scale(1.5) 视觉放大),因此行程按 20px 布局盒钳制;\n // 释放后的 checked 静止位移为 20px(16px thumb),两者由 transition 衔接。\n this._maxTravel = Math.max(1, trackWidth - 4 - 20)\n } else {\n this._maxTravel = 16\n }\n\n this._startOffset = this._checked ? this._maxTravel : 0\n this._dragOffset = this._startOffset\n this._dragProgress = this._checked ? 1 : 0\n\n this._dragGestureHandle?.destroy()\n this._dragGestureHandle = attachDragGesture(e, {\n axis: 'x',\n threshold: 6,\n onMove: info => {\n this._isDragging = true\n const current = clamp(this._startOffset + info.deltaX, 0, this._maxTravel)\n this._dragOffset = current\n this._dragProgress = normalizeProgress(current, 0, this._maxTravel)\n this.requestUpdate()\n },\n onEnd: info => {\n const wasDragging = this._isDragging\n this._isDragging = false\n this._pressed = false\n\n if (wasDragging) {\n this._dragged = true\n let targetChecked = this._checked\n if (info.velocityX > 300) {\n targetChecked = true\n } else if (info.velocityX < -300) {\n targetChecked = false\n } else {\n targetChecked = this._dragProgress >= 0.5\n }\n\n if (targetChecked !== this._checked) {\n const old = this._checked\n this._checked = targetChecked\n this._formAssociation.sync()\n this.requestUpdate('checked', old)\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n } else {\n this.requestUpdate()\n }\n }\n },\n onCancel: () => {\n this._isDragging = false\n this._pressed = false\n this.requestUpdate()\n }\n })\n }\n\n private handlePointerUp() {\n this._pressed = false\n }\n\n private handlePointerLeave() {\n if (!this._isDragging) {\n this._pressed = false\n }\n }\n\n override render() {\n const isOpen = this._isDragging ? this._dragProgress >= 0.5 : this._checked\n const trackCls = {\n 'wui-switch-track': true,\n 'is-open': isOpen,\n 'is-dragging': this._isDragging,\n 'is-disabled': this._isDisabled || this.loading\n }\n // wui-glass 条件化:静止态实体白 thumb,按压/拖拽时切换为玻璃(backdrop blur +\n // 无填充 + 描边)并放大,回到最初修复不跟手问题时期的视觉结构。\n const thumbCls = {\n 'wui-switch-thumb': true,\n 'wui-glass': this._pressed || this._isDragging,\n 'is-pressed': this._pressed || this._isDragging\n }\n const trackStyle = this._isDragging ? { '--wui-switch-drag-offset': `${this._dragOffset}px` } : {}\n\n return html`\n <label\n class=${classMap(trackCls)}\n style=${styleMap(trackStyle)}\n role=\"switch\"\n aria-checked=${String(this._checked)}\n @click=${this.handleClick}\n @pointerdown=${this.handlePointerDown}\n @pointerup=${this.handlePointerUp}\n @pointercancel=${this.handlePointerUp}\n @pointerleave=${this.handlePointerLeave}\n >\n <input\n id=${this._fieldId}\n type=\"checkbox\"\n .checked=${this._checked}\n ?disabled=${this._isDisabled || this.loading}\n class=\"sr-only\"\n @change=${this.handleNativeChange}\n @keydown=${this.handleKeydown}\n />\n <div class=${classMap(thumbCls)}>\n ${\n this.loading\n ? html`<div class=\"wui-switch-loading\">\n <web-ui-icon .icon=${lucideLoaderCircle} size=\"14\" color=\"#08f\" spin></web-ui-icon>\n </div>`\n : ''\n }\n </div>\n </label>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-switch': WebUiSwitch\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/switch/index.ts"],"sourcesContent":["import '@/components/icon'\n\nimport { html, LitElement, unsafeCSS, type PropertyValues } 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 { lucideLoaderCircle } from '@/icons'\nimport { installPointerFocusSuppression } from '@/shared/focus/pointer-focus'\nimport { FormAssociated, defineFormAssociation, FormAssociationController } from '@/shared/form-association'\nimport { attachDragGesture, type DragGestureHandle } from '@/shared/gesture/drag-gesture'\nimport { clamp, normalizeProgress } from '@/shared/gesture/physics'\nimport { LabelEmptinessController } from '@/shared/label-emptiness'\n\nimport style from './style.css?inline'\n\ninstallPointerFocusSuppression()\n\n@customElement('web-ui-switch')\nexport class WebUiSwitch extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n @state() private _checked = false\n /** label 没有可渲染内容时收掉 track 与 label 之间的 gap。 */\n @state() private _labelEmpty = false\n\n get checked(): boolean {\n return this._checked\n }\n\n set checked(v: boolean) {\n const old = this._checked\n this._checked = v\n this._formAssociation.sync()\n this.requestUpdate('checked', old)\n }\n\n @property({ type: String, reflect: true }) name = ''\n @property({ type: String }) value = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) required = false\n @property({ type: Boolean, reflect: true }) loading = false\n @state() private _pressed = false\n @state() private _isDragging = false\n @state() private _dragOffset = 0\n @state() private _dragProgress = 0\n\n private _dragged = false\n private _maxTravel = 20\n private _startOffset = 0\n private _dragGestureHandle: DragGestureHandle | null = null\n private _tabIndexBeforeDisabled: string | null = null\n private _isTabIndexSuppressed = false\n private readonly _handleHostClick = () => this.handleClick()\n private readonly _handleHostKeydown = (event: KeyboardEvent) => this.handleKeydown(event)\n\n override connectedCallback() {\n super.connectedCallback()\n this.addEventListener('click', this._handleHostClick)\n this.addEventListener('keydown', this._handleHostKeydown)\n this._syncAccessibility()\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.removeEventListener('click', this._handleHostClick)\n this.removeEventListener('keydown', this._handleHostKeydown)\n this._dragGestureHandle?.destroy()\n this._dragGestureHandle = null\n }\n\n override updated(changedProperties: PropertyValues<this>) {\n super.updated(changedProperties)\n this._syncAccessibility()\n }\n\n private _syncAccessibility() {\n this.setAttribute('role', 'switch')\n this.setAttribute('aria-checked', String(this._checked))\n const isDisabled = this._isDisabled || this.loading\n this.setAttribute('aria-disabled', String(isDisabled))\n\n if (isDisabled) {\n if (!this._isTabIndexSuppressed) {\n this._tabIndexBeforeDisabled = this.getAttribute('tabindex')\n this._isTabIndexSuppressed = true\n } else if (this.getAttribute('tabindex') !== '-1') {\n this._tabIndexBeforeDisabled = this.getAttribute('tabindex')\n }\n this.tabIndex = -1\n } else if (this._isTabIndexSuppressed) {\n if (this._tabIndexBeforeDisabled === null) this.tabIndex = 0\n else this.setAttribute('tabindex', this._tabIndexBeforeDisabled)\n this._isTabIndexSuppressed = false\n } else if (!this.hasAttribute('tabindex')) {\n this.tabIndex = 0\n }\n }\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n private readonly _formAssociation = defineFormAssociation<boolean>({\n host: this,\n initialize: () => {\n if (this.hasAttribute('checked')) this._checked = true\n },\n getState: () => this._checked,\n setState: checked => {\n this.checked = checked\n },\n getFormValue: () => (this._checked ? this.value || 'on' : null),\n getFormState: () => String(this._checked),\n restoreState: state => {\n if (typeof state === 'string') this.checked = state === 'true'\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n private readonly _labelEmptiness = new LabelEmptinessController(this, '.wui-switch-label', empty => {\n this._labelEmpty = empty\n })\n\n private _syncValidity() {\n const internals = this._formAssociation.getInternals()\n if (!internals || typeof internals.setValidity !== 'function') return\n if (this._isDisabled || !this.required || this._checked) internals.setValidity({})\n else internals.setValidity({ valueMissing: true }, 'Enable this option')\n }\n\n // 交互由 switch host 处理,light DOM 标签文本与轨道点击都会冒泡到这里。\n private handleClick() {\n if (this._dragged) {\n this._dragged = false\n return\n }\n if (this._isDisabled || this.loading) return\n this._applyChecked(!this._checked)\n }\n\n // 自定义 switch host 承接焦点,Space 与 Enter 都切换状态。\n private handleKeydown(e: KeyboardEvent) {\n if (e.key !== ' ' && e.key !== 'Enter') return\n e.preventDefault()\n if (e.repeat || this._isDisabled || this.loading) return\n this._applyChecked(!this._checked)\n }\n\n private _applyChecked(next: boolean, emitInput = true) {\n const old = this._checked\n if (next === old) return\n this._checked = next\n this._formAssociation.sync()\n this.requestUpdate('checked', old)\n if (emitInput) this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private handlePointerDown(e: PointerEvent) {\n if (this._isDisabled || this.loading) return\n this._pressed = true\n\n const track = this.renderRoot.querySelector<HTMLElement>('.wui-switch-track')\n if (track) {\n const trackWidth = track.getBoundingClientRect().width || 40\n // 拖拽时 thumb 的 pressed 布局盒为 20px(scale(1.5) 视觉放大),因此行程按 20px 布局盒钳制;\n // 释放后的 checked 静止位移为 20px(16px thumb),两者由 transition 衔接。\n this._maxTravel = Math.max(1, trackWidth - 4 - 20)\n } else {\n this._maxTravel = 16\n }\n\n this._startOffset = this._checked ? this._maxTravel : 0\n this._dragOffset = this._startOffset\n this._dragProgress = this._checked ? 1 : 0\n\n this._dragGestureHandle?.destroy()\n this._dragGestureHandle = attachDragGesture(e, {\n axis: 'x',\n threshold: 6,\n onMove: info => {\n this._isDragging = true\n const current = clamp(this._startOffset + info.deltaX, 0, this._maxTravel)\n this._dragOffset = current\n this._dragProgress = normalizeProgress(current, 0, this._maxTravel)\n this.requestUpdate()\n },\n onEnd: info => {\n const wasDragging = this._isDragging\n this._isDragging = false\n this._pressed = false\n\n if (wasDragging) {\n this._dragged = true\n let targetChecked = this._checked\n if (info.velocityX > 300) {\n targetChecked = true\n } else if (info.velocityX < -300) {\n targetChecked = false\n } else {\n targetChecked = this._dragProgress >= 0.5\n }\n\n if (targetChecked !== this._checked) {\n const old = this._checked\n this._checked = targetChecked\n this._formAssociation.sync()\n this.requestUpdate('checked', old)\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n } else {\n this.requestUpdate()\n }\n }\n },\n onCancel: () => {\n this._isDragging = false\n this._pressed = false\n this.requestUpdate()\n }\n })\n }\n\n private handlePointerUp() {\n this._pressed = false\n }\n\n private handlePointerLeave() {\n if (!this._isDragging) {\n this._pressed = false\n }\n }\n\n override render() {\n const isOpen = this._isDragging ? this._dragProgress >= 0.5 : this._checked\n const contentCls = {\n 'wui-switch-content': true,\n 'is-label-empty': this._labelEmpty\n }\n const trackCls = {\n 'wui-switch-track': true,\n 'is-open': isOpen,\n 'is-dragging': this._isDragging,\n 'is-disabled': this._isDisabled || this.loading\n }\n const labelCls = {\n 'wui-switch-label': true,\n 'is-disabled': this._isDisabled || this.loading\n }\n // wui-glass 条件化:静止态实体白 thumb,按压/拖拽时切换为玻璃(backdrop blur +\n // 无填充 + 描边)并放大,回到最初修复不跟手问题时期的视觉结构。\n const thumbCls = {\n 'wui-switch-thumb': true,\n 'wui-glass': this._pressed || this._isDragging,\n 'is-pressed': this._pressed || this._isDragging\n }\n const trackStyle = this._isDragging ? { '--wui-switch-drag-offset': `${this._dragOffset}px` } : {}\n\n return html`\n <span class=${classMap(contentCls)}>\n <span\n class=${classMap(trackCls)}\n style=${styleMap(trackStyle)}\n @pointerdown=${this.handlePointerDown}\n @pointerup=${this.handlePointerUp}\n @pointercancel=${this.handlePointerUp}\n @pointerleave=${this.handlePointerLeave}\n >\n <span class=${classMap(thumbCls)}>\n ${\n this.loading\n ? html`<span class=\"wui-switch-loading\">\n <web-ui-icon\n .icon=${lucideLoaderCircle}\n size=\"14\"\n color=\"var(--wui-color-accent, #0071e3)\"\n spin\n ></web-ui-icon>\n </span>`\n : ''\n }\n </span>\n </span>\n <span class=${classMap(labelCls)}><slot></slot></span>\n </span>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-switch': WebUiSwitch\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  //#region src/components/switch/style.css?inline
2
- var e = "*,:before,:after{box-sizing:border-box}:host{touch-action:none;display:inline-block}:host([disabled]){cursor:not-allowed}.sr-only{white-space:nowrap;clip-path:inset(50%);border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.wui-switch-track{touch-action:none;cursor:default;-webkit-user-select:none;user-select:none;border-radius:var(--wui-radius-control,calc(infinity * 1px));background-color:var(--wui-color-surface-track,#e5e5ea);outline:var(--wui-focus-ring-width,3px) solid transparent;outline-offset:2px;width:40px;height:20px;transition:background-color var(--wui-duration-trigger,.16s) ease;justify-content:space-between;align-items:center;display:flex;position:relative}.wui-switch-track:focus-within{outline-color:var(--wui-color-focus-ring,color-mix(in srgb, var(--wui-color-accent,#08f) 40%, transparent))}.wui-switch-track.is-open{background-color:var(--wui-color-accent,#08f)}.wui-switch-track.is-disabled{cursor:not-allowed;opacity:.4}.wui-switch-thumb{--wui-glass-corner-radius:calc(16px / 2);transform-origin:50%;border-radius:var(--wui-radius-control,calc(infinity * 1px));width:16px;height:16px;transition:width var(--wui-duration-press,80ms) cubic-bezier(.4, 0, .2, 1), transform var(--wui-duration-trigger,.16s) cubic-bezier(.4, 0, .2, 1), border-radius var(--wui-duration-press,80ms) cubic-bezier(.4, 0, .2, 1), background-color var(--wui-duration-press,80ms) ease, box-shadow var(--wui-duration-press,80ms) ease;position:absolute;top:2px;left:2px;overflow:hidden;transform:translate(0)scale(1)}.wui-switch-thumb.is-pressed{--wui-color-glass-ring:transparent;border-radius:var(--wui-radius-control,calc(infinity * 1px));width:20px;box-shadow:inset 0 0 0 .5px color-mix(in srgb, var(--wui-color-glass-highlight,#ffffffe6) 38%, transparent), inset 0 1px .5px color-mix(in srgb, var(--wui-color-glass-highlight,#ffffffe6) 52%, transparent), inset 0 -.5px 1px #00000008, 0 2px 20px #0003;background-color:#0000;transform:translate(0)scale(1.5)}.wui-switch-thumb:not(.is-pressed){background-color:var(--wui-color-on-control,#fff);box-shadow:1px 2px 6px #0003}.wui-switch-track.is-open .wui-switch-thumb{transform:translate(20px)scale(1)}.wui-switch-track.is-open .wui-switch-thumb.is-pressed{transform:translate(16px)scale(1.5)}.wui-switch-track.is-dragging{cursor:grabbing}:host([data-wui-pointer-focus]) .wui-switch-track:focus-within{outline-color:#0000}.wui-switch-track.is-dragging .wui-switch-thumb,.wui-switch-track.is-dragging .wui-switch-thumb.is-pressed{transform:translateX(var(--wui-switch-drag-offset,0)) scale(1.5);transition:none}.wui-switch-loading{pointer-events:none;justify-content:center;align-items:center;display:flex;position:absolute;inset:0}";
2
+ var e = "*,:before,:after{box-sizing:border-box}:host{display:inline-flex}:host(:focus-visible){outline:none}:host([disabled]){cursor:not-allowed}.wui-switch-content{align-items:center;gap:8px;display:inline-flex}.wui-switch-content.is-label-empty{gap:0}.wui-switch-label.is-disabled{opacity:.4}.wui-switch-track{touch-action:none;cursor:default;-webkit-user-select:none;user-select:none;border-radius:var(--wui-radius-control,calc(infinity * 1px));background-color:var(--wui-color-surface-track,#e5e5ea);outline:var(--wui-focus-ring-width,3px) solid transparent;outline-offset:2px;width:40px;height:20px;transition:background-color var(--wui-duration-trigger,.16s) ease;justify-content:space-between;align-items:center;display:flex;position:relative}.wui-switch-track.is-open{background-color:var(--wui-color-accent,#0071e3)}.wui-switch-track.is-disabled{cursor:not-allowed;opacity:.4}.wui-switch-thumb{--wui-glass-corner-radius:calc(16px / 2);transform-origin:50%;border-radius:var(--wui-radius-control,calc(infinity * 1px));width:16px;height:16px;transition:width var(--wui-duration-press,80ms) cubic-bezier(.4, 0, .2, 1), transform var(--wui-duration-trigger,.16s) cubic-bezier(.4, 0, .2, 1), border-radius var(--wui-duration-press,80ms) cubic-bezier(.4, 0, .2, 1), background-color var(--wui-duration-press,80ms) ease, box-shadow var(--wui-duration-press,80ms) ease;position:absolute;top:2px;left:2px;overflow:hidden;transform:translate(0)scale(1)}.wui-switch-thumb.is-pressed{--wui-color-glass-ring:transparent;border-radius:var(--wui-radius-control,calc(infinity * 1px));width:20px;box-shadow:inset 0 0 0 .5px color-mix(in srgb, var(--wui-color-glass-highlight,#ffffffe6) 38%, transparent), inset 0 1px .5px color-mix(in srgb, var(--wui-color-glass-highlight,#ffffffe6) 52%, transparent), inset 0 -.5px 1px #00000008, 0 2px 20px #0003;background-color:#0000;transform:translate(0)scale(1.5)}.wui-switch-thumb:not(.is-pressed){background-color:var(--wui-color-on-control,#fff);box-shadow:1px 2px 6px #0003}.wui-switch-track.is-open .wui-switch-thumb{transform:translate(20px)scale(1)}.wui-switch-track.is-open .wui-switch-thumb.is-pressed{transform:translate(16px)scale(1.5)}.wui-switch-track.is-dragging{cursor:grabbing}:host(:focus-visible) .wui-switch-track{outline-color:var(--wui-color-focus-ring,color-mix(in srgb, var(--wui-color-accent,#0071e3) 40%, transparent))}:host([data-wui-pointer-focus]:focus-visible) .wui-switch-track{outline-color:#0000}.wui-switch-track.is-dragging .wui-switch-thumb,.wui-switch-track.is-dragging .wui-switch-thumb.is-pressed{transform:translateX(var(--wui-switch-drag-offset,0)) scale(1.5);transition:none}.wui-switch-loading{pointer-events:none;justify-content:center;align-items:center;display:flex;position:absolute;inset:0}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/switch/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n touch-action: none;\n display: inline-block;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n}\n\n/*\n标准视觉隐藏技术(sr-only):\n业内标准的“屏幕阅读器专用”隐藏方案。\n把 checkbox 缩放到 1x1 像素,但保留在 dom 中,\n既让肉眼看不见,又能让读屏软件识别,并允许键盘用户通过空格键切换状态。\n*/\n.sr-only {\n position: absolute;\n\n overflow: hidden;\n\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n\n white-space: nowrap;\n\n clip-path: inset(50%);\n}\n\n.wui-switch-track {\n touch-action: none;\n cursor: default;\n user-select: none;\n\n position: relative;\n\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n width: 40px;\n height: 20px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n\n background-color: var(--wui-color-surface-track, #e5e5ea);\n outline: var(--wui-focus-ring-width, 3px) solid transparent;\n outline-offset: 2px;\n\n transition: background-color var(--wui-duration-trigger, 160ms) ease;\n\n /* 实际获得焦点的是内部 sr-only 的 checkbox,track 本身不可聚焦;\n :focus-visible 在 track 上永不匹配,需用 :focus-within 让键盘焦点环生效 */\n &:focus-within {\n outline-color: var(--wui-color-focus-ring, color-mix(in srgb, var(--wui-color-accent, #08f) 40%, transparent));\n }\n\n &.is-open {\n background-color: var(--wui-color-accent, #08f);\n }\n\n &.is-disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n}\n\n.wui-switch-thumb {\n /* 按压态 thumb 是 16px 高的 pill(is-pressed 只加宽到 20px),取短边一半 */\n --wui-glass-corner-radius: calc(16px / 2);\n\n position: absolute;\n top: 2px;\n left: 2px;\n transform-origin: center center;\n transform: translateX(0) scale(1);\n\n overflow: hidden;\n\n width: 16px;\n height: 16px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n\n transition:\n width var(--wui-duration-press, 80ms) cubic-bezier(0.4, 0, 0.2, 1),\n transform var(--wui-duration-trigger, 160ms) cubic-bezier(0.4, 0, 0.2, 1),\n border-radius var(--wui-duration-press, 80ms) cubic-bezier(0.4, 0, 0.2, 1),\n background-color var(--wui-duration-press, 80ms) ease,\n box-shadow var(--wui-duration-press, 80ms) ease;\n\n &.is-pressed {\n /* 环底色随按压态转 transparent:pill 的长边长不过 3R,底色不覆盖中段,\n 去掉它只是少一层均匀灰 tint(理由见 menu-portal.css)。 */\n --wui-color-glass-ring: transparent;\n transform: translateX(0) scale(1.5);\n\n width: 20px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n\n background-color: transparent;\n box-shadow:\n inset 0 0 0 0.5px color-mix(in srgb, var(--wui-color-glass-highlight, rgb(255 255 255 / 0.9)) 38%, transparent),\n inset 0 1px 0.5px color-mix(in srgb, var(--wui-color-glass-highlight, rgb(255 255 255 / 0.9)) 52%, transparent),\n inset 0 -0.5px 1px rgb(0 0 0 / 0.03),\n 0 2px 20px rgb(0 0 0 / 0.2);\n }\n\n &:not(.is-pressed) {\n background-color: var(--wui-color-on-control, #fff);\n box-shadow: 1px 2px 6px rgb(0 0 0 / 0.2);\n }\n}\n\n.wui-switch-track.is-open .wui-switch-thumb {\n transform: translateX(20px) scale(1);\n\n &.is-pressed {\n transform: translateX(16px) scale(1.5);\n }\n}\n\n.wui-switch-track.is-dragging {\n cursor: grabbing;\n}\n\n:host([data-wui-pointer-focus]) .wui-switch-track:focus-within {\n outline-color: transparent;\n}\n\n.wui-switch-track.is-dragging .wui-switch-thumb,\n.wui-switch-track.is-dragging .wui-switch-thumb.is-pressed {\n transform: translateX(var(--wui-switch-drag-offset, 0)) scale(1.5);\n transition: none;\n}\n\n.wui-switch-loading {\n pointer-events: none;\n\n position: absolute;\n inset: 0;\n\n display: flex;\n align-items: center;\n justify-content: center;\n}\n"],"mappings":""}
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/switch/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(:focus-visible) {\n outline: none;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n}\n\n.wui-switch-content {\n display: inline-flex;\n gap: 8px;\n align-items: center;\n}\n\n.wui-switch-content.is-label-empty {\n gap: 0;\n}\n\n.wui-switch-label.is-disabled {\n opacity: 0.4;\n}\n\n.wui-switch-track {\n touch-action: none;\n cursor: default;\n user-select: none;\n\n position: relative;\n\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n width: 40px;\n height: 20px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n\n background-color: var(--wui-color-surface-track, #e5e5ea);\n outline: var(--wui-focus-ring-width, 3px) solid transparent;\n outline-offset: 2px;\n\n transition: background-color var(--wui-duration-trigger, 160ms) ease;\n\n &.is-open {\n background-color: var(--wui-color-accent, #0071e3);\n }\n\n &.is-disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n}\n\n.wui-switch-thumb {\n /* 按压态 thumb 是 16px 高的 pill(is-pressed 只加宽到 20px),取短边一半 */\n --wui-glass-corner-radius: calc(16px / 2);\n\n position: absolute;\n top: 2px;\n left: 2px;\n transform-origin: center center;\n transform: translateX(0) scale(1);\n\n overflow: hidden;\n\n width: 16px;\n height: 16px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n\n transition:\n width var(--wui-duration-press, 80ms) cubic-bezier(0.4, 0, 0.2, 1),\n transform var(--wui-duration-trigger, 160ms) cubic-bezier(0.4, 0, 0.2, 1),\n border-radius var(--wui-duration-press, 80ms) cubic-bezier(0.4, 0, 0.2, 1),\n background-color var(--wui-duration-press, 80ms) ease,\n box-shadow var(--wui-duration-press, 80ms) ease;\n\n &.is-pressed {\n /* 环底色随按压态转 transparent:pill 的长边长不过 3R,底色不覆盖中段,\n 去掉它只是少一层均匀灰 tint(理由见 menu-portal.css)。 */\n --wui-color-glass-ring: transparent;\n transform: translateX(0) scale(1.5);\n\n width: 20px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n\n background-color: transparent;\n box-shadow:\n inset 0 0 0 0.5px color-mix(in srgb, var(--wui-color-glass-highlight, rgb(255 255 255 / 0.9)) 38%, transparent),\n inset 0 1px 0.5px color-mix(in srgb, var(--wui-color-glass-highlight, rgb(255 255 255 / 0.9)) 52%, transparent),\n inset 0 -0.5px 1px rgb(0 0 0 / 0.03),\n 0 2px 20px rgb(0 0 0 / 0.2);\n }\n\n &:not(.is-pressed) {\n background-color: var(--wui-color-on-control, #fff);\n box-shadow: 1px 2px 6px rgb(0 0 0 / 0.2);\n }\n}\n\n.wui-switch-track.is-open .wui-switch-thumb {\n transform: translateX(20px) scale(1);\n\n &.is-pressed {\n transform: translateX(16px) scale(1.5);\n }\n}\n\n.wui-switch-track.is-dragging {\n cursor: grabbing;\n}\n\n:host(:focus-visible) .wui-switch-track {\n outline-color: var(--wui-color-focus-ring, color-mix(in srgb, var(--wui-color-accent, #0071e3) 40%, transparent));\n}\n\n:host([data-wui-pointer-focus]:focus-visible) .wui-switch-track {\n outline-color: transparent;\n}\n\n.wui-switch-track.is-dragging .wui-switch-thumb,\n.wui-switch-track.is-dragging .wui-switch-thumb.is-pressed {\n transform: translateX(var(--wui-switch-drag-offset, 0)) scale(1.5);\n transition: none;\n}\n\n.wui-switch-loading {\n pointer-events: none;\n\n position: absolute;\n inset: 0;\n\n display: flex;\n align-items: center;\n justify-content: center;\n}\n"],"mappings":""}
@@ -1,6 +1,6 @@
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/jam-close-circle-f.js";
5
5
  import { FormAssociated as r, FormAssociationController as i, defineFormAssociation as a, forwardInputValidity as o } from "../../shared/form-association/index.js";
6
6
  import "../button/index.js";
@@ -122,7 +122,7 @@ var y = class extends r(l) {
122
122
  e.preventDefault();
123
123
  }
124
124
  render() {
125
- let e = this.clearable && this._value && !this.readonly;
125
+ let e = this.clearable && this._value && !this._isDisabled && !this.readonly;
126
126
  return u`
127
127
  <div class="wui-glass wui-textarea-inner" @click=${this.focusTextarea}>
128
128
  <slot name="prefix" class=${g({ empty: !this._hasPrefix })} @slotchange=${this._onSlotChange}></slot>
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/textarea/index.ts"],"sourcesContent":["import '@/components/button'\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 { ifDefined } from 'lit/directives/if-defined.js'\nimport { styleMap } from 'lit/directives/style-map.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport { jamCloseCircleF } from '@/icons'\nimport {\n FormAssociated,\n defineFormAssociation,\n forwardInputValidity,\n FormAssociationController\n} from '@/shared/form-association'\nimport { createFieldId } from '@/shared/form-association/field-id'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-textarea')\nexport class WebUiTextarea extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: String, reflect: true }) placeholder = ''\n @property({ type: String, reflect: true }) name = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) readonly = false\n @property({ type: Boolean, reflect: true }) required = false\n @property({ type: Boolean, reflect: true }) full = false\n @property({ type: Boolean, reflect: true }) clearable = false\n @property({ type: Boolean, reflect: true }) borderless = false\n @property({ type: Number, reflect: true }) rows = 3\n @property({ type: Number, reflect: true }) minlength: number | undefined\n @property({ type: Number, reflect: true }) maxlength: number | undefined\n @property({ type: Boolean, reflect: true }) autosize = false\n // 自动高度上限(px);0 表示不限制,随内容无限增高\n @property({ type: Number, reflect: true, attribute: 'max-height' }) maxHeight = 0\n @property({ type: String, attribute: 'aria-label' }) override ariaLabel: string | null = null\n @property({ type: String, attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined\n\n @state() private _value = ''\n @state() private _focused = false\n @state() private _hasPrefix = false\n @state() private _hasSuffix = false\n\n private _textarea: HTMLTextAreaElement | null = null\n private _resizeObserver: ResizeObserver | null = null\n private readonly _fieldId = createFieldId('web-ui-textarea')\n\n @property({ type: String, reflect: true })\n get value(): string {\n return this._value\n }\n\n set value(v: string) {\n const old = this._value\n this._value = v\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n private readonly _formAssociation = defineFormAssociation<string>({\n host: this,\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => this._value,\n getFormState: () => this._value,\n restoreState: state => {\n if (typeof state === 'string') this.value = state\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._teardownAutosize()\n }\n\n override firstUpdated() {\n this._textarea = this.shadowRoot?.querySelector('textarea') ?? null\n }\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('_value')) {\n const textarea = this._getTextarea()\n if (textarea && textarea.value !== this._value) {\n textarea.value = this._value\n }\n }\n /*\n * 高度只在这里算,autosize 的两条改值路径(用户输入、外部改写)都汇到\n * changed.has('_value'),不必各自调一次。\n *\n * 不能等 ResizeObserver:autosize 把高度写成内联 px,换值时盒子尺寸没变,\n * 不会触发回调,于是旧高度会一直留着(例如同一个 textarea 随 drawer 切换资源,\n * 从长备注切到短备注仍占着长备注的高度)。\n *\n * rows 同理:它决定 height:auto 时的自然高度,但 inline px 会盖掉它,\n * 改 rows 不会让盒子尺寸变化,ResizeObserver 同样看不到。\n *\n * 上面的 textarea.value 兜底在正常路径上是 no-op——render() 里的 .value 绑定\n * 已经先一步把新值写进 DOM 了;它留给 binding 未生效的场景。\n */\n if (this.autosize && (changed.has('_value') || changed.has('rows'))) this._autosize()\n if (changed.has('autosize')) {\n if (this.autosize) {\n this._setupAutosize()\n } else {\n this._teardownAutosize(true)\n }\n }\n this._syncValidity()\n this.toggleAttribute('focused', this._focused)\n }\n\n private _getTextarea(): HTMLTextAreaElement | null {\n this._textarea ??= this.shadowRoot?.querySelector('textarea') ?? null\n return this._textarea\n }\n\n private _syncValidity() {\n const internals = this._formAssociation.getInternals()\n const textarea = this._getTextarea()\n if (!internals || !textarea || typeof internals.setValidity !== 'function') return\n\n forwardInputValidity(internals, textarea, ['valueMissing', 'tooShort', 'tooLong'], this._isDisabled)\n }\n\n private _setupAutosize() {\n this._teardownAutosize()\n if (typeof ResizeObserver === 'undefined') {\n this._autosize()\n return\n }\n this._resizeObserver = new ResizeObserver(() => {\n this._autosize()\n })\n if (this._textarea) this._resizeObserver.observe(this._textarea)\n this._autosize()\n }\n\n private _teardownAutosize(resetHeight = false) {\n this._resizeObserver?.disconnect()\n this._resizeObserver = null\n if (resetHeight) this._textarea?.style.removeProperty('height')\n }\n\n private _autosize() {\n if (!this._textarea || !this.autosize) return\n this._textarea.style.height = 'auto'\n // height:auto 时 rows 已驱动自然高,scrollHeight 恒 ≥ rows 行高,无需额外 floor;\n // 上限由 CSS max-height(maxHeight 属性)钳制,这里只负责随内容增高,默认无限\n this._textarea.style.height = `${this._textarea.scrollHeight}px`\n }\n\n private handleInput(e: Event) {\n if (this._isDisabled || this.readonly) return\n if (!(e.target instanceof HTMLTextAreaElement)) return\n this._value = e.target.value\n this._formAssociation.sync()\n this._syncValidity()\n }\n\n private handleFocus() {\n if (!this._isDisabled) this._focused = true\n }\n\n private handleBlur() {\n this._focused = false\n }\n\n // 原生 change 不 composed,被 shadow root 挡住;这里补发 composed 事件,\n // 兑现 $events/README 声明的公共 change 契约。\n private handleNativeChange(e: Event) {\n if (this._isDisabled || this.readonly) return\n if (!(e.target instanceof HTMLTextAreaElement)) return\n this._value = e.target.value\n this._formAssociation.sync()\n this._syncValidity()\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n override focus(options?: FocusOptions) {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('textarea')?.focus(options)\n }\n\n override blur() {\n this.shadowRoot?.querySelector('textarea')?.blur()\n }\n\n /**\n * 公共 API:全选当前值。\n *\n * `disabled` 时与 `focus()` 一致不产生效果;原生控件未渲染时安全 no-op。\n */\n select() {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('textarea')?.select()\n }\n\n private focusTextarea() {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('textarea')?.focus()\n }\n\n private _onSlotChange(e: Event) {\n if (!(e.target instanceof HTMLSlotElement)) return\n const slot = e.target\n const hasContent = slot.assignedElements().length > 0\n if (slot.name === 'prefix') this._hasPrefix = hasContent\n if (slot.name === 'suffix') this._hasSuffix = hasContent\n }\n\n private handleClear() {\n if (this._isDisabled || this.readonly) return\n this._value = ''\n this._formAssociation.sync()\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n }\n\n private preventMouseDownBlur(e: Event) {\n e.preventDefault()\n }\n\n override render() {\n // readonly 下不可清除:清空 button 会修改值,与只读语义冲突\n const showClear = this.clearable && this._value && !this.readonly\n\n return html`\n <div class=\"wui-glass wui-textarea-inner\" @click=${this.focusTextarea}>\n <slot name=\"prefix\" class=${classMap({ empty: !this._hasPrefix })} @slotchange=${this._onSlotChange}></slot>\n <textarea\n id=${this._fieldId}\n placeholder=${this.placeholder}\n name=${this.name}\n aria-label=${ifDefined(this.ariaLabel)}\n aria-labelledby=${ifDefined(this.ariaLabelledby)}\n .rows=${this.rows}\n minlength=${ifDefined(this.minlength)}\n maxlength=${ifDefined(this.maxlength)}\n ?disabled=${this._isDisabled}\n ?readonly=${this.readonly}\n ?required=${this.required}\n .value=${this._value}\n style=${this.maxHeight > 0 ? styleMap({ 'max-height': `${this.maxHeight}px` }) : nothing}\n @input=${this.handleInput}\n @change=${this.handleNativeChange}\n @focus=${this.handleFocus}\n @blur=${this.handleBlur}\n ></textarea>\n ${\n showClear\n ? html`<web-ui-button\n icon\n variant=\"ghost\"\n size=\"20\"\n aria-label=\"Clear\"\n @pointerdown=${this.preventMouseDownBlur}\n @click=${this.handleClear}\n >\n <web-ui-icon .icon=${jamCloseCircleF} size=\"14\"></web-ui-icon>\n </web-ui-button>`\n : ''\n }\n <slot name=\"suffix\" class=${classMap({ empty: !this._hasSuffix })} @slotchange=${this._onSlotChange}></slot>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n focus: FocusEvent\n blur: FocusEvent\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-textarea': WebUiTextarea\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/textarea/index.ts"],"sourcesContent":["import '@/components/button'\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 { ifDefined } from 'lit/directives/if-defined.js'\nimport { styleMap } from 'lit/directives/style-map.js'\n\nimport glass from '@/classes/glass.css?inline'\nimport { jamCloseCircleF } from '@/icons'\nimport {\n FormAssociated,\n defineFormAssociation,\n forwardInputValidity,\n FormAssociationController\n} from '@/shared/form-association'\nimport { createFieldId } from '@/shared/form-association/field-id'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-textarea')\nexport class WebUiTextarea extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: String, reflect: true }) placeholder = ''\n @property({ type: String, reflect: true }) name = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) readonly = false\n @property({ type: Boolean, reflect: true }) required = false\n @property({ type: Boolean, reflect: true }) full = false\n @property({ type: Boolean, reflect: true }) clearable = false\n @property({ type: Boolean, reflect: true }) borderless = false\n @property({ type: Number, reflect: true }) rows = 3\n @property({ type: Number, reflect: true }) minlength: number | undefined\n @property({ type: Number, reflect: true }) maxlength: number | undefined\n @property({ type: Boolean, reflect: true }) autosize = false\n // 自动高度上限(px);0 表示不限制,随内容无限增高\n @property({ type: Number, reflect: true, attribute: 'max-height' }) maxHeight = 0\n @property({ type: String, attribute: 'aria-label' }) override ariaLabel: string | null = null\n @property({ type: String, attribute: 'aria-labelledby' }) ariaLabelledby: string | undefined\n\n @state() private _value = ''\n @state() private _focused = false\n @state() private _hasPrefix = false\n @state() private _hasSuffix = false\n\n private _textarea: HTMLTextAreaElement | null = null\n private _resizeObserver: ResizeObserver | null = null\n private readonly _fieldId = createFieldId('web-ui-textarea')\n\n @property({ type: String, reflect: true })\n get value(): string {\n return this._value\n }\n\n set value(v: string) {\n const old = this._value\n this._value = v\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n private readonly _formAssociation = defineFormAssociation<string>({\n host: this,\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => this._value,\n getFormState: () => this._value,\n restoreState: state => {\n if (typeof state === 'string') this.value = state\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._teardownAutosize()\n }\n\n override firstUpdated() {\n this._textarea = this.shadowRoot?.querySelector('textarea') ?? null\n }\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('_value')) {\n const textarea = this._getTextarea()\n if (textarea && textarea.value !== this._value) {\n textarea.value = this._value\n }\n }\n /*\n * 高度只在这里算,autosize 的两条改值路径(用户输入、外部改写)都汇到\n * changed.has('_value'),不必各自调一次。\n *\n * 不能等 ResizeObserver:autosize 把高度写成内联 px,换值时盒子尺寸没变,\n * 不会触发回调,于是旧高度会一直留着(例如同一个 textarea 随 drawer 切换资源,\n * 从长备注切到短备注仍占着长备注的高度)。\n *\n * rows 同理:它决定 height:auto 时的自然高度,但 inline px 会盖掉它,\n * 改 rows 不会让盒子尺寸变化,ResizeObserver 同样看不到。\n *\n * 上面的 textarea.value 兜底在正常路径上是 no-op——render() 里的 .value 绑定\n * 已经先一步把新值写进 DOM 了;它留给 binding 未生效的场景。\n */\n if (this.autosize && (changed.has('_value') || changed.has('rows'))) this._autosize()\n if (changed.has('autosize')) {\n if (this.autosize) {\n this._setupAutosize()\n } else {\n this._teardownAutosize(true)\n }\n }\n this._syncValidity()\n this.toggleAttribute('focused', this._focused)\n }\n\n private _getTextarea(): HTMLTextAreaElement | null {\n this._textarea ??= this.shadowRoot?.querySelector('textarea') ?? null\n return this._textarea\n }\n\n private _syncValidity() {\n const internals = this._formAssociation.getInternals()\n const textarea = this._getTextarea()\n if (!internals || !textarea || typeof internals.setValidity !== 'function') return\n\n forwardInputValidity(internals, textarea, ['valueMissing', 'tooShort', 'tooLong'], this._isDisabled)\n }\n\n private _setupAutosize() {\n this._teardownAutosize()\n if (typeof ResizeObserver === 'undefined') {\n this._autosize()\n return\n }\n this._resizeObserver = new ResizeObserver(() => {\n this._autosize()\n })\n if (this._textarea) this._resizeObserver.observe(this._textarea)\n this._autosize()\n }\n\n private _teardownAutosize(resetHeight = false) {\n this._resizeObserver?.disconnect()\n this._resizeObserver = null\n if (resetHeight) this._textarea?.style.removeProperty('height')\n }\n\n private _autosize() {\n if (!this._textarea || !this.autosize) return\n this._textarea.style.height = 'auto'\n // height:auto 时 rows 已驱动自然高,scrollHeight 恒 ≥ rows 行高,无需额外 floor;\n // 上限由 CSS max-height(maxHeight 属性)钳制,这里只负责随内容增高,默认无限\n this._textarea.style.height = `${this._textarea.scrollHeight}px`\n }\n\n private handleInput(e: Event) {\n if (this._isDisabled || this.readonly) return\n if (!(e.target instanceof HTMLTextAreaElement)) return\n this._value = e.target.value\n this._formAssociation.sync()\n this._syncValidity()\n }\n\n private handleFocus() {\n if (!this._isDisabled) this._focused = true\n }\n\n private handleBlur() {\n this._focused = false\n }\n\n // 原生 change 不 composed,被 shadow root 挡住;这里补发 composed 事件,\n // 兑现 $events/README 声明的公共 change 契约。\n private handleNativeChange(e: Event) {\n if (this._isDisabled || this.readonly) return\n if (!(e.target instanceof HTMLTextAreaElement)) return\n this._value = e.target.value\n this._formAssociation.sync()\n this._syncValidity()\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n override focus(options?: FocusOptions) {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('textarea')?.focus(options)\n }\n\n override blur() {\n this.shadowRoot?.querySelector('textarea')?.blur()\n }\n\n /**\n * 公共 API:全选当前值。\n *\n * `disabled` 时与 `focus()` 一致不产生效果;原生控件未渲染时安全 no-op。\n */\n select() {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('textarea')?.select()\n }\n\n private focusTextarea() {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('textarea')?.focus()\n }\n\n private _onSlotChange(e: Event) {\n if (!(e.target instanceof HTMLSlotElement)) return\n const slot = e.target\n const hasContent = slot.assignedElements().length > 0\n if (slot.name === 'prefix') this._hasPrefix = hasContent\n if (slot.name === 'suffix') this._hasSuffix = hasContent\n }\n\n private handleClear() {\n if (this._isDisabled || this.readonly) return\n this._value = ''\n this._formAssociation.sync()\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n }\n\n private preventMouseDownBlur(e: Event) {\n e.preventDefault()\n }\n\n override render() {\n // disabled / readonly 时隐藏清除 button,避免暴露不可执行的值修改操作\n const showClear = this.clearable && this._value && !this._isDisabled && !this.readonly\n\n return html`\n <div class=\"wui-glass wui-textarea-inner\" @click=${this.focusTextarea}>\n <slot name=\"prefix\" class=${classMap({ empty: !this._hasPrefix })} @slotchange=${this._onSlotChange}></slot>\n <textarea\n id=${this._fieldId}\n placeholder=${this.placeholder}\n name=${this.name}\n aria-label=${ifDefined(this.ariaLabel)}\n aria-labelledby=${ifDefined(this.ariaLabelledby)}\n .rows=${this.rows}\n minlength=${ifDefined(this.minlength)}\n maxlength=${ifDefined(this.maxlength)}\n ?disabled=${this._isDisabled}\n ?readonly=${this.readonly}\n ?required=${this.required}\n .value=${this._value}\n style=${this.maxHeight > 0 ? styleMap({ 'max-height': `${this.maxHeight}px` }) : nothing}\n @input=${this.handleInput}\n @change=${this.handleNativeChange}\n @focus=${this.handleFocus}\n @blur=${this.handleBlur}\n ></textarea>\n ${\n showClear\n ? html`<web-ui-button\n icon\n variant=\"ghost\"\n size=\"20\"\n aria-label=\"Clear\"\n @pointerdown=${this.preventMouseDownBlur}\n @click=${this.handleClear}\n >\n <web-ui-icon .icon=${jamCloseCircleF} size=\"14\"></web-ui-icon>\n </web-ui-button>`\n : ''\n }\n <slot name=\"suffix\" class=${classMap({ empty: !this._hasSuffix })} @slotchange=${this._onSlotChange}></slot>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n focus: FocusEvent\n blur: FocusEvent\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-textarea': WebUiTextarea\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  //#region src/components/textarea/style.css?inline
2
- var e = "*,:before,:after{box-sizing:border-box}:host{--wui-textarea-clear-color:var(--wui-color-text-tertiary,color-mix(in srgb, var(--wui-color-text) 35%, transparent));display:block}:host([disabled]){cursor:not-allowed;opacity:.4}.wui-textarea-inner{--wui-glass-corner-radius:var(--wui-radius-menu,18px);gap:var(--wui-space-2,8px);box-sizing:border-box;width:var(--wui-textarea-width,200px);min-width:var(--wui-control-size,36px);min-height:var(--wui-control-size,36px);padding:7.5px var(--wui-space-3,12px);border-radius:var(--wui-radius-menu,18px);align-items:flex-start;display:flex;position:relative}:host([full]) .wui-textarea-inner{width:100%}:host([borderless]) .wui-textarea-inner{-webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:none;background:0 0;border:none}:host([borderless]) .wui-textarea-inner:before{content:none}textarea{resize:vertical;box-sizing:border-box;width:100%;font:inherit;font-size:var(--wui-font-size,14px);line-height:var(--wui-line-height-normal,1.5);color:var(--wui-color-text,#1b1b1b);background:0 0;border:none;outline:none;flex:1;margin:0;padding:0;display:block}textarea::placeholder{font:inherit;color:var(--wui-color-text-secondary,#6a6a6a)}textarea:disabled{cursor:not-allowed}textarea:read-only{cursor:default}:host([autosize]) textarea{resize:none}@media (pointer:coarse){textarea{font-size:max(16px, var(--wui-font-size,14px))}}.wui-textarea-inner:after{pointer-events:none;content:\"\";border-radius:inherit;box-shadow:inset 0 0 0 1px transparent, 0 0 0 var(--wui-focus-ring-width,3px) transparent;transition:box-shadow var(--wui-duration-focus,.2s);position:absolute;inset:0}:host([focused]) .wui-textarea-inner:after{box-shadow:inset 0 0 0 1px var(--wui-color-accent,#08f), 0 0 0 var(--wui-focus-ring-width,3px) var(--wui-color-focus-ring,color-mix(in srgb, var(--wui-color-accent,#08f) 40%, transparent))}slot{font-size:var(--wui-font-size,14px);color:var(--wui-color-text-secondary,#6a6a6a);flex-shrink:0;align-items:center;display:flex}slot.empty{display:none}.wui-textarea-inner web-ui-button[variant=ghost]{--wui-button-color:var(--wui-textarea-clear-color);margin-top:calc((var(--wui-font-size,14px) * 1.5 - 24px) / 2);margin-left:-8px;margin-right:-4px}";
2
+ var e = "*,:before,:after{box-sizing:border-box}:host{--wui-textarea-clear-color:var(--wui-color-text-tertiary,color-mix(in srgb, var(--wui-color-text) 35%, transparent));display:block}:host([disabled]){cursor:not-allowed;opacity:.4}.wui-textarea-inner{--wui-glass-corner-radius:var(--wui-radius-menu,18px);gap:var(--wui-space-2,8px);box-sizing:border-box;width:var(--wui-textarea-width,200px);min-width:var(--wui-control-size,36px);min-height:var(--wui-control-size,36px);padding:7.5px var(--wui-space-3,12px);border-radius:var(--wui-radius-menu,18px);align-items:flex-start;display:flex;position:relative}:host([full]) .wui-textarea-inner{width:100%}:host([borderless]) .wui-textarea-inner{-webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:none;background:0 0;border:none}:host([borderless]) .wui-textarea-inner:before{content:none}textarea{resize:vertical;box-sizing:border-box;width:100%;font:inherit;font-size:var(--wui-font-size,14px);line-height:var(--wui-line-height-normal,1.5);color:var(--wui-color-text,#1b1b1b);background:0 0;border:none;outline:none;flex:1;margin:0;padding:0;display:block}textarea::placeholder{font:inherit;color:var(--wui-color-text-secondary,#6a6a6a)}textarea:disabled{cursor:not-allowed}textarea:read-only{cursor:default}:host([autosize]) textarea{resize:none}@media (pointer:coarse){textarea{font-size:max(16px, var(--wui-font-size,14px))}}.wui-textarea-inner:after{pointer-events:none;content:\"\";border-radius:inherit;box-shadow:inset 0 0 0 1px transparent, 0 0 0 var(--wui-focus-ring-width,3px) transparent;transition:box-shadow var(--wui-duration-focus,.2s);position:absolute;inset:0}:host([focused]) .wui-textarea-inner:after{box-shadow:inset 0 0 0 1px var(--wui-color-accent,#0071e3), 0 0 0 var(--wui-focus-ring-width,3px) var(--wui-color-focus-ring,color-mix(in srgb, var(--wui-color-accent,#0071e3) 40%, transparent))}slot{font-size:var(--wui-font-size,14px);color:var(--wui-color-text-secondary,#6a6a6a);flex-shrink:0;align-items:center;display:flex}slot.empty{display:none}.wui-textarea-inner web-ui-button[variant=ghost]{--wui-button-color:var(--wui-textarea-clear-color);margin-top:calc((var(--wui-font-size,14px) * 1.5 - 24px) / 2);margin-left:-8px;margin-right:-4px}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/textarea/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n --wui-textarea-clear-color: var(\n --wui-color-text-tertiary,\n color-mix(in srgb, var(--wui-color-text) 35%, transparent)\n );\n display: block;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.wui-textarea-inner {\n --wui-glass-corner-radius: var(--wui-radius-menu, 18px);\n position: relative;\n\n display: flex;\n gap: var(--wui-space-2, 8px);\n align-items: flex-start;\n\n box-sizing: border-box;\n width: var(--wui-textarea-width, 200px);\n min-width: var(--wui-control-size, 36px);\n min-height: var(--wui-control-size, 36px);\n padding: 7.5px var(--wui-space-3, 12px);\n border-radius: var(--wui-radius-menu, 18px);\n}\n\n:host([full]) .wui-textarea-inner {\n width: 100%;\n}\n\n:host([borderless]) .wui-textarea-inner {\n border: none;\n background: transparent;\n backdrop-filter: none;\n box-shadow: none;\n}\n\n/* borderless 必须移除 glass 描边环(.wui-glass::before 底环 + 对角光泽),\n 否则无框后仍残留 1px 描边 */\n:host([borderless]) .wui-textarea-inner::before {\n content: none;\n}\n\ntextarea {\n resize: vertical;\n\n display: block;\n flex: 1;\n\n box-sizing: border-box;\n width: 100%;\n margin: 0;\n padding: 0;\n border: none;\n\n /* 表单控件不继承字体;显式继承以对齐宿主排版,避免 UA 默认字体差异 */\n font: inherit;\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 background: transparent;\n outline: none;\n\n &::placeholder {\n /* 同 input:占位文本的排版属于控件,不在这里另写一份 */\n font: inherit;\n color: var(--wui-color-text-secondary, #6a6a6a);\n }\n\n &:disabled {\n cursor: not-allowed;\n }\n\n &:read-only {\n cursor: default;\n }\n}\n\n/*\n * autosize 由组件用 JS 独占高度(_autosize 写内联 height),原生 resize 拖拽与它是\n * 两套高度来源:用户拖高后,下一次内容变化又会被 autosize 拉回去,拖拽变成假动作。\n * 所以 autosize 下关掉 resize grip,需要上限高度请用 max-height(CSS 钳制)。\n */\n:host([autosize]) textarea {\n resize: none;\n}\n\n/* Coarse pointers include iOS Safari, where a sub-16px font triggers focus zoom. */\n@media (pointer: coarse) {\n textarea {\n font-size: max(16px, var(--wui-font-size, 14px));\n }\n}\n\n/* Focus ring 独立成 ::after 层:base/focused 都是显式 box-shadow,保证所有变体都能过渡 */\n.wui-textarea-inner::after {\n pointer-events: none;\n content: '';\n\n position: absolute;\n inset: 0;\n\n border-radius: inherit;\n\n box-shadow:\n inset 0 0 0 1px transparent,\n 0 0 0 var(--wui-focus-ring-width, 3px) transparent;\n\n transition: box-shadow var(--wui-duration-focus, 200ms);\n}\n\n:host([focused]) .wui-textarea-inner::after {\n box-shadow:\n inset 0 0 0 1px var(--wui-color-accent, #08f),\n 0 0 0 var(--wui-focus-ring-width, 3px)\n var(--wui-color-focus-ring, color-mix(in srgb, var(--wui-color-accent, #08f) 40%, transparent));\n}\n\nslot {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n\n font-size: var(--wui-font-size, 14px);\n color: var(--wui-color-text-secondary, #6a6a6a);\n}\n\nslot.empty {\n display: none;\n}\n\n/* 复用 web-ui-button icon ghost:仅覆盖颜色为 faint 并对齐首行,保持弱意图清除 icon 观感;\n 负 margin-left 抵消容器 flex gap,让清除 button 紧贴 textarea 内容\n 负 margin-right 抵消 button padding,让清除 button 右侧 spacing 视觉上协调 */\n.wui-textarea-inner web-ui-button[variant='ghost'] {\n --wui-button-color: var(--wui-textarea-clear-color);\n\n /* 与首行文字垂直居中对齐:容器 align-items: flex-start 使 button 贴内容区顶部,\n 首行中心 = padding-top + 行高/2;24px button 需相对内容区上移 (行高 − button 高度) / 2 */\n margin-top: calc((var(--wui-font-size, 14px) * 1.5 - 24px) / 2);\n margin-right: -4px;\n margin-left: -8px;\n}\n"],"mappings":""}
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/textarea/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n --wui-textarea-clear-color: var(\n --wui-color-text-tertiary,\n color-mix(in srgb, var(--wui-color-text) 35%, transparent)\n );\n display: block;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.wui-textarea-inner {\n --wui-glass-corner-radius: var(--wui-radius-menu, 18px);\n position: relative;\n\n display: flex;\n gap: var(--wui-space-2, 8px);\n align-items: flex-start;\n\n box-sizing: border-box;\n width: var(--wui-textarea-width, 200px);\n min-width: var(--wui-control-size, 36px);\n min-height: var(--wui-control-size, 36px);\n padding: 7.5px var(--wui-space-3, 12px);\n border-radius: var(--wui-radius-menu, 18px);\n}\n\n:host([full]) .wui-textarea-inner {\n width: 100%;\n}\n\n:host([borderless]) .wui-textarea-inner {\n border: none;\n background: transparent;\n backdrop-filter: none;\n box-shadow: none;\n}\n\n/* borderless 必须移除 glass 描边环(.wui-glass::before 底环 + 对角光泽),\n 否则无框后仍残留 1px 描边 */\n:host([borderless]) .wui-textarea-inner::before {\n content: none;\n}\n\ntextarea {\n resize: vertical;\n\n display: block;\n flex: 1;\n\n box-sizing: border-box;\n width: 100%;\n margin: 0;\n padding: 0;\n border: none;\n\n /* 表单控件不继承字体;显式继承以对齐宿主排版,避免 UA 默认字体差异 */\n font: inherit;\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 background: transparent;\n outline: none;\n\n &::placeholder {\n /* 同 input:占位文本的排版属于控件,不在这里另写一份 */\n font: inherit;\n color: var(--wui-color-text-secondary, #6a6a6a);\n }\n\n &:disabled {\n cursor: not-allowed;\n }\n\n &:read-only {\n cursor: default;\n }\n}\n\n/*\n * autosize 由组件用 JS 独占高度(_autosize 写内联 height),原生 resize 拖拽与它是\n * 两套高度来源:用户拖高后,下一次内容变化又会被 autosize 拉回去,拖拽变成假动作。\n * 所以 autosize 下关掉 resize grip,需要上限高度请用 max-height(CSS 钳制)。\n */\n:host([autosize]) textarea {\n resize: none;\n}\n\n/* Coarse pointers include iOS Safari, where a sub-16px font triggers focus zoom. */\n@media (pointer: coarse) {\n textarea {\n font-size: max(16px, var(--wui-font-size, 14px));\n }\n}\n\n/* Focus ring 独立成 ::after 层:base/focused 都是显式 box-shadow,保证所有变体都能过渡 */\n.wui-textarea-inner::after {\n pointer-events: none;\n content: '';\n\n position: absolute;\n inset: 0;\n\n border-radius: inherit;\n\n box-shadow:\n inset 0 0 0 1px transparent,\n 0 0 0 var(--wui-focus-ring-width, 3px) transparent;\n\n transition: box-shadow var(--wui-duration-focus, 200ms);\n}\n\n:host([focused]) .wui-textarea-inner::after {\n box-shadow:\n inset 0 0 0 1px var(--wui-color-accent, #0071e3),\n 0 0 0 var(--wui-focus-ring-width, 3px)\n var(--wui-color-focus-ring, color-mix(in srgb, var(--wui-color-accent, #0071e3) 40%, transparent));\n}\n\nslot {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n\n font-size: var(--wui-font-size, 14px);\n color: var(--wui-color-text-secondary, #6a6a6a);\n}\n\nslot.empty {\n display: none;\n}\n\n/* 复用 web-ui-button icon ghost:仅覆盖颜色为 faint 并对齐首行,保持弱意图清除 icon 观感;\n 负 margin-left 抵消容器 flex gap,让清除 button 紧贴 textarea 内容\n 负 margin-right 抵消 button padding,让清除 button 右侧 spacing 视觉上协调 */\n.wui-textarea-inner web-ui-button[variant='ghost'] {\n --wui-button-color: var(--wui-textarea-clear-color);\n\n /* 与首行文字垂直居中对齐:容器 align-items: flex-start 使 button 贴内容区顶部,\n 首行中心 = padding-top + 行高/2;24px button 需相对内容区上移 (行高 − button 高度) / 2 */\n margin-top: calc((var(--wui-font-size, 14px) * 1.5 - 24px) / 2);\n margin-right: -4px;\n margin-left: -8px;\n}\n"],"mappings":""}
@@ -28,6 +28,7 @@ export declare class WebUiTheme extends LitElement {
28
28
  getOverlayRoot(): HTMLElement | undefined;
29
29
  isReducedMotion(): boolean;
30
30
  private _findThemeAncestor;
31
+ private _contentWidth;
31
32
  private _transitionOrigin;
32
33
  private _syncTransitionOriginListeners;
33
34
  private _transitionMotion;
@@ -150,6 +150,14 @@ var k = class extends c {
150
150
  }
151
151
  return null;
152
152
  }
153
+ _contentWidth() {
154
+ try {
155
+ let e = document.createRange();
156
+ return e.selectNodeContents(this), e.getBoundingClientRect().width;
157
+ } catch {
158
+ return 0;
159
+ }
160
+ }
153
161
  _transitionOrigin() {
154
162
  if (Number.isFinite(_.x) && Number.isFinite(_.y)) return _;
155
163
  let e = this.getBoundingClientRect();
@@ -194,19 +202,22 @@ var k = class extends c {
194
202
  ::view-transition-old(root) { z-index: ${t === "dark" ? 1 : 2}; }
195
203
  ::view-transition-new(root) { z-index: ${t === "dark" ? 2 : 1}; }
196
204
  `);
197
- let c = this.style.getPropertyValue("display"), l = o ? this.style.getPropertyValue("view-transition-name") : void 0;
198
- o && (this.style.setProperty("display", "block"), this.style.setProperty("view-transition-name", o));
199
- let u = () => {
205
+ let c = this.style.getPropertyValue("display"), l = this.style.getPropertyValue("width"), u = o ? this.style.getPropertyValue("view-transition-name") : void 0;
206
+ if (o) {
207
+ let e = this._contentWidth();
208
+ this.style.setProperty("display", "block"), e > 0 && this.style.setProperty("width", `${e}px`), this.style.setProperty("view-transition-name", o);
209
+ }
210
+ let d = () => {
200
211
  try {
201
212
  document.adoptedStyleSheets = document.adoptedStyleSheets.filter((e) => e !== s);
202
213
  } catch {}
203
- o && (l ? this.style.setProperty("view-transition-name", l) : this.style.removeProperty("view-transition-name"), c ? this.style.setProperty("display", c) : this.style.removeProperty("display"));
204
- }, d = () => this._appearanceRequest === r ? (this._appearance = e, this._syncResolvedAppearance(), this.requestUpdate("appearance", n), this.updateComplete) : this.updateComplete;
205
- this._transitionCleanup = u, document.adoptedStyleSheets = [...document.adoptedStyleSheets, s];
206
- let f = document.startViewTransition(d);
207
- this._activeTransition = f, this._transitionInputSkipCleanup = E(f);
208
- let p = [];
209
- f.ready.then(() => {
214
+ o && (u ? this.style.setProperty("view-transition-name", u) : this.style.removeProperty("view-transition-name"), l ? this.style.setProperty("width", l) : this.style.removeProperty("width"), c ? this.style.setProperty("display", c) : this.style.removeProperty("display"));
215
+ }, f = () => this._appearanceRequest === r ? (this._appearance = e, this._syncResolvedAppearance(), this.requestUpdate("appearance", n), this.updateComplete) : this.updateComplete;
216
+ this._transitionCleanup = d, document.adoptedStyleSheets = [...document.adoptedStyleSheets, s];
217
+ let p = document.startViewTransition(f);
218
+ this._activeTransition = p, this._transitionInputSkipCleanup = E(p);
219
+ let m = [];
220
+ p.ready.then(() => {
210
221
  let { duration: e, easing: n } = this._transitionMotion();
211
222
  if (e <= 0) return;
212
223
  let { x: r, y: i } = this._transitionOrigin(), s = t === "dark" ? "::view-transition-new(root)" : "::view-transition-old(root)";
@@ -215,7 +226,7 @@ var k = class extends c {
215
226
  x: r - a.left,
216
227
  y: i - a.top
217
228
  }, c = Math.ceil(Math.hypot(Math.max(s.x, a.width - s.x), Math.max(s.y, a.height - s.y))), l = this._transitionKeyframes(s.x, s.y, c, t), u = t === "dark" ? `::view-transition-new(${o})` : `::view-transition-old(${o})`;
218
- p.push(document.documentElement.animate(l, {
229
+ m.push(document.documentElement.animate(l, {
219
230
  duration: e,
220
231
  easing: n,
221
232
  fill: "both",
@@ -224,7 +235,7 @@ var k = class extends c {
224
235
  return;
225
236
  }
226
237
  let c = Math.ceil(Math.hypot(Math.max(r, window.innerWidth - r), Math.max(i, window.innerHeight - i)));
227
- p.push(document.documentElement.animate(this._transitionKeyframes(r, i, c, t), {
238
+ m.push(document.documentElement.animate(this._transitionKeyframes(r, i, c, t), {
228
239
  duration: e,
229
240
  easing: n,
230
241
  fill: "both",
@@ -232,11 +243,11 @@ var k = class extends c {
232
243
  }));
233
244
  }).catch(() => void 0);
234
245
  try {
235
- await f.finished;
246
+ await p.finished;
236
247
  } catch {} finally {
237
248
  this._transitionInputSkipCleanup?.(), this._transitionInputSkipCleanup = void 0, this._activeTransition = void 0, this._transitionCleanup = void 0;
238
- for (let e of p) e.cancel();
239
- u(), this._transitionRequested = !1, b === i && (b = null);
249
+ for (let e of m) e.cancel();
250
+ d(), this._transitionRequested = !1, b === i && (b = null);
240
251
  }
241
252
  }
242
253
  _transitionKeyframes(e, t, n, r) {