material 3.3.2 → 3.3.3

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 (149) hide show
  1. package/README.md +2 -2
  2. package/dist/components/slider/config.js +0 -46
  3. package/dist/components/slider/features/controller.js +805 -171
  4. package/dist/components/slider/features/dom.js +29 -62
  5. package/dist/components/slider/features/index.js +1 -3
  6. package/dist/components/slider/features/states.js +2 -30
  7. package/dist/components/slider/slider.js +3 -6
  8. package/dist/components/slider/types.d.ts +0 -26
  9. package/dist/core/canvas/resize.d.ts +1 -1
  10. package/dist/core/canvas/resize.js +24 -11
  11. package/dist/core/dom/classes.js +17 -118
  12. package/dist/core/dom/create.d.ts +0 -2
  13. package/dist/core/dom/create.js +0 -72
  14. package/dist/core/utils/mobile.js +9 -29
  15. package/dist/elements/preupgrade/badge.css +1 -1
  16. package/dist/elements/preupgrade/bottom-app-bar.css +1 -1
  17. package/dist/elements/preupgrade/bottom-sheet.css +1 -1
  18. package/dist/elements/preupgrade/button-group.css +1 -1
  19. package/dist/elements/preupgrade/button.css +1 -1
  20. package/dist/elements/preupgrade/card.css +1 -1
  21. package/dist/elements/preupgrade/carousel.css +1 -1
  22. package/dist/elements/preupgrade/checkbox.css +1 -1
  23. package/dist/elements/preupgrade/chips.css +1 -1
  24. package/dist/elements/preupgrade/datepicker.css +1 -1
  25. package/dist/elements/preupgrade/dialog.css +1 -1
  26. package/dist/elements/preupgrade/divider.css +1 -1
  27. package/dist/elements/preupgrade/drawer.css +1 -1
  28. package/dist/elements/preupgrade/extended-fab.css +1 -1
  29. package/dist/elements/preupgrade/fab-menu.css +1 -1
  30. package/dist/elements/preupgrade/fab.css +1 -1
  31. package/dist/elements/preupgrade/icon-button.css +1 -1
  32. package/dist/elements/preupgrade/list.css +1 -1
  33. package/dist/elements/preupgrade/loading-indicator.css +1 -1
  34. package/dist/elements/preupgrade/menu.css +1 -1
  35. package/dist/elements/preupgrade/navigation-bar.css +1 -1
  36. package/dist/elements/preupgrade/navigation-rail.css +1 -1
  37. package/dist/elements/preupgrade/progress.css +1 -1
  38. package/dist/elements/preupgrade/radios.css +1 -1
  39. package/dist/elements/preupgrade/search.css +1 -1
  40. package/dist/elements/preupgrade/select.css +1 -1
  41. package/dist/elements/preupgrade/side-sheet.css +1 -1
  42. package/dist/elements/preupgrade/slider.css +1 -1
  43. package/dist/elements/preupgrade/snackbar.css +1 -1
  44. package/dist/elements/preupgrade/split-button.css +1 -1
  45. package/dist/elements/preupgrade/switch.css +1 -1
  46. package/dist/elements/preupgrade/tabs.css +1 -1
  47. package/dist/elements/preupgrade/text-field.css +1 -1
  48. package/dist/elements/preupgrade/timepicker.css +1 -1
  49. package/dist/elements/preupgrade/toolbar.css +1 -1
  50. package/dist/elements/preupgrade/tooltip.css +1 -1
  51. package/dist/elements/preupgrade/top-app-bar.css +1 -1
  52. package/dist/elements/preupgrade.css +1 -1
  53. package/dist/package.json +1 -1
  54. package/dist/styles/badge.css +1 -1
  55. package/dist/styles/base.css +1 -1
  56. package/dist/styles/bottom-app-bar.css +1 -1
  57. package/dist/styles/bottom-sheet.css +1 -1
  58. package/dist/styles/button-group.css +1 -1
  59. package/dist/styles/button.css +1 -1
  60. package/dist/styles/card.css +1 -1
  61. package/dist/styles/carousel.css +1 -1
  62. package/dist/styles/checkbox.css +1 -1
  63. package/dist/styles/chips.css +1 -1
  64. package/dist/styles/contrast.css +1 -1
  65. package/dist/styles/datepicker.css +1 -1
  66. package/dist/styles/dialog.css +1 -1
  67. package/dist/styles/divider.css +1 -1
  68. package/dist/styles/drawer.css +1 -1
  69. package/dist/styles/extended-fab.css +1 -1
  70. package/dist/styles/fab-menu.css +1 -1
  71. package/dist/styles/fab.css +1 -1
  72. package/dist/styles/icon-button.css +1 -1
  73. package/dist/styles/list.css +1 -1
  74. package/dist/styles/loading-indicator.css +1 -1
  75. package/dist/styles/menu.css +1 -1
  76. package/dist/styles/navigation-bar.css +1 -1
  77. package/dist/styles/navigation-rail.css +1 -1
  78. package/dist/styles/progress.css +1 -1
  79. package/dist/styles/radios.css +1 -1
  80. package/dist/styles/search.css +1 -1
  81. package/dist/styles/select.css +1 -1
  82. package/dist/styles/side-sheet.css +1 -1
  83. package/dist/styles/slider.css +1 -1
  84. package/dist/styles/snackbar.css +1 -1
  85. package/dist/styles/split-button.css +1 -1
  86. package/dist/styles/switch.css +1 -1
  87. package/dist/styles/tabs.css +1 -1
  88. package/dist/styles/text-field.css +1 -1
  89. package/dist/styles/timepicker.css +1 -1
  90. package/dist/styles/toolbar.css +1 -1
  91. package/dist/styles/tooltip.css +1 -1
  92. package/dist/styles/top-app-bar.css +1 -1
  93. package/dist/styles/typography.css +1 -1
  94. package/dist/styles/utilities.css +1 -1
  95. package/dist/styles.css +1 -1
  96. package/dist/themes/autumn-contrast.css +1 -1
  97. package/dist/themes/autumn.css +1 -1
  98. package/dist/themes/baseline-contrast.css +1 -1
  99. package/dist/themes/baseline.css +1 -1
  100. package/dist/themes/brownbeige-contrast.css +1 -1
  101. package/dist/themes/brownbeige.css +1 -1
  102. package/dist/themes/content-contrast.css +1 -1
  103. package/dist/themes/content.css +1 -1
  104. package/dist/themes/desert-contrast.css +1 -1
  105. package/dist/themes/desert.css +1 -1
  106. package/dist/themes/expressive-contrast.css +1 -1
  107. package/dist/themes/expressive.css +1 -1
  108. package/dist/themes/fidelity-contrast.css +1 -1
  109. package/dist/themes/fidelity.css +1 -1
  110. package/dist/themes/forest-contrast.css +1 -1
  111. package/dist/themes/forest.css +1 -1
  112. package/dist/themes/fruit-salad-contrast.css +1 -1
  113. package/dist/themes/fruit-salad.css +1 -1
  114. package/dist/themes/highcontrast-contrast.css +1 -1
  115. package/dist/themes/highcontrast.css +1 -1
  116. package/dist/themes/monochrome-contrast.css +1 -1
  117. package/dist/themes/monochrome.css +1 -1
  118. package/dist/themes/neutral-contrast.css +1 -1
  119. package/dist/themes/neutral.css +1 -1
  120. package/dist/themes/ocean-contrast.css +1 -1
  121. package/dist/themes/ocean.css +1 -1
  122. package/dist/themes/rainbow-contrast.css +1 -1
  123. package/dist/themes/rainbow.css +1 -1
  124. package/dist/themes/sageivory-contrast.css +1 -1
  125. package/dist/themes/sageivory.css +1 -1
  126. package/dist/themes/spring-contrast.css +1 -1
  127. package/dist/themes/spring.css +1 -1
  128. package/dist/themes/summer-contrast.css +1 -1
  129. package/dist/themes/summer.css +1 -1
  130. package/dist/themes/sunset-contrast.css +1 -1
  131. package/dist/themes/sunset.css +1 -1
  132. package/dist/themes/tealcaramel-contrast.css +1 -1
  133. package/dist/themes/tealcaramel.css +1 -1
  134. package/dist/themes/vibrant-contrast.css +1 -1
  135. package/dist/themes/vibrant.css +1 -1
  136. package/package.json +1 -1
  137. package/dist/components/card/features.js +0 -217
  138. package/dist/components/chips/chip/constants.js +0 -18
  139. package/dist/components/chips/chip/index.js +0 -1
  140. package/dist/components/chips/schema.js +0 -46
  141. package/dist/components/progress/features/index.js +0 -4
  142. package/dist/components/progress/features/resize-observer.js +0 -40
  143. package/dist/components/slider/api.js +0 -108
  144. package/dist/components/slider/features/handlers.js +0 -379
  145. package/dist/components/slider/features/range.js +0 -70
  146. package/dist/components/slider/features/tracks.js +0 -287
  147. package/dist/components/tabs/scroll-indicators.js +0 -78
  148. package/dist/components/tabs/state.js +0 -44
  149. package/dist/core/utils/validate.js +0 -114
