@antadesign/anta 0.3.34 → 0.3.35

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 (184) hide show
  1. package/LICENSE.md +14 -1
  2. package/README.md +9 -0
  3. package/dist/anta_helpers.d.ts +46 -30
  4. package/dist/anta_helpers.js +66 -25
  5. package/dist/bundle.css +1 -1
  6. package/dist/bundle.js +2 -1333
  7. package/dist/components/Banner.d.ts +25 -40
  8. package/dist/components/Banner.js +15 -17
  9. package/dist/components/Button.js +1 -0
  10. package/dist/components/ButtonCopy.js +3 -2
  11. package/dist/components/Calendar.js +13 -13
  12. package/dist/components/Card.d.ts +9 -17
  13. package/dist/components/Card.js +50 -46
  14. package/dist/components/Dialog.d.ts +1 -0
  15. package/dist/components/Dialog.js +16 -10
  16. package/dist/components/Expander.d.ts +6 -21
  17. package/dist/components/Expander.js +24 -23
  18. package/dist/components/Icon.d.ts +8 -14
  19. package/dist/components/Input.d.ts +77 -96
  20. package/dist/components/Input.js +23 -27
  21. package/dist/components/InputAutocomplete.d.ts +29 -10
  22. package/dist/components/InputAutocomplete.js +25 -8
  23. package/dist/components/InputDate.js +2 -2
  24. package/dist/components/InputTime.d.ts +10 -4
  25. package/dist/components/InputTime.js +20 -16
  26. package/dist/components/Loader.d.ts +2 -2
  27. package/dist/components/Menu.d.ts +5 -4
  28. package/dist/components/Menu.js +13 -8
  29. package/dist/components/MenuItem.js +1 -14
  30. package/dist/components/MenuItemCopy.js +2 -2
  31. package/dist/components/Panel.d.ts +3 -1
  32. package/dist/components/Panel.js +17 -11
  33. package/dist/components/Progress.d.ts +21 -23
  34. package/dist/components/Progress.js +13 -9
  35. package/dist/components/RadioGroup.js +6 -8
  36. package/dist/components/{select-parts.css → Select.css} +1 -1
  37. package/dist/components/Select.d.ts +53 -35
  38. package/dist/components/Select.js +53 -97
  39. package/dist/components/SelectFaceted.d.ts +7 -3
  40. package/dist/components/SelectFaceted.js +21 -9
  41. package/dist/components/Slider.d.ts +28 -11
  42. package/dist/components/Slider.js +20 -20
  43. package/dist/components/Steps.css +1 -1
  44. package/dist/components/Steps.js +1 -11
  45. package/dist/components/Tabs.d.ts +17 -20
  46. package/dist/components/Tabs.js +5 -8
  47. package/dist/components/Text.d.ts +55 -36
  48. package/dist/components/Text.js +35 -22
  49. package/dist/components/Title.d.ts +16 -26
  50. package/dist/components/Title.js +2 -1
  51. package/dist/components/Toaster.js +9 -2
  52. package/dist/components/Tooltip.d.ts +10 -15
  53. package/dist/components/copy-props.d.ts +14 -3
  54. package/dist/components/copy-props.js +14 -3
  55. package/dist/components/select-tree.d.ts +24 -0
  56. package/dist/components/select-tree.js +35 -0
  57. package/dist/element-base.d.ts +10 -0
  58. package/dist/element-base.js +15 -0
  59. package/dist/elements/a-banner.css +1 -1
  60. package/dist/elements/a-banner.d.ts +4 -1
  61. package/dist/elements/a-banner.js +22 -112
  62. package/dist/elements/a-button.d.ts +2 -1
  63. package/dist/elements/a-button.js +18 -1
  64. package/dist/elements/a-calendar.d.ts +14 -13
  65. package/dist/elements/a-calendar.js +29 -24
  66. package/dist/elements/a-card.css +1 -1
  67. package/dist/elements/a-card.d.ts +0 -9
  68. package/dist/elements/a-card.js +0 -184
  69. package/dist/elements/a-checkbox.css +1 -1
  70. package/dist/elements/a-copy.css +1 -1
  71. package/dist/elements/a-copy.d.ts +1 -5
  72. package/dist/elements/a-copy.js +129 -108
  73. package/dist/elements/a-dialog.css +1 -1
  74. package/dist/elements/a-dialog.d.ts +1 -1
  75. package/dist/elements/a-dialog.js +66 -194
  76. package/dist/elements/a-expander.css +1 -1
  77. package/dist/elements/a-expander.d.ts +4 -30
  78. package/dist/elements/a-expander.js +85 -344
  79. package/dist/elements/a-icon.d.ts +5 -23
  80. package/dist/elements/a-icon.js +1 -2
  81. package/dist/elements/a-input-time.css +1 -1
  82. package/dist/elements/a-input-time.d.ts +2 -1
  83. package/dist/elements/a-input-time.js +101 -168
  84. package/dist/elements/a-input.css +1 -1
  85. package/dist/elements/a-input.d.ts +10 -8
  86. package/dist/elements/a-input.js +155 -230
  87. package/dist/elements/a-loader.d.ts +5 -8
  88. package/dist/elements/a-loader.js +1 -2
  89. package/dist/elements/a-menu-item.css +1 -1
  90. package/dist/elements/a-menu-item.d.ts +5 -3
  91. package/dist/elements/a-menu-item.js +5 -1
  92. package/dist/elements/a-menu.css +1 -1
  93. package/dist/elements/a-menu.d.ts +5 -7
  94. package/dist/elements/a-menu.js +383 -299
  95. package/dist/elements/a-panel.css +1 -1
  96. package/dist/elements/a-panel.js +19 -37
  97. package/dist/elements/a-progress.css +1 -1
  98. package/dist/elements/a-progress.d.ts +0 -22
  99. package/dist/elements/a-progress.js +0 -100
  100. package/dist/elements/a-radio-group.d.ts +1 -1
  101. package/dist/elements/a-radio-group.js +6 -4
  102. package/dist/elements/a-select-faceted.d.ts +1 -1
  103. package/dist/elements/a-select-faceted.js +1 -1
  104. package/dist/elements/a-select.d.ts +1 -1
  105. package/dist/elements/a-select.js +1 -1
  106. package/dist/elements/a-slider.css +1 -1
  107. package/dist/elements/a-slider.d.ts +11 -3
  108. package/dist/elements/a-slider.js +244 -242
  109. package/dist/elements/a-switch.css +1 -1
  110. package/dist/elements/a-tab.css +1 -1
  111. package/dist/elements/a-tabs.css +1 -1
  112. package/dist/elements/a-tabs.d.ts +1 -1
  113. package/dist/elements/a-tabs.js +6 -4
  114. package/dist/elements/a-tag.css +1 -1
  115. package/dist/elements/a-text.css +1 -1
  116. package/dist/elements/a-text.d.ts +6 -43
  117. package/dist/elements/a-text.js +110 -193
  118. package/dist/elements/a-title.css +1 -1
  119. package/dist/elements/a-toast.css +1 -1
  120. package/dist/elements/a-toast.d.ts +10 -45
  121. package/dist/elements/a-toast.js +80 -66
  122. package/dist/elements/a-toaster.css +1 -1
  123. package/dist/elements/a-toaster.js +9 -44
  124. package/dist/elements/a-tooltip.css +1 -1
  125. package/dist/elements/a-tooltip.d.ts +1 -1
  126. package/dist/elements/a-tooltip.js +142 -136
  127. package/dist/elements/copy-behavior.d.ts +1 -1
  128. package/dist/elements/copy-behavior.js +21 -21
  129. package/dist/elements/index.d.ts +3 -3
  130. package/dist/elements/index.js +3 -7
  131. package/dist/elements/input-focus-modality.d.ts +15 -0
  132. package/dist/elements/input-focus-modality.js +44 -0
  133. package/dist/elements/input-trailing-actions.d.ts +5 -0
  134. package/dist/elements/input-trailing-actions.js +38 -0
  135. package/dist/elements/shadow-aria.d.ts +2 -0
  136. package/dist/elements/shadow-aria.js +2 -1
  137. package/dist/elements/value-input.d.ts +3 -0
  138. package/dist/elements/value-input.js +12 -0
  139. package/dist/general_types.d.ts +103 -96
  140. package/dist/index.d.ts +1 -1
  141. package/dist/input-events.d.ts +4 -0
  142. package/dist/input-events.js +15 -0
  143. package/dist/jsx-runtime.d.ts +70 -23
  144. package/dist/jsx-runtime.js +26 -19
  145. package/dist/native-event-props.d.ts +2 -0
  146. package/dist/native-event-props.js +10 -0
  147. package/dist/reset.css +1 -1
  148. package/dist/theme-antithesis.css +10 -1
  149. package/dist/theme-antune.css +1 -1
  150. package/docs/accessibility.md +17 -3
  151. package/docs/changelog.md +348 -2
  152. package/docs/comparison.md +2 -2
  153. package/docs/components/avatar.md +5 -0
  154. package/docs/components/banner.md +117 -187
  155. package/docs/components/button.md +22 -0
  156. package/docs/components/card.md +74 -28
  157. package/docs/components/checkbox.md +9 -10
  158. package/docs/components/dialog.md +83 -32
  159. package/docs/components/expander.md +198 -435
  160. package/docs/components/icon.md +11 -17
  161. package/docs/components/input-autocomplete.md +152 -52
  162. package/docs/components/input-date.md +175 -148
  163. package/docs/components/input-time.md +81 -38
  164. package/docs/components/input.md +343 -349
  165. package/docs/components/loader.md +2 -1
  166. package/docs/components/menu.md +35 -6
  167. package/docs/components/panel.md +18 -0
  168. package/docs/components/progress.md +83 -49
  169. package/docs/components/radio.md +39 -43
  170. package/docs/components/select-faceted.md +54 -7
  171. package/docs/components/select.md +390 -386
  172. package/docs/components/slider.md +136 -19
  173. package/docs/components/steps.md +2 -1
  174. package/docs/components/switch.md +8 -9
  175. package/docs/components/tabs.md +110 -104
  176. package/docs/components/text.md +110 -86
  177. package/docs/components/title.md +47 -33
  178. package/docs/components/toaster.md +242 -84
  179. package/docs/components/tooltip.md +204 -113
  180. package/docs/install-config.md +183 -26
  181. package/docs/packages/plot.md +170 -148
  182. package/docs/theming.md +7 -0
  183. package/package.json +41 -3
  184. package/theme-antithesis-license.md +66 -0
