mtrl 0.2.2 → 0.2.4

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 (97) hide show
  1. package/.typedocignore +11 -0
  2. package/DOCS.md +153 -0
  3. package/index.ts +18 -3
  4. package/package.json +7 -2
  5. package/src/components/badge/_styles.scss +174 -0
  6. package/src/components/badge/api.ts +292 -0
  7. package/src/components/badge/badge.ts +52 -0
  8. package/src/components/badge/config.ts +68 -0
  9. package/src/components/badge/constants.ts +30 -0
  10. package/src/components/badge/features.ts +185 -0
  11. package/src/components/badge/index.ts +4 -0
  12. package/src/components/badge/types.ts +105 -0
  13. package/src/components/button/types.ts +174 -29
  14. package/src/components/carousel/_styles.scss +645 -0
  15. package/src/components/carousel/api.ts +147 -0
  16. package/src/components/carousel/carousel.ts +178 -0
  17. package/src/components/carousel/config.ts +91 -0
  18. package/src/components/carousel/constants.ts +95 -0
  19. package/src/components/carousel/features/drag.ts +388 -0
  20. package/src/components/carousel/features/index.ts +8 -0
  21. package/src/components/carousel/features/slides.ts +682 -0
  22. package/src/components/carousel/index.ts +38 -0
  23. package/src/components/carousel/types.ts +327 -0
  24. package/src/components/dialog/_styles.scss +213 -0
  25. package/src/components/dialog/api.ts +283 -0
  26. package/src/components/dialog/config.ts +113 -0
  27. package/src/components/dialog/constants.ts +32 -0
  28. package/src/components/dialog/dialog.ts +56 -0
  29. package/src/components/dialog/features.ts +713 -0
  30. package/src/components/dialog/index.ts +15 -0
  31. package/src/components/dialog/types.ts +221 -0
  32. package/src/components/progress/_styles.scss +13 -1
  33. package/src/components/progress/api.ts +2 -2
  34. package/src/components/progress/progress.ts +2 -2
  35. package/src/components/progress/types.ts +3 -0
  36. package/src/components/radios/_styles.scss +232 -0
  37. package/src/components/radios/api.ts +100 -0
  38. package/src/components/radios/config.ts +60 -0
  39. package/src/components/radios/constants.ts +28 -0
  40. package/src/components/radios/index.ts +4 -0
  41. package/src/components/radios/radio.ts +269 -0
  42. package/src/components/radios/radios.ts +42 -0
  43. package/src/components/radios/types.ts +232 -0
  44. package/src/components/sheet/_styles.scss +236 -0
  45. package/src/components/sheet/api.ts +96 -0
  46. package/src/components/sheet/config.ts +66 -0
  47. package/src/components/sheet/constants.ts +20 -0
  48. package/src/components/sheet/features/content.ts +51 -0
  49. package/src/components/sheet/features/gestures.ts +177 -0
  50. package/src/components/sheet/features/index.ts +6 -0
  51. package/src/components/sheet/features/position.ts +42 -0
  52. package/src/components/sheet/features/state.ts +116 -0
  53. package/src/components/sheet/features/title.ts +86 -0
  54. package/src/components/sheet/index.ts +4 -0
  55. package/src/components/sheet/sheet.ts +57 -0
  56. package/src/components/sheet/types.ts +266 -0
  57. package/src/components/slider/_styles.scss +518 -0
  58. package/src/components/slider/api.ts +336 -0
  59. package/src/components/slider/config.ts +145 -0
  60. package/src/components/slider/constants.ts +28 -0
  61. package/src/components/slider/features/appearance.ts +140 -0
  62. package/src/components/slider/features/disabled.ts +43 -0
  63. package/src/components/slider/features/events.ts +164 -0
  64. package/src/components/slider/features/index.ts +5 -0
  65. package/src/components/slider/features/interactions.ts +256 -0
  66. package/src/components/slider/features/keyboard.ts +114 -0
  67. package/src/components/slider/features/slider.ts +336 -0
  68. package/src/components/slider/features/structure.ts +264 -0
  69. package/src/components/slider/features/ui.ts +518 -0
  70. package/src/components/slider/index.ts +9 -0
  71. package/src/components/slider/slider.ts +58 -0
  72. package/src/components/slider/types.ts +166 -0
  73. package/src/components/tabs/_styles.scss +224 -0
  74. package/src/components/tabs/api.ts +443 -0
  75. package/src/components/tabs/config.ts +80 -0
  76. package/src/components/tabs/constants.ts +12 -0
  77. package/src/components/tabs/index.ts +4 -0
  78. package/src/components/tabs/tabs.ts +52 -0
  79. package/src/components/tabs/types.ts +247 -0
  80. package/src/components/textfield/_styles.scss +97 -4
  81. package/src/components/tooltip/_styles.scss +241 -0
  82. package/src/components/tooltip/api.ts +411 -0
  83. package/src/components/tooltip/config.ts +78 -0
  84. package/src/components/tooltip/constants.ts +27 -0
  85. package/src/components/tooltip/index.ts +4 -0
  86. package/src/components/tooltip/tooltip.ts +60 -0
  87. package/src/components/tooltip/types.ts +178 -0
  88. package/src/core/build/_ripple.scss +79 -0
  89. package/src/core/build/constants.ts +48 -0
  90. package/src/core/build/icon.ts +137 -0
  91. package/src/core/build/ripple.ts +216 -0
  92. package/src/core/build/text.ts +91 -0
  93. package/src/index.ts +9 -1
  94. package/src/styles/abstract/_variables.scss +24 -12
  95. package/tsconfig.json +22 -0
  96. package/typedoc.json +28 -0
  97. package/typedoc.simple.json +14 -0