@@ -1,13 +1,294 @@
1
1
  import { SLIDER_EVENTS } from "../types.js";
2
- import { defaultConfig } from "../config.js";
3
- import { createHandlers } from "./handlers.js";
4
- import { getExternalTrackRadius, trackPositionCss } from "./tracks.js";
5
- import { bubbleTransform, getAxis, handleTransform, isRtl, lengthOf, offsetAlong } from "./axis.js";
2
+ import { defaultConfig, sliderSizeClass } from "../config.js";
3
+ import { SLIDER_MEASUREMENTS, SLIDER_SIZES } from "../constants.js";
4
+ import { PREFIX } from "../../../core/config.js";
5
+ import { cornerToken } from "../../../core/theme/shape.js";
6
+ import { setHTML } from "../../../core/dom/html.js";
6
7
  import { setFormValue } from "../../../core/dom/form-value.js";
8
+ import { activeElementOf } from "../../../core/dom/focus.js";
9
+ import { arrowSign, bubbleTransform, getAxis, handleTransform, isRtl, lengthOf, offsetAlong, } from "./axis.js";
10
+ const getTrackHeight = (size) => {
11
+ if (typeof size === "number") {
12
+ return Math.max(size, SLIDER_SIZES.XS);
13
+ }
14
+ if (typeof size === "string" && size in SLIDER_SIZES) {
15
+ return SLIDER_SIZES[size];
16
+ }
17
+ return SLIDER_SIZES.XS;
18
+ };
19
+ const getHandleHeight = (size) => {
20
+ const trackHeight = getTrackHeight(size);
21
+ if (trackHeight <= SLIDER_SIZES.S) {
22
+ return SLIDER_MEASUREMENTS.SMALL_HANDLE_HEIGHT;
23
+ }
24
+ return trackHeight + SLIDER_MEASUREMENTS.HANDLE_HEIGHT_OFFSET;
25
+ };
26
+ const TRACK_STEPS = { 8: "small", 12: "medium", 16: "large", 28: "extra-large" };
27
+ const getExternalTrackRadius = (size) => {
28
+ const trackHeight = getTrackHeight(size);
29
+ if (trackHeight <= SLIDER_SIZES.S)
30
+ return 8;
31
+ if (trackHeight <= SLIDER_SIZES.M)
32
+ return 12;
33
+ if (trackHeight <= SLIDER_SIZES.L)
34
+ return 16;
35
+ return 28;
36
+ };
37
+ const trackPositionCss = (fraction, inset) => inset
38
+ ? `calc(${fraction * 100}% + ${inset * (1 - 2 * fraction)}px)`
39
+ : `${fraction * 100}%`;
40
+ const A0 = { f: 0, p: 0 };
41
+ const A1 = { f: 1, p: 0 };
42
+ const ap = (f, p) => ({ f, p });
43
+ const add = (a, b) => ap(a.f + b.f, a.p + b.p);
44
+ const sub = (a, b) => ap(a.f - b.f, a.p - b.p);
45
+ const px = (p) => ap(0, p);
46
+ const fractionOf = (value, min, max) => min === max ? 0 : Math.min(1, Math.max(0, (value - min) / (max - min)));
47
+ const alongCss = (a, unit = "%") => {
48
+ const pct = Math.round(a.f * 1e8) / 1e6;
49
+ const p = Math.round(a.p * 1e6) / 1e6;
50
+ if (pct === 0)
51
+ return `${p}px`;
52
+ if (p === 0)
53
+ return `${pct}${unit}`;
54
+ return `calc(${pct}${unit} ${p < 0 ? "-" : "+"} ${Math.abs(p)}px)`;
55
+ };
56
+ const INSET_ICON_PADDING = 10;
57
+ const HANDLE_WIDTH = 4;
58
+ const NARROW_HANDLE_WIDTH = 2;
59
+ export const withTracks = (config) => (component) => {
60
+ const container = component.container;
61
+ const element = (className, parent) => {
62
+ const node = document.createElement("div");
63
+ node.className = component.getClass(className);
64
+ parent.append(node);
65
+ return node;
66
+ };
67
+ let axis = getAxis(config);
68
+ const opposite = { left: "right", right: "left", top: "bottom", bottom: "top" };
69
+ const at = (node, position) => {
70
+ node.style[opposite[axis.start]] = "";
71
+ node.style[axis.start] = position;
72
+ };
73
+ const visual = element("slider__visual", container);
74
+ visual.setAttribute("aria-hidden", "true");
75
+ const track = element("slider__track", visual);
76
+ const segments = Array.from({ length: 3 }, () => element("slider__segment", track));
77
+ const ticks = [element("slider__ticks", visual), element("slider__ticks", visual)];
78
+ ticks[1].classList.add(component.getClass("slider__ticks--active"));
79
+ const dots = [element("slider__dot", visual), element("slider__dot", visual)];
80
+ dots[0].classList.add(component.getClass("slider__dot--start"));
81
+ const insetIcon = element("slider__inset-icon", visual);
82
+ insetIcon.hidden = true;
83
+ let insetMarkup = { icon: config.insetIcon ?? "", atMin: config.insetIconAtMin ?? "" };
84
+ let shownMarkup = null;
85
+ let destroyed = false;
86
+ let size = config.size ?? "XS";
87
+ let state = {
88
+ value: config.value ?? 0, secondValue: config.secondValue ?? null,
89
+ min: config.min ?? 0, max: config.max ?? 100, step: config.step ?? 1,
90
+ };
91
+ const narrowed = (which) => {
92
+ const handle = which === "first" ? component.handle : component.secondHandle;
93
+ const pressed = !!state.pressed && (state.activeHandle === which || (!config.range && which === "first"));
94
+ return pressed || !!handle?.classList.contains(component.getClass("slider__handle--focused"));
95
+ };
96
+ const render = (next) => {
97
+ if (destroyed)
98
+ return;
99
+ if (next)
100
+ state = { ...next };
101
+ axis = getAxis(config, isRtl(component.element));
102
+ const width = lengthOf(container.getBoundingClientRect(), axis) || 0;
103
+ const corner = getExternalTrackRadius(size);
104
+ const inset = config.ticks ? corner : 0;
105
+ const fraction = (value) => fractionOf(value, state.min, state.max);
106
+ const atValue = (value) => {
107
+ const f = fraction(value);
108
+ return ap(f, inset * (1 - 2 * f));
109
+ };
110
+ const cmp = (a, b) => width > 0 ? a.f * width + a.p - (b.f * width + b.p) : (a.f - b.f) || (a.p - b.p);
111
+ const gapOf = (which) => px((narrowed(which) ? NARROW_HANDLE_WIDTH : HANDLE_WIDTH) / 2 + SLIDER_MEASUREMENTS.HANDLE_GAP);
112
+ const firstGap = gapOf("first");
113
+ const secondGap = gapOf("second");
114
+ const first = atValue(state.value);
115
+ const second = atValue(state.secondValue ?? state.max);
116
+ let parts;
117
+ let activeStart, activeEnd;
118
+ if (config.centered) {
119
+ const zero = atValue(Math.min(state.max, Math.max(state.min, 0)));
120
+ if (cmp(first, zero) >= 0) {
121
+ activeStart = zero;
122
+ activeEnd = first;
123
+ parts = [[A0, sub(zero, firstGap), false], [zero, sub(first, firstGap), true], [add(first, firstGap), A1, false]];
124
+ }
125
+ else {
126
+ activeStart = first;
127
+ activeEnd = zero;
128
+ parts = [[A0, sub(first, firstGap), false], [add(first, firstGap), zero, true], [add(zero, firstGap), A1, false]];
129
+ }
130
+ }
131
+ else if (config.range && state.secondValue !== null) {
132
+ const lowIsFirst = cmp(first, second) <= 0;
133
+ const low = lowIsFirst ? first : second;
134
+ const high = lowIsFirst ? second : first;
135
+ const lowGap = lowIsFirst ? firstGap : secondGap;
136
+ const highGap = lowIsFirst ? secondGap : firstGap;
137
+ activeStart = low;
138
+ activeEnd = high;
139
+ parts = [[A0, sub(low, lowGap), false], [add(low, lowGap), sub(high, highGap), true], [add(high, highGap), A1, false]];
140
+ }
141
+ else {
142
+ activeStart = A0;
143
+ activeEnd = first;
144
+ parts = [[A0, A0, false], [A0, sub(first, firstGap), true], [add(first, firstGap), A1, false]];
145
+ }
146
+ const standard = !config.centered && !(config.range && state.secondValue !== null);
147
+ const short = (len) => width > 0 ? len.f * width + len.p <= corner : len.f < 0 || (len.f === 0 && len.p <= corner);
148
+ parts.forEach((part, index) => {
149
+ const meetsEnd = index !== 1 || standard;
150
+ if (meetsEnd && short(sub(part[1], part[0])))
151
+ part[1] = part[0];
152
+ });
153
+ segments.forEach((segment, index) => {
154
+ const [start, end, active] = parts[index] ?? [A0, A0, false];
155
+ const len = sub(end, start);
156
+ const drawn = (width > 0 ? len.f * width + len.p < 0 : len.f < 0 || (len.f === 0 && len.p < 0)) ? A0 : len;
157
+ at(segment, alongCss(start));
158
+ segment.style[axis.size] = alongCss(drawn);
159
+ segment.classList.toggle(component.getClass("slider__segment--active"), active);
160
+ });
161
+ dots[0].hidden = !!config.ticks || standard || cmp(parts[0][1], parts[0][0]) <= 0;
162
+ dots[1].hidden = !!config.ticks || cmp(parts[2][1], parts[2][0]) <= 0;
163
+ at(dots[0], alongCss(px(corner - 2)));
164
+ at(dots[1], alongCss(ap(1, -(corner + 2))));
165
+ placeInsetIcon(parts, width);
166
+ const discrete = !!config.ticks && state.step > 0 && state.max > state.min;
167
+ ticks.forEach(tick => { tick.hidden = !discrete; });
168
+ if (discrete) {
169
+ const stepF = state.step / (state.max - state.min);
170
+ const repeat = `max(0.01px, ${alongCss(ap(stepF, -2 * inset * stepF))})`;
171
+ const offset = alongCss(ap(-stepF / 2, inset * (1 + stepF)), axis.vertical ? "cqh" : "cqw");
172
+ const last = atValue(state.min + Math.floor((state.max - state.min) / state.step) * state.step);
173
+ const holes = [[sub(first, firstGap), add(first, firstGap)]];
174
+ if (config.range && state.secondValue !== null)
175
+ holes.push([sub(second, secondGap), add(second, secondGap)]);
176
+ const mask = (intervals) => {
177
+ const merged = [];
178
+ for (const [start, end] of [...intervals].sort((a, b) => cmp(a[0], b[0]))) {
179
+ const previous = merged[merged.length - 1];
180
+ if (previous && cmp(start, previous[1]) <= 0) {
181
+ if (cmp(end, previous[1]) > 0)
182
+ previous[1] = end;
183
+ }
184
+ else
185
+ merged.push([start, end]);
186
+ }
187
+ const stops = merged.flatMap(([start, end]) => [`black ${alongCss(start)}`, `transparent ${alongCss(start)}`, `transparent ${alongCss(end)}`, `black ${alongCss(end)}`]);
188
+ const direction = { left: "to right", right: "to left", top: "to bottom", bottom: "to top" }[axis.start];
189
+ return stops.length ? `linear-gradient(${direction}, black 0px, ${stops.join(",")}, black 100%)` : "none";
190
+ };
191
+ ticks.forEach((tick, index) => {
192
+ tick.style.backgroundSize = axis.vertical ? `100% ${repeat}` : `${repeat} 100%`;
193
+ tick.style.backgroundPosition = axis.vertical
194
+ ? `center ${axis.start} ${offset}`
195
+ : `${axis.start} ${offset} center`;
196
+ tick.style.maskImage = mask(index === 0 ? [...holes, [sub(activeStart, px(2)), add(activeEnd, px(2))]] : holes);
197
+ });
198
+ const clip = (fromStart, fromEnd) => ({
199
+ left: `inset(0 ${fromEnd} 0 ${fromStart})`,
200
+ right: `inset(0 ${fromStart} 0 ${fromEnd})`,
201
+ top: `inset(${fromStart} 0 ${fromEnd} 0)`,
202
+ bottom: `inset(${fromEnd} 0 ${fromStart} 0)`,
203
+ })[axis.start];
204
+ const fromEnd = (edge) => `max(0px, calc(100% - ${alongCss(edge)}))`;
205
+ ticks[0].style.clipPath = clip("0px", fromEnd(add(last, px(2))));
206
+ ticks[1].style.clipPath = clip(`max(0px, ${alongCss(sub(activeStart, px(2)))})`, `max(0px, calc(100% - min(${alongCss(add(last, px(2)))}, ${alongCss(add(activeEnd, px(2)))})))`);
207
+ }
208
+ };
209
+ function placeInsetIcon(parts, width) {
210
+ const trackHeight = getTrackHeight(size);
211
+ const eligible = !config.range && !config.centered && trackHeight >= SLIDER_SIZES.M;
212
+ const markup = state.value <= state.min && insetMarkup.atMin ? insetMarkup.atMin : insetMarkup.icon;
213
+ const iconSize = trackHeight >= SLIDER_SIZES.XL ? 32 : 24;
214
+ const room = iconSize + 2 * INSET_ICON_PADDING;
215
+ const fits = (len) => width > 0 ? len.f * width + len.p >= room : len.f > 0 || (len.f === 0 && len.p >= room);
216
+ const onActive = fits(sub(parts[1][1], parts[1][0]));
217
+ const onInactive = !onActive && fits(sub(parts[2][1], parts[2][0]));
218
+ insetIcon.hidden = !eligible || !markup || (!onActive && !onInactive);
219
+ if (insetIcon.hidden)
220
+ return;
221
+ if (markup !== shownMarkup) {
222
+ setHTML(insetIcon, markup);
223
+ shownMarkup = markup;
224
+ }
225
+ insetIcon.style.width = insetIcon.style.height = `${iconSize}px`;
226
+ at(insetIcon, alongCss(add(onActive ? parts[1][0] : parts[2][0], px(INSET_ICON_PADDING))));
227
+ insetIcon.classList.toggle(component.getClass("slider__inset-icon--inactive"), onInactive);
228
+ }
229
+ component.setInsetIcon = (icon, atMin = "") => {
230
+ if (destroyed)
231
+ return;
232
+ insetMarkup = { icon, atMin };
233
+ render();
234
+ };
235
+ const setSize = (next) => {
236
+ if (destroyed)
237
+ return;
238
+ const previous = sliderSizeClass(size), current = sliderSizeClass(next);
239
+ if (previous)
240
+ component.element.classList.remove(previous);
241
+ if (current)
242
+ component.element.classList.add(current);
243
+ size = next;
244
+ const handleHeight = getHandleHeight(size);
245
+ container.style[axis.cross] = `${Math.max(handleHeight, SLIDER_MEASUREMENTS.MIN_HEIGHT)}px`;
246
+ track.style[axis.cross] = `${getTrackHeight(size)}px`;
247
+ track.style.borderRadius = cornerToken(TRACK_STEPS[getExternalTrackRadius(size)], getExternalTrackRadius(size), PREFIX);
248
+ for (const handle of [component.handle, component.secondHandle]) {
249
+ if (handle)
250
+ handle.style[axis.cross] = `${handleHeight}px`;
251
+ }
252
+ component.element.style.setProperty(`--${PREFIX}-slider-handle-height`, `${handleHeight}px`);
253
+ render();
254
+ };
255
+ component.renderTracks = render;
256
+ component.setSize = setSize;
257
+ component.getSize = () => size;
258
+ const setColor = component.appearance.setColor;
259
+ component.appearance.setColor = (color) => {
260
+ if (destroyed)
261
+ return;
262
+ setColor.call(component.appearance, color);
263
+ config.color = color;
264
+ };
265
+ setSize(size);
266
+ const resize = () => {
267
+ if (!destroyed)
268
+ render();
269
+ };
270
+ const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(resize) : null;
271
+ if (observer)
272
+ observer.observe(container);
273
+ else
274
+ window.addEventListener("resize", resize);
275
+ const destroy = component.lifecycle.destroy;
276
+ component.lifecycle.destroy = () => {
277
+ if (destroyed)
278
+ return;
279
+ destroyed = true;
280
+ observer?.disconnect();
281
+ if (!observer)
282
+ window.removeEventListener("resize", resize);
283
+ destroy.call(component.lifecycle);
284
+ };
285
+ return component;
286
+ };
287
+ const clientXOf = (e) => "touches" in e ? e.touches[0].clientX : e.clientX;
288
+ const clientYOf = (e) => "touches" in e ? e.touches[0].clientY : e.clientY;
7
289
  export const withController = (config, getComponent) => (component) => {
8
290
  const max = config.max !== undefined ? config.max : 100;
9
291
  const state = {
10
- component,
11
292
  value: config.value !== undefined ? config.value : 0,
12
293
  secondValue: config.secondValue !== undefined
13
294
  ? config.secondValue
@@ -23,44 +304,25 @@ export const withController = (config, getComponent) => (component) => {
23
304
  activeBubble: null,
24
305
  valueHideTimer: null,
25
306
  };
26
- const eventHelpers = {
27
- triggerEvent(eventName, originalEvent = null) {
28
- const eventData = {
29
- slider: getComponent(),
30
- value: state.value,
31
- secondValue: state.secondValue,
32
- originalEvent,
33
- preventDefault: () => {
34
- eventData.defaultPrevented = true;
35
- },
36
- defaultPrevented: false,
37
- };
38
- state.component.emit(eventName, eventData);
39
- return eventData;
40
- },
41
- };
42
- const getComponents = () => {
43
- const components = state.component.components || {};
44
- const component = state.component;
45
- return {
46
- container: component.container || components.container,
47
- handle: component.handle || components.handle,
48
- valueBubble: component.valueBubble || components.valueBubble,
49
- secondHandle: component.secondHandle || components.secondHandle,
50
- secondValueBubble: component.secondValueBubble || components.secondValueBubble,
51
- ...components,
307
+ const emitEvent = (eventName, originalEvent = null) => {
308
+ const eventData = {
309
+ slider: getComponent(),
310
+ value: state.value,
311
+ secondValue: state.secondValue,
312
+ originalEvent,
313
+ preventDefault: () => {
314
+ eventData.defaultPrevented = true;
315
+ },
316
+ defaultPrevented: false,
52
317
  };
53
- };
54
- const getPercentage = (value) => {
55
- const range = state.max - state.min;
56
- return range === 0 ? 0 : ((value - state.min) / range) * 100;
318
+ component.emit(eventName, eventData);
319
+ return eventData;
57
320
  };
58
321
  const axisNow = () => getAxis(config, isRtl(component.element));
59
322
  const inset = () => config.ticks ? getExternalTrackRadius(component.getSize?.() ?? config.size) : 0;
60
- const visualPosition = (percent) => trackPositionCss(Math.min(1, Math.max(0, percent / 100)), inset());
323
+ const visualPosition = (value) => trackPositionCss(fractionOf(value, state.min, state.max), inset());
61
324
  const getValueFromPosition = (position) => {
62
- const components = getComponents();
63
- const { handle, container } = components;
325
+ const { handle, container } = component;
64
326
  if (!handle || !container)
65
327
  return state.min;
66
328
  try {
@@ -87,8 +349,7 @@ export const withController = (config, getComponent) => (component) => {
87
349
  };
88
350
  const clamp = (value, min = state.min, max = state.max) => Math.min(Math.max(value, min), max);
89
351
  const updateHandlePositions = () => {
90
- const components = getComponents();
91
- const { handle, container, valueBubble, secondHandle, secondValueBubble } = components;
352
+ const { handle, container, valueBubble, secondHandle, secondValueBubble } = component;
92
353
  if (!handle || !container)
93
354
  return;
94
355
  const axis = axisNow();
@@ -99,7 +360,7 @@ export const withController = (config, getComponent) => (component) => {
99
360
  element.style.left = "auto";
100
361
  if (axis.start === "left")
101
362
  element.style.right = "";
102
- element.style[axis.start] = visualPosition(getPercentage(value));
363
+ element.style[axis.start] = visualPosition(value);
103
364
  element.style.transform = transform;
104
365
  };
105
366
  place(handle, state.value, handleTransform(axis));
@@ -125,8 +386,7 @@ export const withController = (config, getComponent) => (component) => {
125
386
  }
126
387
  };
127
388
  const updateValueBubbles = () => {
128
- const components = getComponents();
129
- const { valueBubble, secondValueBubble } = components;
389
+ const { valueBubble, secondValueBubble } = component;
130
390
  if (!valueBubble)
131
391
  return;
132
392
  const formatter = config.valueFormatter || ((value) => value.toString());
@@ -138,13 +398,9 @@ export const withController = (config, getComponent) => (component) => {
138
398
  const showValueBubble = (bubbleElement, show) => {
139
399
  if (!bubbleElement || !config.showValue)
140
400
  return;
141
- const bubbleClass = state.component.getClass("slider__value");
401
+ const bubbleClass = component.getClass("slider__value");
142
402
  bubbleElement.classList[show ? "add" : "remove"](`${bubbleClass}--visible`);
143
403
  };
144
- const generateTicks = () => {
145
- };
146
- const updateTicks = () => {
147
- };
148
404
  const SETTLE_MS = 450;
149
405
  let rendered = null;
150
406
  let settleTimer = null;
@@ -189,35 +445,348 @@ export const withController = (config, getComponent) => (component) => {
189
445
  console.warn("Error rendering UI:", error);
190
446
  }
191
447
  };
192
- const uiRenderer = {
193
- getPercentage,
194
- getValueFromPosition,
195
- roundToStep,
196
- clamp,
197
- showValueBubble,
198
- updateUi: render,
199
- render,
448
+ const pointerOf = config.orientation === "vertical" ? clientYOf : clientXOf;
449
+ const rangeBounds = (isSecondHandle) => !config.range || state.secondValue === null
450
+ ? [state.min, state.max]
451
+ : isSecondHandle
452
+ ? [state.value, state.max]
453
+ : [state.min, state.secondValue];
454
+ const { container, handle, valueBubble, secondHandle, secondValueBubble } = component;
455
+ const handleFor = (second) => (second ? secondHandle : handle) ?? null;
456
+ const bubbleFor = (second) => (second ? secondValueBubble : valueBubble) ?? null;
457
+ const disabled = () => !!component.disabled?.isDisabled();
458
+ let hasActualDragStarted = false;
459
+ let initialX = 0;
460
+ const DRAG_THRESHOLD = 3;
461
+ let lastFocusedHandle = null;
462
+ const clearBubbleHideTimer = () => {
463
+ if (state.valueHideTimer) {
464
+ clearTimeout(state.valueHideTimer);
465
+ state.valueHideTimer = null;
466
+ }
467
+ };
468
+ const hideAllBubbles = () => {
469
+ clearBubbleHideTimer();
470
+ if (valueBubble)
471
+ showValueBubble(valueBubble, false);
472
+ if (secondValueBubble)
473
+ showValueBubble(secondValueBubble, false);
474
+ };
475
+ const showActiveBubble = (bubble) => {
476
+ hideAllBubbles();
477
+ if (bubble && config.showValue)
478
+ showValueBubble(bubble, true);
479
+ };
480
+ const hideActiveBubble = (bubble, delay = 0) => {
481
+ clearBubbleHideTimer();
482
+ if (!bubble || !config.showValue)
483
+ return;
484
+ if (delay > 0) {
485
+ state.valueHideTimer = setTimeout(() => {
486
+ showValueBubble(bubble, false);
487
+ }, delay);
488
+ }
489
+ else {
490
+ showValueBubble(bubble, false);
491
+ }
492
+ };
493
+ const clearKeyboardFocus = () => {
494
+ if (handle)
495
+ handle.classList.remove(`${component.getClass("slider__handle")}--focused`);
496
+ if (secondHandle)
497
+ secondHandle.classList.remove(`${component.getClass("slider__handle")}--focused`);
498
+ try {
499
+ const focusClass = component.getClass("slider__handle--focused");
500
+ component.element
501
+ .querySelectorAll(`.${focusClass}`)
502
+ .forEach((el) => {
503
+ el.classList.remove(focusClass);
504
+ });
505
+ const active = activeElementOf(component.element);
506
+ if (active &&
507
+ component.element.contains(active) &&
508
+ active.classList.contains(component.getClass("slider__handle"))) {
509
+ active.blur();
510
+ }
511
+ }
512
+ catch (error) {
513
+ console.warn("Error clearing keyboard focus:", error);
514
+ }
515
+ };
516
+ const followPointer = () => {
517
+ document.addEventListener("mousemove", handleMouseMove);
518
+ document.addEventListener("mouseup", handleMouseUp);
519
+ document.addEventListener("touchmove", handleMouseMove, { passive: false });
520
+ document.addEventListener("touchend", handleMouseUp);
521
+ };
522
+ const stopFollowing = () => {
523
+ document.removeEventListener("mousemove", handleMouseMove);
524
+ document.removeEventListener("mouseup", handleMouseUp);
525
+ document.removeEventListener("touchmove", handleMouseMove);
526
+ document.removeEventListener("touchend", handleMouseUp);
527
+ };
528
+ const handleHandleMouseDown = (e, isSecondHandle = false) => {
529
+ if (disabled()) {
530
+ return;
531
+ }
532
+ if (e.type === "mousedown") {
533
+ e.preventDefault();
534
+ }
535
+ e.stopPropagation();
536
+ hideAllBubbles();
537
+ clearKeyboardFocus();
538
+ initialX = pointerOf(e);
539
+ state.dragging = false;
540
+ state.pressed = true;
541
+ hasActualDragStarted = false;
542
+ state.activeHandle = isSecondHandle ? "second" : "first";
543
+ state.activeBubble = bubbleFor(isSecondHandle);
544
+ showActiveBubble(state.activeBubble);
545
+ followPointer();
546
+ render();
547
+ emitEvent(SLIDER_EVENTS.START, e);
548
+ };
549
+ const handleTrackMouseDown = (e) => {
550
+ if (disabled() || !container) {
551
+ return;
552
+ }
553
+ if (e.type === "mousedown") {
554
+ e.preventDefault();
555
+ }
556
+ hideAllBubbles();
557
+ clearKeyboardFocus();
558
+ let isSecondHandle = false;
559
+ try {
560
+ const position = pointerOf(e);
561
+ let newValue = getValueFromPosition(position);
562
+ if (config.snapToSteps && state.step > 0)
563
+ newValue = roundToStep(newValue);
564
+ newValue = clamp(newValue, state.min, state.max);
565
+ if (config.range && state.secondValue !== null) {
566
+ const distToFirst = Math.abs(newValue - state.value);
567
+ const distToSecond = Math.abs(newValue - state.secondValue);
568
+ isSecondHandle = distToSecond < distToFirst;
569
+ isSecondHandle
570
+ ? (state.secondValue = newValue)
571
+ : (state.value = newValue);
572
+ }
573
+ else {
574
+ state.value = newValue;
575
+ }
576
+ render();
577
+ emitEvent(SLIDER_EVENTS.INPUT, e);
578
+ }
579
+ catch (error) {
580
+ console.warn("Error handling track click:", error);
581
+ }
582
+ state.activeHandle = isSecondHandle ? "second" : "first";
583
+ state.activeBubble = bubbleFor(isSecondHandle);
584
+ initialX = pointerOf(e);
585
+ if (config.centered && !config.range) {
586
+ showActiveBubble(state.activeBubble);
587
+ followPointer();
588
+ state.pressed = false;
589
+ emitEvent(SLIDER_EVENTS.START, e);
590
+ }
591
+ else {
592
+ state.dragging = false;
593
+ state.pressed = false;
594
+ hasActualDragStarted = false;
595
+ showActiveBubble(state.activeBubble);
596
+ followPointer();
597
+ render();
598
+ emitEvent(SLIDER_EVENTS.START, e);
599
+ }
600
+ };
601
+ const handleMouseMove = (e) => {
602
+ if (!state.activeHandle || !container)
603
+ return;
604
+ e.preventDefault();
605
+ try {
606
+ const currentX = pointerOf(e);
607
+ if (!hasActualDragStarted &&
608
+ Math.abs(currentX - initialX) >= DRAG_THRESHOLD) {
609
+ hasActualDragStarted = true;
610
+ state.dragging = true;
611
+ component.element.classList.add(`${component.getClass("slider")}--dragging`);
612
+ }
613
+ let newValue = getValueFromPosition(currentX);
614
+ if (config.snapToSteps && state.step > 0)
615
+ newValue = roundToStep(newValue);
616
+ newValue = clamp(newValue, state.min, state.max);
617
+ const isSecondHandle = state.activeHandle === "second";
618
+ if (config.range && state.secondValue !== null) {
619
+ const [low, high] = rangeBounds(isSecondHandle);
620
+ const clamped = Math.min(high, Math.max(low, newValue));
621
+ if (isSecondHandle)
622
+ state.secondValue = clamped;
623
+ else
624
+ state.value = clamped;
625
+ }
626
+ else {
627
+ state.value = newValue;
628
+ }
629
+ render();
630
+ emitEvent(SLIDER_EVENTS.INPUT, e);
631
+ }
632
+ catch (error) {
633
+ console.warn("Error during slider drag:", error);
634
+ }
635
+ };
636
+ const handleMouseUp = (e) => {
637
+ if (!state.activeHandle)
638
+ return;
639
+ e.preventDefault();
640
+ state.dragging = false;
641
+ state.pressed = false;
642
+ hasActualDragStarted = false;
643
+ component.element.classList.remove(`${component.getClass("slider")}--dragging`);
644
+ hideActiveBubble(state.activeBubble, 1000);
645
+ stopFollowing();
646
+ state.activeHandle = null;
647
+ render();
648
+ emitEvent(SLIDER_EVENTS.CHANGE, e);
649
+ emitEvent(SLIDER_EVENTS.END, e);
650
+ };
651
+ const handleKeyDown = (e, isSecondHandle = false) => {
652
+ if (disabled())
653
+ return;
654
+ const step = state.step || 1;
655
+ let newValue = isSecondHandle ? state.secondValue : state.value;
656
+ if (newValue === null)
657
+ return;
658
+ const stepSize = e.shiftKey ? step * 10 : step;
659
+ if (e.key === "Tab")
660
+ return;
661
+ const axis = getAxis(config, isRtl(component.element));
662
+ const sign = arrowSign(axis);
663
+ const intervals = Math.max(1, Math.floor((state.max - state.min) / step));
664
+ const page = Math.min(10, Math.max(1, Math.floor(intervals / 10))) * step;
665
+ const along = axis.vertical ? ["ArrowUp", "ArrowDown"] : ["ArrowRight", "ArrowLeft"];
666
+ let target;
667
+ if (e.key === along[0])
668
+ target = newValue + sign * stepSize;
669
+ else if (e.key === along[1])
670
+ target = newValue - sign * stepSize;
671
+ else if (e.key === "ArrowUp" || e.key === "ArrowRight")
672
+ target = newValue + stepSize;
673
+ else if (e.key === "ArrowDown" || e.key === "ArrowLeft")
674
+ target = newValue - stepSize;
675
+ else if (e.key === "PageUp")
676
+ target = newValue + (axis.vertical ? sign : 1) * page;
677
+ else if (e.key === "PageDown")
678
+ target = newValue - (axis.vertical ? sign : 1) * page;
679
+ else if (e.key === "Home")
680
+ target = state.min;
681
+ else if (e.key === "End")
682
+ target = state.max;
683
+ else
684
+ return;
685
+ e.preventDefault();
686
+ const [low, high] = rangeBounds(isSecondHandle);
687
+ newValue = Math.min(high, Math.max(low, target));
688
+ state.activeBubble = bubbleFor(isSecondHandle);
689
+ showActiveBubble(state.activeBubble);
690
+ if (isSecondHandle) {
691
+ state.secondValue = newValue;
692
+ }
693
+ else {
694
+ state.value = newValue;
695
+ }
696
+ render();
697
+ emitEvent(SLIDER_EVENTS.INPUT, e);
698
+ emitEvent(SLIDER_EVENTS.CHANGE, e);
699
+ };
700
+ const handleFocus = (e, isSecondHandle = false) => {
701
+ if (disabled())
702
+ return;
703
+ const currentHandle = handleFor(isSecondHandle);
704
+ if (lastFocusedHandle && lastFocusedHandle !== currentHandle) {
705
+ hideAllBubbles();
706
+ }
707
+ if (!currentHandle)
708
+ return;
709
+ lastFocusedHandle = currentHandle;
710
+ currentHandle.classList.add(`${component.getClass("slider__handle")}--focused`);
711
+ showActiveBubble(bubbleFor(isSecondHandle));
712
+ state.activeBubble = bubbleFor(isSecondHandle);
713
+ render();
714
+ emitEvent(SLIDER_EVENTS.FOCUS, e);
715
+ };
716
+ const handleBlur = (e, isSecondHandle = false) => {
717
+ const handleElement = handleFor(isSecondHandle);
718
+ if (!handleElement)
719
+ return;
720
+ handleElement.classList.remove(`${component.getClass("slider__handle")}--focused`);
721
+ render();
722
+ const relatedTarget = e.relatedTarget;
723
+ const otherHandle = handleFor(!isSecondHandle);
724
+ if (!relatedTarget || relatedTarget !== otherHandle) {
725
+ hideActiveBubble(bubbleFor(isSecondHandle), 200);
726
+ }
727
+ emitEvent(SLIDER_EVENTS.BLUR, e);
728
+ };
729
+ const listenersFor = (isSecondHandle) => ({
730
+ press: (e) => handleHandleMouseDown(e, isSecondHandle),
731
+ keydown: (e) => handleKeyDown(e, isSecondHandle),
732
+ focus: (e) => handleFocus(e, isSecondHandle),
733
+ blur: (e) => handleBlur(e, isSecondHandle),
734
+ });
735
+ const handleListeners = listenersFor(false);
736
+ const secondHandleListeners = listenersFor(true);
737
+ const attach = (element, listeners) => {
738
+ element.addEventListener("mousedown", listeners.press);
739
+ element.addEventListener("touchstart", listeners.press, { passive: true });
740
+ element.addEventListener("keydown", listeners.keydown);
741
+ element.addEventListener("focus", listeners.focus);
742
+ element.addEventListener("blur", listeners.blur);
743
+ };
744
+ const detach = (element, listeners) => {
745
+ element.removeEventListener("mousedown", listeners.press);
746
+ element.removeEventListener("touchstart", listeners.press);
747
+ element.removeEventListener("keydown", listeners.keydown);
748
+ element.removeEventListener("focus", listeners.focus);
749
+ element.removeEventListener("blur", listeners.blur);
750
+ };
751
+ const setupEventListeners = () => {
752
+ if (!handle || !container) {
753
+ console.warn("Cannot set up event listeners: missing container or handle");
754
+ return;
755
+ }
756
+ container.addEventListener("mousedown", handleTrackMouseDown);
757
+ container.addEventListener("touchstart", handleTrackMouseDown, {
758
+ passive: true,
759
+ });
760
+ attach(handle, handleListeners);
761
+ if (config.range && secondHandle)
762
+ attach(secondHandle, secondHandleListeners);
763
+ };
764
+ const cleanupEventListeners = () => {
765
+ if (container) {
766
+ container.removeEventListener("mousedown", handleTrackMouseDown);
767
+ container.removeEventListener("touchstart", handleTrackMouseDown);
768
+ }
769
+ if (handle)
770
+ detach(handle, handleListeners);
771
+ if (config.range && secondHandle)
772
+ detach(secondHandle, secondHandleListeners);
773
+ stopFollowing();
200
774
  };
201
- const handlers = createHandlers(config, state, uiRenderer, eventHelpers);
202
775
  const initController = () => {
203
776
  try {
204
- const components = getComponents();
205
- if (!components.handle) {
777
+ if (!handle) {
206
778
  console.warn("Cannot initialize slider controller: missing required handle component");
207
779
  return;
208
780
  }
209
- const { handle, secondHandle } = components;
210
- if (handle) {
211
- handle.setAttribute("aria-valuemin", String(state.min));
212
- handle.setAttribute("aria-valuemax", String(state.max));
213
- handle.setAttribute("aria-valuenow", String(state.value));
214
- if (config.range && secondHandle && state.secondValue !== null) {
215
- secondHandle.setAttribute("aria-valuemin", String(state.min));
216
- secondHandle.setAttribute("aria-valuemax", String(state.max));
217
- secondHandle.setAttribute("aria-valuenow", String(state.secondValue));
218
- }
781
+ handle.setAttribute("aria-valuemin", String(state.min));
782
+ handle.setAttribute("aria-valuemax", String(state.max));
783
+ handle.setAttribute("aria-valuenow", String(state.value));
784
+ if (config.range && secondHandle && state.secondValue !== null) {
785
+ secondHandle.setAttribute("aria-valuemin", String(state.min));
786
+ secondHandle.setAttribute("aria-valuemax", String(state.max));
787
+ secondHandle.setAttribute("aria-valuenow", String(state.secondValue));
219
788
  }
220
- handlers.setupEventListeners();
789
+ setupEventListeners();
221
790
  render();
222
791
  }
223
792
  catch (error) {
@@ -235,111 +804,176 @@ export const withController = (config, getComponent) => (component) => {
235
804
  clearTimeout(settleTimer);
236
805
  if (state.valueHideTimer !== null)
237
806
  clearTimeout(state.valueHideTimer);
238
- handlers.cleanupEventListeners();
807
+ cleanupEventListeners();
239
808
  originalDestroy.call(component.lifecycle);
240
809
  };
241
810
  }
242
- return {
243
- ...component,
244
- slider: {
245
- setValue(value, triggerEvent = false) {
246
- const ceiling = config.range && state.secondValue !== null ? state.secondValue : state.max;
247
- const newValue = clamp(roundToStep(clamp(value, state.min, state.max)), state.min, ceiling);
248
- state.value = newValue;
249
- render();
250
- if (triggerEvent) {
251
- eventHelpers.triggerEvent(SLIDER_EVENTS.CHANGE);
252
- }
253
- return this;
254
- },
255
- getValue() {
256
- return state.value;
257
- },
258
- setSecondValue(value, triggerEvent = false) {
259
- if (!config.range)
260
- return this;
261
- const newValue = clamp(roundToStep(clamp(value, state.min, state.max)), state.value, state.max);
262
- state.secondValue = newValue;
263
- render();
264
- if (triggerEvent) {
265
- eventHelpers.triggerEvent(SLIDER_EVENTS.CHANGE);
266
- }
267
- return this;
268
- },
269
- getSecondValue() {
270
- return config.range ? state.secondValue : null;
271
- },
272
- setMin(min) {
273
- state.min = min;
274
- const components = getComponents();
275
- if (components.handle) {
276
- components.handle.setAttribute("aria-valuemin", String(min));
277
- }
278
- if (config.range && components.secondHandle) {
279
- components.secondHandle.setAttribute("aria-valuemin", String(min));
280
- }
281
- if (state.value < min)
282
- state.value = min;
283
- if (config.range &&
284
- state.secondValue !== null &&
285
- state.secondValue < min) {
286
- state.secondValue = min;
287
- }
288
- if (config.ticks || config.tickLabels) {
289
- generateTicks();
290
- }
291
- render();
292
- return this;
293
- },
294
- getMin() {
295
- return state.min;
296
- },
297
- setMax(max) {
298
- state.max = max;
299
- const components = getComponents();
300
- if (components.handle) {
301
- components.handle.setAttribute("aria-valuemax", String(max));
302
- }
303
- if (config.range && components.secondHandle) {
304
- components.secondHandle.setAttribute("aria-valuemax", String(max));
305
- }
306
- if (state.value > max)
307
- state.value = max;
308
- if (config.range &&
309
- state.secondValue !== null &&
310
- state.secondValue > max) {
311
- state.secondValue = max;
312
- }
313
- if (config.ticks || config.tickLabels) {
314
- generateTicks();
315
- }
316
- render();
317
- return this;
318
- },
319
- getMax() {
320
- return state.max;
321
- },
322
- setStep(step) {
323
- state.step = step;
324
- render();
325
- component.element.classList[step > 0 ? "add" : "remove"](`${component.getClass("slider")}--discrete`);
326
- if (config.ticks || config.tickLabels) {
327
- generateTicks();
328
- updateTicks();
329
- }
330
- return this;
331
- },
332
- getStep() {
333
- return state.step;
334
- },
335
- regenerateTicks() {
336
- render();
337
- return this;
338
- },
339
- updateUi() {
340
- render();
811
+ const api = {
812
+ element: component.element,
813
+ setValue(value, triggerEvent = false) {
814
+ const ceiling = config.range && state.secondValue !== null ? state.secondValue : state.max;
815
+ const newValue = clamp(roundToStep(clamp(value, state.min, state.max)), state.min, ceiling);
816
+ state.value = newValue;
817
+ render();
818
+ if (triggerEvent) {
819
+ emitEvent(SLIDER_EVENTS.CHANGE);
820
+ }
821
+ return this;
822
+ },
823
+ getValue() {
824
+ return state.value;
825
+ },
826
+ setSecondValue(value, triggerEvent = false) {
827
+ if (!config.range)
341
828
  return this;
342
- },
829
+ const newValue = clamp(roundToStep(clamp(value, state.min, state.max)), state.value, state.max);
830
+ state.secondValue = newValue;
831
+ render();
832
+ if (triggerEvent) {
833
+ emitEvent(SLIDER_EVENTS.CHANGE);
834
+ }
835
+ return this;
836
+ },
837
+ getSecondValue() {
838
+ return config.range ? state.secondValue : null;
839
+ },
840
+ setMin(min) {
841
+ state.min = min;
842
+ if (handle) {
843
+ handle.setAttribute("aria-valuemin", String(min));
844
+ }
845
+ if (config.range && secondHandle) {
846
+ secondHandle.setAttribute("aria-valuemin", String(min));
847
+ }
848
+ if (state.value < min)
849
+ state.value = min;
850
+ if (config.range &&
851
+ state.secondValue !== null &&
852
+ state.secondValue < min) {
853
+ state.secondValue = min;
854
+ }
855
+ render();
856
+ return this;
857
+ },
858
+ getMin() {
859
+ return state.min;
860
+ },
861
+ setMax(max) {
862
+ state.max = max;
863
+ if (handle) {
864
+ handle.setAttribute("aria-valuemax", String(max));
865
+ }
866
+ if (config.range && secondHandle) {
867
+ secondHandle.setAttribute("aria-valuemax", String(max));
868
+ }
869
+ if (state.value > max)
870
+ state.value = max;
871
+ if (config.range &&
872
+ state.secondValue !== null &&
873
+ state.secondValue > max) {
874
+ state.secondValue = max;
875
+ }
876
+ render();
877
+ return this;
878
+ },
879
+ getMax() {
880
+ return state.max;
881
+ },
882
+ setStep(step) {
883
+ state.step = step;
884
+ render();
885
+ component.element.classList[step > 0 ? "add" : "remove"](`${component.getClass("slider")}--discrete`);
886
+ return this;
887
+ },
888
+ getStep() {
889
+ return state.step;
890
+ },
891
+ enable() {
892
+ component.disabled?.enable();
893
+ return this;
343
894
  },
895
+ disable() {
896
+ component.disabled?.disable();
897
+ return this;
898
+ },
899
+ isDisabled() {
900
+ return component.disabled?.isDisabled() ?? false;
901
+ },
902
+ setColor(color) {
903
+ component.appearance.setColor(color);
904
+ return this;
905
+ },
906
+ getColor() {
907
+ return component.appearance.getColor();
908
+ },
909
+ setSize(size) {
910
+ component.setSize?.(size);
911
+ return this;
912
+ },
913
+ getSize() {
914
+ return component.getSize?.() ?? "XS";
915
+ },
916
+ showTicks(show) {
917
+ component.appearance.showTicks(show);
918
+ return this;
919
+ },
920
+ showCurrentValue(show) {
921
+ component.appearance.showCurrentValue(show);
922
+ return this;
923
+ },
924
+ setLabel(text) {
925
+ component.label?.setText(text);
926
+ return this;
927
+ },
928
+ getLabel() {
929
+ return component.label?.getText() ?? "";
930
+ },
931
+ setInsetIcon(icon, atMin) {
932
+ component.setInsetIcon?.(icon, atMin);
933
+ return this;
934
+ },
935
+ setIcon(iconHtml) {
936
+ component.icon?.setIcon(iconHtml);
937
+ return this;
938
+ },
939
+ getIcon() {
940
+ return component.icon?.getIcon() ?? "";
941
+ },
942
+ on(event, handler) {
943
+ component.on?.(event, handler);
944
+ return this;
945
+ },
946
+ off(event, handler) {
947
+ component.off?.(event, handler);
948
+ return this;
949
+ },
950
+ destroy() {
951
+ component.lifecycle?.destroy?.();
952
+ },
953
+ };
954
+ const slider = {
955
+ setValue: api.setValue,
956
+ getValue: api.getValue,
957
+ setSecondValue: api.setSecondValue,
958
+ getSecondValue: api.getSecondValue,
959
+ setMin: api.setMin,
960
+ getMin: api.getMin,
961
+ setMax: api.setMax,
962
+ getMax: api.getMax,
963
+ setStep: api.setStep,
964
+ getStep: api.getStep,
965
+ regenerateTicks() {
966
+ render();
967
+ return this;
968
+ },
969
+ updateUi() {
970
+ render();
971
+ return this;
972
+ },
973
+ };
974
+ return {
975
+ ...component,
976
+ ...api,
977
+ slider,
344
978
  };
345
979
  };