@@ -1,5 +1,7 @@
1
1
  import { finiteNumber, HTMLElementBase, isModifiedNavigationKey } from "../anta_helpers";
2
2
  import "./a-slider.css";
3
+ import { referencedElements } from "./shadow-aria";
4
+ import { upgradeValueProperties } from "./value-input";
3
5
  const precisionOf = (value) => {
4
6
  const exponent = value.toString().match(/(?:\.(\d+))?(?:e([+-]?\d+))?$/i);
5
7
  return Math.max(0, (exponent?.[1]?.length ?? 0) - Number(exponent?.[2] ?? 0));
@@ -30,7 +32,9 @@ class ASliderElement extends HTMLElementBase {
30
32
  "inverted",
31
33
  "name",
32
34
  "aria-label",
33
- "aria-labelledby"
35
+ "aria-labelledby",
36
+ "aria-describedby",
37
+ "aria-description"
34
38
  ];
35
39
  internals;
36
40
  #control;
@@ -61,186 +65,45 @@ class ASliderElement extends HTMLElementBase {
61
65
  #lastActiveThumb = 0;
62
66
  #seeded = false;
63
67
  #dirty = false;
68
+ // Match an accepted range request to the physical thumb that produced it.
69
+ #pendingRequest;
70
+ #endpointLabels = [];
71
+ #accessibilityDirty = true;
72
+ #accessibilityState;
73
+ #accessibilityReferences = [];
74
+ #accessibilityUnresolved = false;
64
75
  #drag;
76
+ #onPointerDown = (event) => {
77
+ if (event.composedPath().includes(this.#control)) this.#beginDrag(event);
78
+ };
79
+ #onPointerMove = (event) => this.#moveDrag(event);
80
+ #onPointerEnd = (event) => this.#finishDrag(event);
81
+ #onKeyDown = (event) => this.#handleKeydown(event);
82
+ // Run the default action after consumer listeners on this host, including
83
+ // listeners added after connection. Ancestors may stop propagation.
84
+ #orderDefaultAction = (event) => {
85
+ const handler = event.type === "keydown" ? this.#onKeyDown : event.type === "pointerdown" ? this.#onPointerDown : this.#onPointerMove;
86
+ this.removeEventListener(event.type, handler);
87
+ this.addEventListener(event.type, handler);
88
+ };
65
89
  get value() {
66
90
  return Array.isArray(this.#value) ? [...this.#value] : this.#value;
67
91
  }
68
92
  set value(value) {
69
- this.setAttribute("value", Array.isArray(value) ? value.join(" ") : String(value));
93
+ if (value == null || value === "") this.removeAttribute("value");
94
+ else this.setAttribute("value", serializeSliderValue(value));
70
95
  }
71
96
  get defaultValue() {
72
97
  return parseSliderValue(this.getAttribute("defaultvalue"), this.#fallbackValue());
73
98
  }
74
99
  set defaultValue(value) {
75
- this.setAttribute("defaultvalue", Array.isArray(value) ? value.join(" ") : String(value));
100
+ if (value == null) this.removeAttribute("defaultvalue");
101
+ else this.setAttribute("defaultvalue", serializeSliderValue(value));
76
102
  }
77
103
  constructor() {
78
104
  super();
79
105
  this.internals = this.attachInternals?.();
80
106
  const shadow = this.attachShadow({ mode: "open" });
81
- const style = document.createElement("style");
82
- style.textContent = `
83
- :host {
84
- display: grid;
85
- grid-template-columns: minmax(0, 1fr);
86
- gap: 1px;
87
- outline: none;
88
- }
89
- [hidden] { display: none !important; }
90
- .header {
91
- display: flex;
92
- align-items: baseline;
93
- gap: 1px;
94
- min-width: 0;
95
- }
96
- slot[name="label"] {
97
- display: block;
98
- min-width: 0;
99
- color: var(--slider-label);
100
- font-family: var(--sans-serif);
101
- font-size: var(--slider-label-size);
102
- font-weight: 500;
103
- line-height: var(--slider-label-line-height);
104
- }
105
- .inline-value,
106
- .end-value {
107
- color: var(--slider-value);
108
- font-family: var(--sans-serif);
109
- font-size: var(--slider-value-size);
110
- font-variant-numeric: tabular-nums;
111
- line-height: var(--slider-value-line-height);
112
- }
113
- .value-affix { color: var(--slider-value-affix); }
114
- .inline-separator,
115
- .inline-value { display: none; }
116
- .inline-value { margin-inline-start: 0.5ch; }
117
- .end-value {
118
- display: block;
119
- flex: 0 0 auto;
120
- margin-inline-start: auto;
121
- padding-inline-start: 1ch;
122
- }
123
- :host([value-display="inline"]) .inline-separator,
124
- :host([value-display="inline"]) .inline-value { display: block; }
125
- :host([value-display="inline"]) .end-value,
126
- :host([value-display="thumb"]) .end-value,
127
- :host([value-display="none"]) .end-value { display: none; }
128
- .control {
129
- position: relative;
130
- block-size: var(--anta-slider-control-size);
131
- border-radius: 4px;
132
- cursor: grab;
133
- touch-action: none;
134
- user-select: none;
135
- }
136
- :host([track-click="jump"]) .control { cursor: pointer; }
137
- .control[data-dragging] { cursor: grabbing; }
138
- .control[data-dragging] .thumb[data-active] { background: var(--slider-thumb-active); }
139
- :host(:focus-visible) .control:not([data-pointer-focus]) {
140
- outline: 1px solid var(--focus-ring);
141
- outline-offset: 0;
142
- }
143
- .rail-area {
144
- position: absolute;
145
- inset-block-start: 50%;
146
- inset-inline: calc(var(--anta-slider-thumb-size) / 2);
147
- block-size: var(--anta-slider-track-size);
148
- transform: translateY(-50%);
149
- }
150
- .rail,
151
- .fill {
152
- position: absolute;
153
- inset: 0;
154
- border-radius: var(--slider-track-radius);
155
- transition: background-color 75ms ease-out;
156
- }
157
- .rail {
158
- inset-inline: calc(var(--slider-track-inset) - var(--anta-slider-thumb-size) / 2);
159
- background: var(--slider-track);
160
- }
161
- .fill {
162
- inset-inline-start: calc(var(--_fill-start, 0%) + var(--_fill-start-edge, 0px));
163
- inset-inline-end: auto;
164
- inline-size: calc(var(--_fill-end, 0%) - var(--_fill-start, 0%) + var(--_fill-end-edge, 0px) - var(--_fill-start-edge, 0px));
165
- background: var(--slider-fill);
166
- }
167
- .thumb {
168
- position: absolute;
169
- inset-block-start: 50%;
170
- inset-inline-start: var(--_percent, 0%);
171
- box-sizing: border-box;
172
- inline-size: var(--anta-slider-thumb-size);
173
- block-size: var(--anta-slider-thumb-size);
174
- border: var(--anta-slider-thumb-border-width) solid var(--slider-thumb-border);
175
- border-radius: var(--slider-thumb-radius);
176
- background: var(--slider-thumb);
177
- transition: background-color 75ms ease-out, border-color 75ms ease-out;
178
- transform: translate(-50%, -50%);
179
- }
180
- .thumb[data-last-active] { z-index: 1; }
181
- .thumb:focus-visible {
182
- outline: 1px solid var(--focus-ring);
183
- outline-offset: 2px;
184
- z-index: 2;
185
- }
186
- .thumb-value {
187
- display: none;
188
- position: absolute;
189
- inset-inline-start: var(--_percent, 0%);
190
- inset-block-start: 50%;
191
- min-inline-size: max-content;
192
- color: var(--slider-value);
193
- font-family: var(--sans-serif);
194
- font-size: var(--slider-value-size);
195
- font-variant-numeric: tabular-nums;
196
- line-height: var(--slider-value-line-height);
197
- padding-inline: 6px;
198
- background: var(--bg-2);
199
- border: 1px solid var(--border-3);
200
- border-radius: 4px;
201
- text-align: center;
202
- transform: translate(-50%, calc(
203
- -50% - var(--anta-slider-control-size) / 2 - 1px - var(--slider-value-line-height) / 2
204
- ));
205
- }
206
- :host([value-display="thumb"]) .thumb-value { display: block; }
207
- :host([value-display="thumb"]) .thumb-value[hidden] { display: none !important; }
208
- slot[name="markers"] {
209
- display: block;
210
- position: relative;
211
- min-block-size: var(--slider-marker-line-height);
212
- }
213
- slot[name="markers"]::slotted(*) {
214
- position: absolute;
215
- inset-inline-start: var(--slider-marker-position);
216
- max-inline-size: min(12rem, 50vw);
217
- color: var(--slider-marker);
218
- font-family: var(--sans-serif);
219
- font-size: var(--slider-marker-size);
220
- font-variant-numeric: tabular-nums;
221
- line-height: var(--slider-marker-line-height);
222
- text-align: center;
223
- transform: translateX(-50%);
224
- white-space: nowrap;
225
- }
226
- slot[name="markers"]::slotted([data-slider-marker-start]) { text-align: start; transform: translateX(0); }
227
- slot[name="markers"]::slotted([data-slider-marker-end]) { text-align: end; transform: translateX(-100%); }
228
- slot:not([name]) { display: block; }
229
- :host([disabled]) .control,
230
- :host(:disabled) .control { cursor: not-allowed; }
231
- @media (forced-colors: active) {
232
- .rail { background: Canvas; border: 1px solid CanvasText; }
233
- .fill { background: Highlight; }
234
- .thumb { background: Canvas; border-color: CanvasText; }
235
- :host(:focus-visible) .control { outline: 2px solid Highlight; }
236
- .thumb:focus-visible { outline: 2px solid Highlight; }
237
- }
238
- @media (prefers-reduced-motion: reduce) {
239
- .rail,
240
- .fill,
241
- .thumb { transition: none; }
242
- }
243
- `;
244
107
  this.#header = document.createElement("div");
245
108
  this.#header.className = "header";
246
109
  this.#header.part.add("header");
@@ -249,22 +112,27 @@ class ASliderElement extends HTMLElementBase {
249
112
  this.#label.part.add("label");
250
113
  const separator = document.createElement("span");
251
114
  separator.className = "inline-separator";
115
+ separator.part.add("value-separator");
252
116
  separator.textContent = ":";
253
117
  this.#inlineValue = document.createElement("span");
254
118
  this.#inlineValue.className = "inline-value";
255
- this.#inlineValue.part.add("value");
119
+ this.#inlineValue.part.add("value", "inline-value");
256
120
  this.#inlineValuePrefix = document.createElement("span");
257
121
  this.#inlineValuePrefix.className = "value-affix";
122
+ this.#inlineValuePrefix.part.add("value-affix");
258
123
  this.#inlineValueSuffix = document.createElement("span");
259
124
  this.#inlineValueSuffix.className = "value-affix";
125
+ this.#inlineValueSuffix.part.add("value-affix");
260
126
  this.#inlineValue.append(this.#inlineValuePrefix, this.#inlineValueSuffix);
261
127
  this.#endValue = document.createElement("span");
262
128
  this.#endValue.className = "end-value";
263
- this.#endValue.part.add("value");
129
+ this.#endValue.part.add("value", "end-value");
264
130
  this.#endValuePrefix = document.createElement("span");
265
131
  this.#endValuePrefix.className = "value-affix";
132
+ this.#endValuePrefix.part.add("value-affix");
266
133
  this.#endValueSuffix = document.createElement("span");
267
134
  this.#endValueSuffix.className = "value-affix";
135
+ this.#endValueSuffix.part.add("value-affix");
268
136
  this.#endValue.append(this.#endValuePrefix, this.#endValueSuffix);
269
137
  this.#header.append(this.#label, separator, this.#inlineValue, this.#endValue);
270
138
  this.#control = document.createElement("div");
@@ -272,12 +140,13 @@ class ASliderElement extends HTMLElementBase {
272
140
  this.#control.part.add("control");
273
141
  this.#railArea = document.createElement("div");
274
142
  this.#railArea.className = "rail-area";
143
+ this.#railArea.part.add("rail-area");
275
144
  const rail = document.createElement("div");
276
145
  rail.className = "rail";
277
146
  rail.part.add("track");
278
147
  this.#fill = document.createElement("div");
279
148
  this.#fill.className = "fill";
280
- this.#fill.part.add("fill");
149
+ this.#fill.part.add("fill", "fill-start");
281
150
  this.#fillEnd = document.createElement("div");
282
151
  this.#fillEnd.className = "fill";
283
152
  this.#fillEnd.part.add("fill", "fill-end");
@@ -289,19 +158,23 @@ class ASliderElement extends HTMLElementBase {
289
158
  this.#thumbEnd.part.add("thumb", "thumb-2");
290
159
  this.#thumbValue = document.createElement("span");
291
160
  this.#thumbValue.className = "thumb-value";
292
- this.#thumbValue.part.add("thumb-value");
161
+ this.#thumbValue.part.add("thumb-value", "thumb-value-1");
293
162
  this.#thumbValuePrefix = document.createElement("span");
294
163
  this.#thumbValuePrefix.className = "value-affix";
164
+ this.#thumbValuePrefix.part.add("value-affix");
295
165
  this.#thumbValueSuffix = document.createElement("span");
296
166
  this.#thumbValueSuffix.className = "value-affix";
167
+ this.#thumbValueSuffix.part.add("value-affix");
297
168
  this.#thumbValue.append(this.#thumbValuePrefix, this.#thumbValueSuffix);
298
169
  this.#thumbValueEnd = document.createElement("span");
299
170
  this.#thumbValueEnd.className = "thumb-value";
300
171
  this.#thumbValueEnd.part.add("thumb-value", "thumb-value-2");
301
172
  this.#thumbValueEndPrefix = document.createElement("span");
302
173
  this.#thumbValueEndPrefix.className = "value-affix";
174
+ this.#thumbValueEndPrefix.part.add("value-affix");
303
175
  this.#thumbValueEndSuffix = document.createElement("span");
304
176
  this.#thumbValueEndSuffix.className = "value-affix";
177
+ this.#thumbValueEndSuffix.part.add("value-affix");
305
178
  this.#thumbValueEnd.append(this.#thumbValueEndPrefix, this.#thumbValueEndSuffix);
306
179
  this.#railArea.append(rail, this.#fill, this.#fillEnd, this.#thumb, this.#thumbEnd, this.#thumbValue, this.#thumbValueEnd);
307
180
  this.#control.append(this.#railArea);
@@ -313,7 +186,10 @@ class ASliderElement extends HTMLElementBase {
313
186
  for (const slot of [this.#label, this.#markers, this.#extras]) {
314
187
  slot.addEventListener("slotchange", () => {
315
188
  this.#syncSlotVisibility();
316
- if (slot === this.#label) this.#paintAccessibility();
189
+ if (slot === this.#label) {
190
+ this.#accessibilityDirty = true;
191
+ this.#paintAccessibility();
192
+ }
317
193
  });
318
194
  }
319
195
  for (const [index, thumb] of [this.#thumb, this.#thumbEnd].entries()) {
@@ -323,16 +199,25 @@ class ASliderElement extends HTMLElementBase {
323
199
  this.#paint();
324
200
  });
325
201
  }
326
- this.#control.addEventListener("pointerdown", (event) => this.#beginDrag(event));
327
- this.#control.addEventListener("pointermove", (event) => this.#moveDrag(event));
328
- this.#control.addEventListener("pointerup", (event) => this.#finishDrag(event));
329
- this.#control.addEventListener("pointercancel", (event) => this.#finishDrag(event));
330
- this.#control.addEventListener("lostpointercapture", (event) => this.#finishDrag(event));
331
- this.addEventListener("keydown", (event) => this.#handleKeydown(event));
332
- this.addEventListener("blur", () => delete this.#control.dataset.pointerFocus);
333
- shadow.append(style, this.#header, this.#control, this.#markers, this.#extras);
202
+ this.#control.addEventListener("pointerup", this.#onPointerEnd);
203
+ this.#control.addEventListener("pointercancel", this.#onPointerEnd);
204
+ this.#control.addEventListener("lostpointercapture", this.#onPointerEnd);
205
+ this.addEventListener("blur", () => {
206
+ delete this.#control.dataset.pointerFocus;
207
+ this.#paintStates();
208
+ });
209
+ for (const thumb of [this.#thumb, this.#thumbEnd]) {
210
+ const endpoint = document.createElement("span");
211
+ endpoint.hidden = true;
212
+ thumb.append(endpoint);
213
+ this.#endpointLabels.push(endpoint);
214
+ }
215
+ shadow.append(this.#header, this.#control, this.#markers, this.#extras);
216
+ upgradeValueProperties(this);
334
217
  }
335
218
  connectedCallback() {
219
+ this.#accessibilityDirty = true;
220
+ for (const type of ["pointerdown", "keydown", "pointermove"]) this.addEventListener(type, this.#orderDefaultAction, true);
336
221
  if (!this.#seeded) {
337
222
  this.#applyValue(this.#initialValue());
338
223
  this.#seeded = true;
@@ -340,24 +225,54 @@ class ASliderElement extends HTMLElementBase {
340
225
  this.#paint();
341
226
  this.#syncSlotVisibility();
342
227
  }
343
- attributeChangedCallback(name) {
228
+ disconnectedCallback() {
229
+ this.#endDrag(false);
230
+ for (const type of ["pointerdown", "keydown", "pointermove"]) this.removeEventListener(type, this.#orderDefaultAction, true);
231
+ this.removeEventListener("pointerdown", this.#onPointerDown);
232
+ this.removeEventListener("keydown", this.#onKeyDown);
233
+ this.removeEventListener("pointermove", this.#onPointerMove);
234
+ this.#pendingRequest = void 0;
235
+ delete this.#control.dataset.pointerFocus;
236
+ this.#paintStates();
237
+ }
238
+ adoptedCallback() {
239
+ this.disconnectedCallback();
240
+ }
241
+ attributeChangedCallback(name, previous, next) {
344
242
  if (!this.#seeded) return;
243
+ if (name.startsWith("aria-")) this.#accessibilityDirty = true;
345
244
  if (name === "value") {
346
- if (this.hasAttribute("value")) {
347
- this.#applyValue(parseSliderValue(this.getAttribute("value"), this.#fallbackValue()));
348
- this.#dirty = false;
349
- } else if (!this.#dirty) {
350
- this.#applyValue(this.#initialValue());
351
- }
352
- } else if (name === "defaultvalue" && !this.hasAttribute("value") && !this.#dirty) {
353
- this.#applyValue(this.#initialValue());
245
+ if (next === null && previous !== null) {
246
+ this.#dirty = true;
247
+ this.#pendingRequest = void 0;
248
+ this.#rebaseDrag();
249
+ } else this.#syncInput();
250
+ } else if (name === "defaultvalue") {
251
+ if (!this.#controlled && !this.#dirty) this.#syncInput();
354
252
  } else if (name === "min" || name === "max" || name === "step") {
355
- this.#applyValue(this.#value);
253
+ if (this.#controlled) this.#syncInput();
254
+ else {
255
+ this.#applyValue(this.#value);
256
+ this.#rebaseDrag();
257
+ }
356
258
  }
259
+ if (this.#isDisabled) this.#endDrag(true);
357
260
  this.#paint();
358
261
  if (name === "value-display") this.#syncSlotVisibility();
359
262
  }
263
+ #syncInput() {
264
+ const next = this.#normalizeValue(this.#initialValue());
265
+ const pending = this.#pendingRequest;
266
+ if (pending && sameSliderValue(pending.value, next)) this.#orderReversed = pending.reversed;
267
+ else this.#rebaseDrag(next);
268
+ this.#pendingRequest = void 0;
269
+ this.#applyValue(next);
270
+ this.#paint();
271
+ }
360
272
  formResetCallback() {
273
+ this.#endDrag(false);
274
+ this.#pendingRequest = void 0;
275
+ if (this.#controlled) return;
361
276
  const previous = this.#value;
362
277
  this.#applyValue(this.defaultValue);
363
278
  this.#dirty = false;
@@ -366,11 +281,15 @@ class ASliderElement extends HTMLElementBase {
366
281
  }
367
282
  formStateRestoreCallback(state) {
368
283
  if (typeof state !== "string") return;
284
+ if (this.#controlled) return;
285
+ this.#endDrag(false);
286
+ this.#pendingRequest = void 0;
369
287
  this.#applyValue(parseSliderValue(state, this.#fallbackValue()));
370
288
  this.#dirty = true;
371
289
  this.#paint();
372
290
  }
373
291
  formDisabledCallback() {
292
+ if (this.#isDisabled) this.#endDrag(true);
374
293
  this.#paint();
375
294
  }
376
295
  get #min() {
@@ -386,21 +305,25 @@ class ASliderElement extends HTMLElementBase {
386
305
  get #isDisabled() {
387
306
  return this.hasAttribute("disabled") || this.matches(":disabled");
388
307
  }
308
+ get #controlled() {
309
+ return this.hasAttribute("value");
310
+ }
389
311
  #fallbackValue() {
390
312
  const range = Array.isArray(this.#value) || Array.isArray(parseSliderValue(this.getAttribute("value"), this.#min));
391
313
  return range ? [this.#min, this.#max] : this.#min;
392
314
  }
393
315
  #initialValue() {
394
- const source = this.getAttribute("value") ?? this.getAttribute("defaultvalue");
395
- return parseSliderValue(source, this.#fallbackValue());
316
+ if (this.#controlled) return parseSliderValue(this.getAttribute("value"), this.#fallbackValue());
317
+ return this.defaultValue;
396
318
  }
397
319
  #normalize(value) {
398
320
  const min = this.#min;
399
321
  const max = this.#max;
400
322
  const step = this.#step;
401
323
  const stepped = min + Math.round((Math.min(max, Math.max(min, value)) - min) / step) * step;
402
- const precision = Math.min(12, Math.max(precisionOf(min), precisionOf(max), precisionOf(step)));
403
- return Number(Math.min(max, Math.max(min, stepped)).toFixed(precision));
324
+ const precision = Math.max(precisionOf(min), precisionOf(max), precisionOf(step));
325
+ const rounded = precision <= 100 ? Number(stepped.toFixed(precision)) : Number(stepped.toPrecision(15));
326
+ return Math.min(max, Math.max(min, rounded));
404
327
  }
405
328
  #normalizeValue(value) {
406
329
  if (!Array.isArray(value)) return this.#normalize(value);
@@ -464,6 +387,7 @@ class ASliderElement extends HTMLElementBase {
464
387
  this.#renderValue(this.#endValue, this.#endValuePrefix, this.#endValueSuffix, range ? low : high, range ? high : void 0);
465
388
  this.#renderValue(this.#thumbValue, this.#thumbValuePrefix, this.#thumbValueSuffix, first);
466
389
  if (range) this.#renderValue(this.#thumbValueEnd, this.#thumbValueEndPrefix, this.#thumbValueEndSuffix, second);
390
+ this.#paintStates();
467
391
  this.#paintAccessibility();
468
392
  this.#paintFormValue();
469
393
  }
@@ -485,43 +409,82 @@ class ASliderElement extends HTMLElementBase {
485
409
  const suffix = this.getAttribute("value-suffix") ?? "";
486
410
  return `${prefix}${value}${suffix}`;
487
411
  }
412
+ #paintStates() {
413
+ const states = this.internals?.states;
414
+ if (!states) return;
415
+ const set = (name, enabled) => enabled ? states.add(name) : states.delete(name);
416
+ set("dragging", !!this.#drag);
417
+ set("pointer-focus", this.#control.hasAttribute("data-pointer-focus"));
418
+ set("thumb-1-active", this.#lastActiveThumb === 0);
419
+ set("thumb-2-active", this.#lastActiveThumb === 1);
420
+ for (const [name, element] of [
421
+ ["has-header", this.#header],
422
+ ["has-markers", this.#markers],
423
+ ["has-extras", this.#extras],
424
+ ["has-fill", this.#fill],
425
+ ["has-fill-end", this.#fillEnd],
426
+ ["has-thumb-value", this.#thumbValue],
427
+ ["has-thumb-value-end", this.#thumbValueEnd]
428
+ ]) set(name, !element.hidden);
429
+ set("range", Array.isArray(this.#value));
430
+ }
488
431
  #paintAccessibility() {
489
432
  const range = Array.isArray(this.#value);
490
- const slottedLabel = this.#label.assignedNodes({ flatten: true }).map((node) => node.textContent ?? "").join("").trim();
433
+ const disabled = this.#isDisabled;
434
+ const previous = this.#accessibilityState;
491
435
  const root = this.getRootNode();
492
- const referencedLabel = (this.getAttribute("aria-labelledby") ?? "").split(/\s+/).map((id) => root.getElementById(id)?.textContent?.trim() ?? "").filter(Boolean).join(" ");
493
- const base = (this.getAttribute("aria-label") ?? referencedLabel ?? slottedLabel).trim() || slottedLabel;
436
+ const referencesChanged = this.#accessibilityReferences.some(({ id, element }) => element.id !== id || element.getRootNode() !== root);
437
+ if (this.#accessibilityDirty || this.#accessibilityUnresolved || referencesChanged || !previous || previous.range !== range || previous.disabled !== disabled || range && previous.reversed !== this.#orderReversed) {
438
+ this.#syncAccessibility(range, disabled);
439
+ this.#accessibilityDirty = false;
440
+ this.#accessibilityState = { range, reversed: this.#orderReversed, disabled };
441
+ }
494
442
  const internals = this.internals;
495
443
  if (internals) {
496
- internals.role = range ? "group" : "slider";
497
- internals.ariaLabel = !this.hasAttribute("aria-label") && !this.hasAttribute("aria-labelledby") ? slottedLabel || null : null;
498
444
  internals.ariaValueMin = range ? null : String(this.#min);
499
445
  internals.ariaValueMax = range ? null : String(this.#max);
500
446
  internals.ariaValueNow = range ? null : String(this.#value);
501
447
  internals.ariaValueText = range ? null : this.#formatValue(this.#value);
502
448
  }
449
+ if (!range) return;
450
+ for (const [index, thumb] of [this.#thumb, this.#thumbEnd].entries()) {
451
+ thumb.ariaValueMin = String(this.#min);
452
+ thumb.ariaValueMax = String(this.#max);
453
+ thumb.ariaValueNow = String(this.#physicalValues[index]);
454
+ thumb.ariaValueText = this.#formatValue(this.#physicalValues[index]);
455
+ }
456
+ }
457
+ #syncAccessibility(range, disabled) {
458
+ const referenced = referencedElements(this, this.getAttribute("aria-labelledby") ?? "");
459
+ const labels = referenced.length ? referenced : this.#label.assignedElements({ flatten: true });
460
+ const explicit = referenced.length ? null : this.getAttribute("aria-label");
461
+ const descriptions = range ? referencedElements(this, this.getAttribute("aria-describedby") ?? "") : [];
462
+ this.#accessibilityReferences = [...referenced, ...descriptions].map((element) => ({ id: element.id, element }));
463
+ const ids = [this.getAttribute("aria-labelledby"), range ? this.getAttribute("aria-describedby") : null].flatMap((value) => value?.match(/\S+/g) ?? []);
464
+ this.#accessibilityUnresolved = ids.length !== this.#accessibilityReferences.length;
465
+ const internals = this.internals;
466
+ if (internals) {
467
+ internals.role = range ? "group" : "slider";
468
+ internals.ariaLabelledByElements = explicit == null && !this.hasAttribute("aria-labelledby") ? labels : [];
469
+ internals.ariaDisabled = disabled ? "true" : null;
470
+ }
503
471
  for (const [index, thumb] of [this.#thumb, this.#thumbEnd].entries()) {
504
472
  if (!range) {
505
- thumb.removeAttribute("role");
473
+ for (const name of ["role", "aria-label", "aria-valuemin", "aria-valuemax", "aria-valuenow", "aria-valuetext", "aria-disabled", "aria-description"]) thumb.removeAttribute(name);
474
+ thumb.ariaLabelledByElements = [];
475
+ thumb.ariaDescribedByElements = [];
506
476
  thumb.tabIndex = -1;
507
- thumb.removeAttribute("aria-label");
508
- thumb.removeAttribute("aria-valuemin");
509
- thumb.removeAttribute("aria-valuemax");
510
- thumb.removeAttribute("aria-valuenow");
511
- thumb.removeAttribute("aria-valuetext");
512
- thumb.removeAttribute("aria-disabled");
513
477
  continue;
514
478
  }
515
479
  const endpoint = index === 0 !== this.#orderReversed ? "minimum" : "maximum";
516
- const value = this.#physicalValues[index];
480
+ const endpointLabel = this.#endpointLabels[index];
481
+ endpointLabel.textContent = explicit != null ? `${explicit} ${endpoint}` : labels.length ? endpoint : `${endpoint[0].toUpperCase()}${endpoint.slice(1)} value`;
517
482
  thumb.role = "slider";
518
- thumb.tabIndex = this.#isDisabled ? -1 : 0;
519
- thumb.ariaLabel = base ? `${base} ${endpoint}` : `${endpoint[0].toUpperCase()}${endpoint.slice(1)} value`;
520
- thumb.ariaValueMin = String(this.#min);
521
- thumb.ariaValueMax = String(this.#max);
522
- thumb.ariaValueNow = String(value);
523
- thumb.ariaValueText = this.#formatValue(value);
524
- thumb.ariaDisabled = this.#isDisabled ? "true" : null;
483
+ thumb.tabIndex = disabled ? -1 : 0;
484
+ thumb.ariaLabelledByElements = explicit != null ? [endpointLabel] : [...labels, endpointLabel];
485
+ thumb.ariaDescribedByElements = descriptions;
486
+ thumb.ariaDescription = this.getAttribute("aria-description");
487
+ thumb.ariaDisabled = disabled ? "true" : null;
525
488
  }
526
489
  }
527
490
  #paintFormValue() {
@@ -547,37 +510,48 @@ class ASliderElement extends HTMLElementBase {
547
510
  this.#header.hidden = !assigned(this.#label) && this.getAttribute("value-display") === "none";
548
511
  this.#markers.hidden = !assigned(this.#markers);
549
512
  this.#extras.hidden = !assigned(this.#extras);
513
+ this.#paintStates();
514
+ }
515
+ #snapshot(value) {
516
+ return { value: Array.isArray(value) ? [...value] : value, min: this.#min, max: this.#max, step: this.#step, name: this.getAttribute("name") ?? void 0 };
517
+ }
518
+ #notify(type, value = this.#value) {
519
+ this.dispatchEvent(new CustomEvent(type, { bubbles: true, composed: true, detail: this.#snapshot(value) }));
550
520
  }
551
521
  #setValue(value, thumb = 0) {
552
522
  const next = this.#normalize(value);
553
- if (Array.isArray(this.#value)) {
554
- const previous = this.#value;
555
- this.#physicalValues[thumb] = next;
556
- if (this.#physicalValues[0] !== this.#physicalValues[1]) {
557
- this.#orderReversed = this.#physicalValues[0] > this.#physicalValues[1];
558
- }
559
- this.#value = this.#orderReversed ? [this.#physicalValues[1], this.#physicalValues[0]] : [this.#physicalValues[0], this.#physicalValues[1]];
560
- this.#lastActiveThumb = thumb;
561
- this.#paint();
562
- if (sameSliderValue(previous, this.#value)) return false;
563
- } else {
564
- if (next === this.#value) return false;
565
- this.#value = next;
566
- this.#physicalValues = [next, next];
523
+ const physical = [...this.#physicalValues];
524
+ physical[thumb] = next;
525
+ const reversed = physical[0] === physical[1] ? this.#orderReversed : physical[0] > physical[1];
526
+ const requested = Array.isArray(this.#value) ? reversed ? [physical[1], physical[0]] : physical : next;
527
+ if (sameSliderValue(this.#drag?.lastInput ?? this.#value, requested)) return false;
528
+ if (this.#drag) this.#drag.requested = this.#drag.lastInput = requested;
529
+ this.#pendingRequest = this.#controlled ? { value: requested, reversed } : void 0;
530
+ if (!this.#controlled) {
531
+ this.#orderReversed = reversed;
532
+ this.#applyValue(requested);
533
+ this.#dirty = true;
567
534
  this.#paint();
568
535
  }
569
- this.#dirty = true;
570
- this.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
571
- return true;
536
+ this.#notify("input", requested);
537
+ return requested;
572
538
  }
573
539
  #emitInputAndChange() {
574
- this.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
575
- this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
540
+ this.#notify("input");
541
+ this.#notify("change");
542
+ }
543
+ #rebaseDrag(value = this.#value) {
544
+ const drag = this.#drag;
545
+ if (!drag) return;
546
+ drag.startX = drag.clientX;
547
+ drag.startValue = Array.isArray(value) ? value[this.#orderReversed ? 1 - drag.thumb : drag.thumb] : value;
548
+ drag.lastInput = void 0;
576
549
  }
577
550
  #valueAt(clientX) {
578
551
  const rect = this.#railArea.getBoundingClientRect();
579
552
  if (rect.width <= 0) return Array.isArray(this.#value) ? this.#physicalValues[this.#lastActiveThumb] : this.#value;
580
- return this.#min + (clientX - rect.left) / rect.width * (this.#max - this.#min);
553
+ const ratio = this.#rtl() ? (rect.right - clientX) / rect.width : (clientX - rect.left) / rect.width;
554
+ return this.#min + ratio * (this.#max - this.#min);
581
555
  }
582
556
  #thumbAt(clientX) {
583
557
  if (!Array.isArray(this.#value)) return 0;
@@ -586,14 +560,22 @@ class ASliderElement extends HTMLElementBase {
586
560
  const secondDistance = Math.abs(candidate - this.#physicalValues[1]);
587
561
  return firstDistance === secondDistance ? this.#lastActiveThumb : firstDistance < secondDistance ? 0 : 1;
588
562
  }
563
+ #rtl() {
564
+ return this.ownerDocument.defaultView?.getComputedStyle(this).direction === "rtl";
565
+ }
589
566
  #beginDrag(event) {
590
- if (this.#isDisabled || event.button !== 0) return;
567
+ if (event.defaultPrevented || this.#drag || this.#isDisabled || event.button !== 0) return;
591
568
  const rect = this.#railArea.getBoundingClientRect();
592
569
  if (rect.width <= 0) return;
593
570
  const thumb = event.composedPath()[0] === this.#thumb ? 0 : event.composedPath()[0] === this.#thumbEnd ? 1 : this.#thumbAt(event.clientX);
594
571
  const startValue = Array.isArray(this.#value) ? this.#physicalValues[thumb] : this.#value;
595
- this.#drag = { pointerId: event.pointerId, startX: event.clientX, startValue, startPublicValue: this.value, thumb };
596
- this.#control.setPointerCapture(event.pointerId);
572
+ this.#drag = { pointerId: event.pointerId, startX: event.clientX, clientX: event.clientX, startValue, startPublicValue: this.value, thumb };
573
+ try {
574
+ this.#control.setPointerCapture(event.pointerId);
575
+ } catch {
576
+ this.#drag = void 0;
577
+ return;
578
+ }
597
579
  this.#control.dataset.dragging = "";
598
580
  this.#control.dataset.pointerFocus = "";
599
581
  this.#lastActiveThumb = thumb;
@@ -603,43 +585,62 @@ class ASliderElement extends HTMLElementBase {
603
585
  focusTarget.focus({ preventScroll: true });
604
586
  [this.#thumb, this.#thumbEnd][thumb].dataset.active = "";
605
587
  if (this.getAttribute("track-click") === "jump") this.#setValue(this.#valueAt(event.clientX), thumb);
588
+ this.#paintStates();
606
589
  event.preventDefault();
607
590
  }
608
591
  #moveDrag(event) {
609
592
  const drag = this.#drag;
610
593
  if (!drag || drag.pointerId !== event.pointerId) return;
594
+ if (this.#isDisabled) {
595
+ this.#endDrag(true);
596
+ return;
597
+ }
598
+ if (event.defaultPrevented) return;
599
+ drag.clientX = event.clientX;
600
+ const width = this.#railArea.getBoundingClientRect().width;
601
+ if (width <= 0) return;
611
602
  if (!(event.buttons & 1)) {
612
603
  this.#finishDrag(event);
613
604
  return;
614
605
  }
615
- const next = this.getAttribute("track-click") === "jump" ? this.#valueAt(event.clientX) : drag.startValue + (event.clientX - drag.startX) / this.#railArea.getBoundingClientRect().width * (this.#max - this.#min);
606
+ const next = this.getAttribute("track-click") === "jump" ? this.#valueAt(event.clientX) : drag.startValue + (event.clientX - drag.startX) * (this.#rtl() ? -1 : 1) / width * (this.#max - this.#min);
616
607
  this.#setValue(next, drag.thumb);
617
608
  event.preventDefault();
618
609
  }
619
610
  #finishDrag(event) {
611
+ if (this.#drag?.pointerId === event.pointerId) this.#endDrag(true);
612
+ }
613
+ #endDrag(commit) {
620
614
  const drag = this.#drag;
621
- if (!drag || drag.pointerId !== event.pointerId) return;
615
+ if (!drag) return;
622
616
  this.#drag = void 0;
623
617
  delete this.#control.dataset.dragging;
624
618
  delete [this.#thumb, this.#thumbEnd][drag.thumb].dataset.active;
625
- if (this.#control.hasPointerCapture(event.pointerId)) this.#control.releasePointerCapture(event.pointerId);
626
- if (!sameSliderValue(this.#value, drag.startPublicValue)) this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
619
+ if (this.#control.hasPointerCapture(drag.pointerId)) this.#control.releasePointerCapture(drag.pointerId);
620
+ this.#paintStates();
621
+ const value = drag.requested;
622
+ if (commit && value !== void 0 && !sameSliderValue(value, drag.startPublicValue)) this.#notify("change", value);
627
623
  }
628
624
  #handleKeydown(event) {
629
625
  const source = event.composedPath()[0];
630
626
  const range = Array.isArray(this.#value);
631
627
  const thumb = source === this.#thumb ? 0 : source === this.#thumbEnd ? 1 : void 0;
632
- if ((range ? thumb === void 0 : source !== this) || this.#isDisabled || isModifiedNavigationKey(event)) return;
628
+ if (event.defaultPrevented || !event.composedPath().includes(this) || (range ? thumb === void 0 : source !== this) || this.#isDisabled || isModifiedNavigationKey(event)) return;
633
629
  delete this.#control.dataset.pointerFocus;
630
+ this.#paintStates();
634
631
  const page = Math.max(this.#step, (this.#max - this.#min) / 10);
635
632
  const current = range ? this.#physicalValues[thumb ?? 0] : this.#value;
636
633
  let next;
637
634
  switch (event.key) {
638
635
  case "ArrowRight":
636
+ next = current + (this.#rtl() ? -this.#step : this.#step);
637
+ break;
639
638
  case "ArrowUp":
640
639
  next = current + this.#step;
641
640
  break;
642
641
  case "ArrowLeft":
642
+ next = current + (this.#rtl() ? this.#step : -this.#step);
643
+ break;
643
644
  case "ArrowDown":
644
645
  next = current - this.#step;
645
646
  break;
@@ -659,7 +660,8 @@ class ASliderElement extends HTMLElementBase {
659
660
  return;
660
661
  }
661
662
  event.preventDefault();
662
- if (this.#setValue(next, range ? thumb ?? 0 : 0)) this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
663
+ const requested = this.#setValue(next, range ? thumb ?? 0 : 0);
664
+ if (requested !== false) this.#notify("change", requested);
663
665
  }
664
666
  }
665
667
  function register_a_slider() {