@@ -0,0 +1,518 @@
1
+ // src/components/slider/features/ui.ts - Updated track update methods
2
+ import { SLIDER_ORIENTATIONS } from '../constants';
3
+ import { SliderConfig } from '../types';
4
+
5
+ /**
6
+ * Create UI update helpers for slider component with MD3 enhancements
7
+ *
8
+ * @param config Slider configuration
9
+ * @param state Slider state object
10
+ * @returns UI update helper methods
11
+ */
12
+ export const createUiHelpers = (config: SliderConfig, state) => {
13
+ // Make sure state.component.structure exists
14
+ if (!state.component || !state.component.structure) {
15
+ console.error('Cannot create UI helpers: component structure is missing');
16
+ return {
17
+ getPercentage: () => 0,
18
+ getValueFromPosition: () => 0,
19
+ roundToStep: (value) => value,
20
+ clamp: (value, min, max) => Math.min(Math.max(value, min), max),
21
+ setThumbPosition: () => {},
22
+ updateActiveTrack: () => {},
23
+ updateStartTrack: () => {},
24
+ updateRemainingTrack: () => {},
25
+ updateThumbPositions: () => {},
26
+ updateValueBubbles: () => {},
27
+ showValueBubble: () => {},
28
+ generateTicks: () => {},
29
+ updateTicks: () => {},
30
+ updateUi: () => {}
31
+ };
32
+ }
33
+
34
+ const {
35
+ track,
36
+ activeTrack,
37
+ startTrack,
38
+ remainingTrack,
39
+ thumb,
40
+ valueBubble,
41
+ secondThumb,
42
+ secondValueBubble
43
+ } = state.component.structure;
44
+
45
+ /**
46
+ * Calculates percentage position for a value
47
+ * @param value Value to convert to percentage
48
+ * @returns Percentage position (0-100)
49
+ */
50
+ const getPercentage = (value) => {
51
+ const range = state.max - state.min;
52
+ if (range === 0) return 0;
53
+ return ((value - state.min) / range) * 100;
54
+ };
55
+
56
+ /**
57
+ * Gets slider value from a position on the track
58
+ * @param position Screen coordinate (clientX/clientY)
59
+ * @param vertical Whether slider is vertical
60
+ * @returns Calculated value
61
+ */
62
+ const getValueFromPosition = (position, vertical = false) => {
63
+ if (!track) return state.min;
64
+
65
+ try {
66
+ const trackRect = track.getBoundingClientRect();
67
+ const range = state.max - state.min;
68
+
69
+ if (vertical) {
70
+ const trackHeight = trackRect.height;
71
+ // For vertical sliders, 0% is at the bottom, 100% at the top
72
+ const percentageFromBottom = 1 - ((position - trackRect.top) / trackHeight);
73
+ // Clamp percentage between 0 and 1
74
+ const clampedPercentage = Math.max(0, Math.min(1, percentageFromBottom));
75
+ return state.min + clampedPercentage * range;
76
+ } else {
77
+ const trackWidth = trackRect.width;
78
+ // For horizontal sliders, 0% is at the left, 100% at the right
79
+ const percentageFromLeft = (position - trackRect.left) / trackWidth;
80
+ // Clamp percentage between 0 and 1
81
+ const clampedPercentage = Math.max(0, Math.min(1, percentageFromLeft));
82
+ return state.min + clampedPercentage * range;
83
+ }
84
+ } catch (error) {
85
+ console.warn('Error calculating value from position:', error);
86
+ return state.min;
87
+ }
88
+ };
89
+
90
+ /**
91
+ * Rounds a value to the nearest step
92
+ * @param value Value to round
93
+ * @returns Rounded value
94
+ */
95
+ const roundToStep = (value) => {
96
+ const step = state.step;
97
+ if (step <= 0) return value;
98
+
99
+ const steps = Math.round((value - state.min) / step);
100
+ return state.min + (steps * step);
101
+ };
102
+
103
+ /**
104
+ * Clamps a value between min and max
105
+ * @param value Value to clamp
106
+ * @param min Minimum allowed value
107
+ * @param max Maximum allowed value
108
+ * @returns Clamped value
109
+ */
110
+ const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
111
+
112
+ /**
113
+ * Sets thumb position based on a value percentage
114
+ * @param thumbElement Thumb element to position
115
+ * @param valueBubbleElement Value bubble element to position
116
+ * @param valuePercent Percentage position (0-100)
117
+ */
118
+ const setThumbPosition = (thumbElement, valueBubbleElement, valuePercent) => {
119
+ if (!thumbElement) return;
120
+
121
+ if (config.orientation === SLIDER_ORIENTATIONS.VERTICAL) {
122
+ // Fix for vertical slider - position from bottom using absolute positioning
123
+ thumbElement.style.bottom = `${valuePercent}%`;
124
+ thumbElement.style.left = '50%'; // Keep it centered horizontally
125
+ thumbElement.style.top = 'auto'; // Clear top property to prevent conflicts
126
+
127
+ if (valueBubbleElement) {
128
+ valueBubbleElement.style.bottom = `${valuePercent}%`;
129
+ valueBubbleElement.style.top = 'auto';
130
+ }
131
+ } else {
132
+ thumbElement.style.left = `${valuePercent}%`;
133
+ if (valueBubbleElement) {
134
+ valueBubbleElement.style.left = `${valuePercent}%`;
135
+ }
136
+ }
137
+ };
138
+
139
+
140
+
141
+ /**
142
+ * Updates start track styles (before the first thumb for range slider)
143
+ * This method now includes padding adjustments in the calculation
144
+ */
145
+ const updateStartTrack = () => {
146
+ if (!startTrack) return;
147
+
148
+ if (config.range && state.secondValue !== null) {
149
+ // For range slider, calculate the track from start to first thumb
150
+ const lowerValue = Math.min(state.value, state.secondValue);
151
+ const lowerPercent = getPercentage(lowerValue);
152
+
153
+ // Get track width for pixel calculations
154
+ const trackRect = track.getBoundingClientRect();
155
+ const isVertical = config.orientation === SLIDER_ORIENTATIONS.VERTICAL;
156
+ const trackSize = isVertical ? trackRect.height : trackRect.width;
157
+
158
+ // Calculate width with adjustment for thumb spacing - subtract 8px equivalent
159
+ const paddingAdjustment = 8; // 8px padding
160
+ const paddingPercent = (paddingAdjustment / trackSize) * 100;
161
+
162
+ if (isVertical) {
163
+ startTrack.style.display = 'block';
164
+ startTrack.style.height = `${Math.max(0, lowerPercent - paddingPercent)}%`;
165
+ startTrack.style.bottom = '0';
166
+ startTrack.style.top = 'auto';
167
+ startTrack.style.width = '100%';
168
+ startTrack.style.left = '0';
169
+ } else {
170
+ startTrack.style.display = 'block';
171
+ startTrack.style.width = `${Math.max(0, lowerPercent - paddingPercent)}%`;
172
+ startTrack.style.left = '0';
173
+ startTrack.style.height = '100%';
174
+ }
175
+ } else {
176
+ // For single thumb slider, no start track needed
177
+ startTrack.style.display = 'none';
178
+ }
179
+ };
180
+
181
+ /**
182
+ * Updates active track styles - with padding adjustments
183
+ */
184
+ const updateActiveTrack = () => {
185
+ if (!activeTrack) return;
186
+
187
+ // Get track width for pixel calculations
188
+ const trackRect = track.getBoundingClientRect();
189
+ const isVertical = config.orientation === SLIDER_ORIENTATIONS.VERTICAL;
190
+ const trackSize = isVertical ? trackRect.height : trackRect.width;
191
+
192
+ // Calculate padding adjustment
193
+ const paddingAdjustment = 8; // 8px padding
194
+ const paddingPercent = (paddingAdjustment / trackSize) * 100;
195
+
196
+ if (config.range && state.secondValue !== null) {
197
+ // Range slider (two thumbs)
198
+ const lowerValue = Math.min(state.value, state.secondValue);
199
+ const higherValue = Math.max(state.value, state.secondValue);
200
+ const lowerPercent = getPercentage(lowerValue);
201
+ const higherPercent = getPercentage(higherValue);
202
+
203
+ // Adjust positions and width to account for spacing
204
+ let adjustedLowerPercent = lowerPercent;
205
+ let adjustedHigherPercent = higherPercent;
206
+
207
+ if (higherPercent - lowerPercent > paddingPercent * 2) {
208
+ // If there's enough space, add padding to both sides
209
+ adjustedLowerPercent = lowerPercent + paddingPercent;
210
+ adjustedHigherPercent = higherPercent - paddingPercent;
211
+ }
212
+
213
+ const trackLength = Math.max(0, adjustedHigherPercent - adjustedLowerPercent);
214
+
215
+ if (isVertical) {
216
+ activeTrack.style.display = 'block';
217
+ activeTrack.style.height = `${trackLength}%`;
218
+ activeTrack.style.bottom = `${adjustedLowerPercent}%`;
219
+ activeTrack.style.top = 'auto';
220
+ activeTrack.style.width = '100%';
221
+ } else {
222
+ activeTrack.style.display = 'block';
223
+ activeTrack.style.width = `${trackLength}%`;
224
+ activeTrack.style.left = `${adjustedLowerPercent}%`;
225
+ activeTrack.style.height = '100%';
226
+ }
227
+ } else {
228
+ // Single thumb slider
229
+ const percent = getPercentage(state.value);
230
+
231
+ // For single slider, adjust for left padding
232
+ const adjustedWidth = Math.max(0, percent - paddingPercent);
233
+
234
+ if (isVertical) {
235
+ activeTrack.style.display = 'block';
236
+ activeTrack.style.height = `${adjustedWidth}%`;
237
+ activeTrack.style.bottom = '0';
238
+ activeTrack.style.top = 'auto';
239
+ activeTrack.style.width = '100%';
240
+ } else {
241
+ activeTrack.style.display = 'block';
242
+ activeTrack.style.width = `${adjustedWidth}%`;
243
+ activeTrack.style.left = '0';
244
+ activeTrack.style.height = '100%';
245
+ }
246
+ }
247
+ };
248
+
249
+ /**
250
+ * Updates remaining track styles - with padding adjustments
251
+ */
252
+ const updateRemainingTrack = () => {
253
+ if (!remainingTrack) return;
254
+
255
+ // Get track width for pixel calculations
256
+ const trackRect = track.getBoundingClientRect();
257
+ const isVertical = config.orientation === SLIDER_ORIENTATIONS.VERTICAL;
258
+ const trackSize = isVertical ? trackRect.height : trackRect.width;
259
+
260
+ // Calculate padding adjustment
261
+ const paddingAdjustment = 8; // 8px padding
262
+ const paddingPercent = (paddingAdjustment / trackSize) * 100;
263
+
264
+ if (config.range && state.secondValue !== null) {
265
+ // Range slider (two thumbs)
266
+ const higherValue = Math.max(state.value, state.secondValue);
267
+ const higherPercent = getPercentage(higherValue);
268
+
269
+ // Adjust for right padding
270
+ const adjustedPercent = higherPercent + paddingPercent;
271
+ const adjustedWidth = Math.max(0, 100 - adjustedPercent);
272
+
273
+ if (isVertical) {
274
+ remainingTrack.style.display = 'block';
275
+ remainingTrack.style.height = `${adjustedWidth}%`;
276
+ remainingTrack.style.bottom = `${adjustedPercent}%`;
277
+ remainingTrack.style.top = 'auto';
278
+ remainingTrack.style.width = '100%';
279
+ } else {
280
+ remainingTrack.style.display = 'block';
281
+ remainingTrack.style.width = `${adjustedWidth}%`;
282
+ remainingTrack.style.left = `${adjustedPercent}%`;
283
+ remainingTrack.style.height = '100%';
284
+ }
285
+ } else {
286
+ // Single thumb slider
287
+ const percent = getPercentage(state.value);
288
+
289
+ // Adjust for right padding
290
+ const adjustedPercent = percent + paddingPercent;
291
+ const adjustedWidth = Math.max(0, 100 - adjustedPercent);
292
+
293
+ if (isVertical) {
294
+ remainingTrack.style.display = 'block';
295
+ remainingTrack.style.height = `${adjustedWidth}%`;
296
+ remainingTrack.style.bottom = `${adjustedPercent}%`;
297
+ remainingTrack.style.top = 'auto';
298
+ remainingTrack.style.width = '100%';
299
+ } else {
300
+ remainingTrack.style.display = 'block';
301
+ remainingTrack.style.width = `${adjustedWidth}%`;
302
+ remainingTrack.style.left = `${adjustedPercent}%`;
303
+ remainingTrack.style.height = '100%';
304
+ }
305
+ }
306
+ };
307
+
308
+ /**
309
+ * Updates thumb positions
310
+ */
311
+ const updateThumbPositions = () => {
312
+ if (!thumb) return;
313
+
314
+ // Update main thumb
315
+ const percent = getPercentage(state.value);
316
+ setThumbPosition(thumb, valueBubble, percent);
317
+
318
+ // Update second thumb if range slider
319
+ if (config.range && secondThumb && secondValueBubble && state.secondValue !== null) {
320
+ const secondPercent = getPercentage(state.secondValue);
321
+ setThumbPosition(secondThumb, secondValueBubble, secondPercent);
322
+ }
323
+
324
+ // Update ARIA attributes
325
+ thumb.setAttribute('aria-valuenow', String(state.value));
326
+ if (config.range && secondThumb && state.secondValue !== null) {
327
+ secondThumb.setAttribute('aria-valuenow', String(state.secondValue));
328
+ }
329
+ };
330
+
331
+ /**
332
+ * Updates value bubble content
333
+ */
334
+ const updateValueBubbles = () => {
335
+ if (!valueBubble) return;
336
+
337
+ // Format the values
338
+ const formatter = config.valueFormatter || (value => value.toString());
339
+ const formattedValue = formatter(state.value);
340
+
341
+ // Update main value bubble
342
+ valueBubble.textContent = formattedValue;
343
+
344
+ // Update second value bubble if range slider
345
+ if (config.range && secondValueBubble && state.secondValue !== null) {
346
+ const formattedSecondValue = formatter(state.secondValue);
347
+ secondValueBubble.textContent = formattedSecondValue;
348
+ }
349
+ };
350
+
351
+ /**
352
+ * Shows or hides value bubble
353
+ * @param bubbleElement Value bubble element
354
+ * @param show Whether to show the bubble
355
+ */
356
+ const showValueBubble = (bubbleElement, show) => {
357
+ if (!bubbleElement || !config.showValue) return;
358
+
359
+ if (show) {
360
+ bubbleElement.classList.add(`${state.component.getClass('slider-value')}--visible`);
361
+ } else {
362
+ bubbleElement.classList.remove(`${state.component.getClass('slider-value')}--visible`);
363
+ }
364
+ };
365
+
366
+ /**
367
+ * Generates tick marks and labels
368
+ */
369
+ const generateTicks = () => {
370
+ // Remove existing ticks
371
+ state.ticks.forEach(tick => {
372
+ if (tick.parentNode) {
373
+ tick.parentNode.removeChild(tick);
374
+ }
375
+ });
376
+
377
+ state.tickLabels.forEach(label => {
378
+ if (label.parentNode) {
379
+ label.parentNode.removeChild(label);
380
+ }
381
+ });
382
+
383
+ state.ticks = [];
384
+ state.tickLabels = [];
385
+
386
+ if (!config.ticks && !config.tickLabels) return;
387
+
388
+ // Generate new ticks
389
+ const numSteps = Math.floor((state.max - state.min) / state.step);
390
+ const tickValues = [];
391
+
392
+ for (let i = 0; i <= numSteps; i++) {
393
+ const value = state.min + (i * state.step);
394
+ if (value <= state.max) {
395
+ tickValues.push(value);
396
+ }
397
+ }
398
+
399
+ // Ensure max value is included
400
+ if (tickValues[tickValues.length - 1] !== state.max) {
401
+ tickValues.push(state.max);
402
+ }
403
+
404
+ // Create ticks
405
+ tickValues.forEach(value => {
406
+ if (config.ticks) {
407
+ const tick = document.createElement('div');
408
+ tick.classList.add(state.component.getClass('slider-tick'));
409
+
410
+ // Position tick
411
+ const percent = getPercentage(value);
412
+ if (config.orientation === SLIDER_ORIENTATIONS.VERTICAL) {
413
+ tick.style.bottom = `${percent}%`;
414
+ } else {
415
+ tick.style.left = `${percent}%`;
416
+ }
417
+
418
+ // Set active class if value is in active range
419
+ if (config.range && state.secondValue !== null) {
420
+ const lowerValue = Math.min(state.value, state.secondValue);
421
+ const higherValue = Math.max(state.value, state.secondValue);
422
+
423
+ if (value >= lowerValue && value <= higherValue) {
424
+ tick.classList.add(`${state.component.getClass('slider-tick')}--active`);
425
+ }
426
+ } else if (value <= state.value) {
427
+ tick.classList.add(`${state.component.getClass('slider-tick')}--active`);
428
+ }
429
+
430
+ state.component.element.appendChild(tick);
431
+ state.ticks.push(tick);
432
+ }
433
+
434
+ if (config.tickLabels) {
435
+ const label = document.createElement('div');
436
+ label.classList.add(state.component.getClass('slider-label'));
437
+
438
+ // Position label
439
+ const percent = getPercentage(value);
440
+ if (config.orientation === SLIDER_ORIENTATIONS.VERTICAL) {
441
+ label.style.bottom = `${percent}%`;
442
+ } else {
443
+ label.style.left = `${percent}%`;
444
+ }
445
+
446
+ // Set label text
447
+ if (Array.isArray(config.tickLabels) && config.tickLabels[tickValues.indexOf(value)]) {
448
+ label.textContent = config.tickLabels[tickValues.indexOf(value)];
449
+ } else {
450
+ const formatter = config.valueFormatter || (value => value.toString());
451
+ label.textContent = formatter(value);
452
+ }
453
+
454
+ state.component.element.appendChild(label);
455
+ state.tickLabels.push(label);
456
+ }
457
+ });
458
+ };
459
+
460
+ /**
461
+ * Updates active state of tick marks
462
+ */
463
+ const updateTicks = () => {
464
+ // Update active ticks based on current value
465
+ state.ticks.forEach((tick, index) => {
466
+ const tickValue = state.min + (index * state.step);
467
+
468
+ if (config.range && state.secondValue !== null) {
469
+ // Range slider - ticks between the two values should be active
470
+ const lowerValue = Math.min(state.value, state.secondValue);
471
+ const higherValue = Math.max(state.value, state.secondValue);
472
+
473
+ if (tickValue >= lowerValue && tickValue <= higherValue) {
474
+ tick.classList.add(`${state.component.getClass('slider-tick')}--active`);
475
+ } else {
476
+ tick.classList.remove(`${state.component.getClass('slider-tick')}--active`);
477
+ }
478
+ } else {
479
+ // Single slider - ticks below or equal to value should be active
480
+ if (tickValue <= state.value) {
481
+ tick.classList.add(`${state.component.getClass('slider-tick')}--active`);
482
+ } else {
483
+ tick.classList.remove(`${state.component.getClass('slider-tick')}--active`);
484
+ }
485
+ }
486
+ });
487
+ };
488
+
489
+ /**
490
+ * Updates all UI elements
491
+ */
492
+ const updateUi = () => {
493
+ updateThumbPositions();
494
+ updateStartTrack(); // Call BEFORE updateActiveTrack
495
+ updateActiveTrack();
496
+ updateRemainingTrack();
497
+ updateValueBubbles();
498
+ updateTicks();
499
+ };
500
+
501
+ // Return helper methods
502
+ return {
503
+ getPercentage,
504
+ getValueFromPosition,
505
+ roundToStep,
506
+ clamp,
507
+ setThumbPosition,
508
+ updateActiveTrack,
509
+ updateStartTrack,
510
+ updateRemainingTrack,
511
+ updateThumbPositions,
512
+ updateValueBubbles,
513
+ showValueBubble,
514
+ generateTicks,
515
+ updateTicks,
516
+ updateUi
517
+ };
518
+ };
@@ -0,0 +1,9 @@
1
+ // src/components/slider/index.ts
2
+ export { default } from './slider';
3
+ export {
4
+ SLIDER_COLORS,
5
+ SLIDER_SIZES,
6
+ SLIDER_ORIENTATIONS,
7
+ SLIDER_EVENTS
8
+ } from './constants';
9
+ export { SliderConfig, SliderComponent, SliderEvent } from './types';
@@ -0,0 +1,58 @@
1
+ // src/components/slider/slider.ts
2
+ import { pipe } from '../../core/compose';
3
+ import { createBase, withElement } from '../../core/compose/component';
4
+ import { withEvents, withLifecycle } from '../../core/compose/features';
5
+ import {
6
+ withStructure,
7
+ withDisabled,
8
+ withAppearance,
9
+ withSlider
10
+ } from './features';
11
+ import { withAPI } from './api';
12
+ import { SliderConfig, SliderComponent } from './types';
13
+ import { createBaseConfig, getElementConfig, getApiConfig } from './config';
14
+
15
+ /**
16
+ * Creates a new Slider component
17
+ * @param {SliderConfig} config - Slider configuration object
18
+ * @returns {SliderComponent} Slider component instance
19
+ */
20
+ const createSlider = (config: SliderConfig = {}): SliderComponent => {
21
+ const baseConfig = createBaseConfig(config);
22
+
23
+ try {
24
+ // First create the component with all required features
25
+ const component = pipe(
26
+ createBase,
27
+ withEvents(),
28
+ withElement(getElementConfig(baseConfig)),
29
+ withStructure(baseConfig),
30
+ withDisabled(baseConfig),
31
+ withAppearance(baseConfig),
32
+ withSlider(baseConfig),
33
+ withLifecycle()
34
+ )(baseConfig);
35
+
36
+ // Then generate the API configuration using the component
37
+ const apiOptions = getApiConfig(component);
38
+
39
+ // Finally, apply the API layer with the generated options
40
+ const slider = withAPI(apiOptions)(component);
41
+
42
+ // Register event handlers from config
43
+ if (baseConfig.on && typeof slider.on === 'function') {
44
+ Object.entries(baseConfig.on).forEach(([event, handler]) => {
45
+ if (typeof handler === 'function') {
46
+ slider.on(event, handler);
47
+ }
48
+ });
49
+ }
50
+
51
+ return slider;
52
+ } catch (error) {
53
+ console.error('Slider creation error:', error);
54
+ throw new Error(`Failed to create slider: ${(error as Error).message}`);
55
+ }
56
+ };
57
+
58
+ export default createSlider;