mtrl 0.2.2 → 0.2.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.
- package/.typedocignore +11 -0
- package/DOCS.md +153 -0
- package/index.ts +18 -3
- package/package.json +7 -2
- package/src/components/badge/_styles.scss +174 -0
- package/src/components/badge/api.ts +292 -0
- package/src/components/badge/badge.ts +52 -0
- package/src/components/badge/config.ts +68 -0
- package/src/components/badge/constants.ts +30 -0
- package/src/components/badge/features.ts +185 -0
- package/src/components/badge/index.ts +4 -0
- package/src/components/badge/types.ts +105 -0
- package/src/components/button/types.ts +174 -29
- package/src/components/carousel/_styles.scss +645 -0
- package/src/components/carousel/api.ts +147 -0
- package/src/components/carousel/carousel.ts +178 -0
- package/src/components/carousel/config.ts +91 -0
- package/src/components/carousel/constants.ts +95 -0
- package/src/components/carousel/features/drag.ts +388 -0
- package/src/components/carousel/features/index.ts +8 -0
- package/src/components/carousel/features/slides.ts +682 -0
- package/src/components/carousel/index.ts +38 -0
- package/src/components/carousel/types.ts +327 -0
- package/src/components/dialog/_styles.scss +213 -0
- package/src/components/dialog/api.ts +283 -0
- package/src/components/dialog/config.ts +113 -0
- package/src/components/dialog/constants.ts +32 -0
- package/src/components/dialog/dialog.ts +56 -0
- package/src/components/dialog/features.ts +713 -0
- package/src/components/dialog/index.ts +15 -0
- package/src/components/dialog/types.ts +221 -0
- package/src/components/progress/_styles.scss +13 -1
- package/src/components/progress/api.ts +2 -2
- package/src/components/progress/progress.ts +2 -2
- package/src/components/progress/types.ts +3 -0
- package/src/components/radios/_styles.scss +232 -0
- package/src/components/radios/api.ts +100 -0
- package/src/components/radios/config.ts +60 -0
- package/src/components/radios/constants.ts +28 -0
- package/src/components/radios/index.ts +4 -0
- package/src/components/radios/radio.ts +269 -0
- package/src/components/radios/radios.ts +42 -0
- package/src/components/radios/types.ts +232 -0
- package/src/components/sheet/_styles.scss +236 -0
- package/src/components/sheet/api.ts +96 -0
- package/src/components/sheet/config.ts +66 -0
- package/src/components/sheet/constants.ts +20 -0
- package/src/components/sheet/features/content.ts +51 -0
- package/src/components/sheet/features/gestures.ts +177 -0
- package/src/components/sheet/features/index.ts +6 -0
- package/src/components/sheet/features/position.ts +42 -0
- package/src/components/sheet/features/state.ts +116 -0
- package/src/components/sheet/features/title.ts +86 -0
- package/src/components/sheet/index.ts +4 -0
- package/src/components/sheet/sheet.ts +57 -0
- package/src/components/sheet/types.ts +266 -0
- package/src/components/slider/_styles.scss +518 -0
- package/src/components/slider/api.ts +336 -0
- package/src/components/slider/config.ts +145 -0
- package/src/components/slider/constants.ts +28 -0
- package/src/components/slider/features/appearance.ts +140 -0
- package/src/components/slider/features/disabled.ts +43 -0
- package/src/components/slider/features/events.ts +164 -0
- package/src/components/slider/features/index.ts +5 -0
- package/src/components/slider/features/interactions.ts +256 -0
- package/src/components/slider/features/keyboard.ts +114 -0
- package/src/components/slider/features/slider.ts +336 -0
- package/src/components/slider/features/structure.ts +264 -0
- package/src/components/slider/features/ui.ts +518 -0
- package/src/components/slider/index.ts +9 -0
- package/src/components/slider/slider.ts +58 -0
- package/src/components/slider/types.ts +166 -0
- package/src/components/tabs/_styles.scss +224 -0
- package/src/components/tabs/api.ts +443 -0
- package/src/components/tabs/config.ts +80 -0
- package/src/components/tabs/constants.ts +12 -0
- package/src/components/tabs/index.ts +4 -0
- package/src/components/tabs/tabs.ts +52 -0
- package/src/components/tabs/types.ts +247 -0
- package/src/components/textfield/_styles.scss +97 -4
- package/src/components/tooltip/_styles.scss +241 -0
- package/src/components/tooltip/api.ts +411 -0
- package/src/components/tooltip/config.ts +78 -0
- package/src/components/tooltip/constants.ts +27 -0
- package/src/components/tooltip/index.ts +4 -0
- package/src/components/tooltip/tooltip.ts +60 -0
- package/src/components/tooltip/types.ts +178 -0
- package/src/index.ts +9 -1
- package/src/styles/abstract/_variables.scss +24 -12
- package/tsconfig.json +22 -0
- package/typedoc.json +28 -0
- package/typedoc.simple.json +14 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
// src/components/slider/features/slider.ts - Added initialization code
|
|
2
|
+
import { SLIDER_EVENTS } from '../constants';
|
|
3
|
+
import { SliderConfig } from '../types';
|
|
4
|
+
import { createUiHelpers } from './ui';
|
|
5
|
+
import { createInteractionHandlers } from './interactions';
|
|
6
|
+
import { createKeyboardHandlers } from './keyboard';
|
|
7
|
+
import { createEventHelpers } from './events';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Add main slider functionality to component
|
|
11
|
+
* @param config Slider configuration
|
|
12
|
+
* @returns Component enhancer with slider functionality
|
|
13
|
+
*/
|
|
14
|
+
export const withSlider = (config: SliderConfig) => component => {
|
|
15
|
+
// Ensure component has events
|
|
16
|
+
if (!component.events) {
|
|
17
|
+
component.events = {
|
|
18
|
+
listeners: {},
|
|
19
|
+
on: function(event, handler) {
|
|
20
|
+
if (!this.listeners[event]) {
|
|
21
|
+
this.listeners[event] = [];
|
|
22
|
+
}
|
|
23
|
+
this.listeners[event].push(handler);
|
|
24
|
+
return this;
|
|
25
|
+
},
|
|
26
|
+
off: function(event, handler) {
|
|
27
|
+
if (this.listeners[event]) {
|
|
28
|
+
this.listeners[event] = this.listeners[event].filter(h => h !== handler);
|
|
29
|
+
}
|
|
30
|
+
return this;
|
|
31
|
+
},
|
|
32
|
+
trigger: function(event, data) {
|
|
33
|
+
if (this.listeners[event]) {
|
|
34
|
+
this.listeners[event].forEach(handler => handler(data));
|
|
35
|
+
}
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Initialize state
|
|
42
|
+
const state = {
|
|
43
|
+
value: config.value !== undefined ? config.value : 0,
|
|
44
|
+
secondValue: config.secondValue !== undefined ? config.secondValue : null,
|
|
45
|
+
min: config.min !== undefined ? config.min : 0,
|
|
46
|
+
max: config.max !== undefined ? config.max : 100,
|
|
47
|
+
step: config.step !== undefined ? config.step : 1,
|
|
48
|
+
dragging: false,
|
|
49
|
+
activeBubble: null,
|
|
50
|
+
activeThumb: null,
|
|
51
|
+
ticks: [],
|
|
52
|
+
tickLabels: [],
|
|
53
|
+
component
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Create helper functions
|
|
57
|
+
const uiHelpers = createUiHelpers(config, state);
|
|
58
|
+
const eventHelpers = createEventHelpers(state);
|
|
59
|
+
|
|
60
|
+
// Combine helpers for event handlers
|
|
61
|
+
const handlers = {
|
|
62
|
+
...uiHelpers,
|
|
63
|
+
triggerEvent: eventHelpers.triggerEvent
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Create event handlers
|
|
67
|
+
const interactionHandlers = createInteractionHandlers(config, state, handlers);
|
|
68
|
+
const keyboardHandlers = createKeyboardHandlers(state, handlers);
|
|
69
|
+
|
|
70
|
+
// Initialize slider
|
|
71
|
+
const initSlider = () => {
|
|
72
|
+
// Set ARIA attributes
|
|
73
|
+
component.element.setAttribute('aria-valuemin', String(state.min));
|
|
74
|
+
component.element.setAttribute('aria-valuemax', String(state.max));
|
|
75
|
+
component.element.setAttribute('aria-valuenow', String(state.value));
|
|
76
|
+
|
|
77
|
+
if (!component.structure) {
|
|
78
|
+
console.warn('Cannot initialize slider: missing structure');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const { thumb, secondThumb } = component.structure;
|
|
83
|
+
|
|
84
|
+
if (!thumb) {
|
|
85
|
+
console.warn('Cannot initialize slider: missing thumb');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
thumb.setAttribute('aria-valuemin', String(state.min));
|
|
90
|
+
thumb.setAttribute('aria-valuemax', String(state.max));
|
|
91
|
+
thumb.setAttribute('aria-valuenow', String(state.value));
|
|
92
|
+
|
|
93
|
+
if (config.range && secondThumb && state.secondValue !== null) {
|
|
94
|
+
secondThumb.setAttribute('aria-valuemin', String(state.min));
|
|
95
|
+
secondThumb.setAttribute('aria-valuemax', String(state.max));
|
|
96
|
+
secondThumb.setAttribute('aria-valuenow', String(state.secondValue));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Setup initial positions
|
|
100
|
+
uiHelpers.updateUi();
|
|
101
|
+
|
|
102
|
+
// Generate ticks if needed
|
|
103
|
+
if (config.ticks || config.tickLabels) {
|
|
104
|
+
uiHelpers.generateTicks();
|
|
105
|
+
uiHelpers.updateTicks();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Setup event listeners
|
|
109
|
+
eventHelpers.setupEventListeners(interactionHandlers, keyboardHandlers);
|
|
110
|
+
|
|
111
|
+
// Force one more UI update after a delay to ensure everything is properly positioned
|
|
112
|
+
// This is especially important for range sliders
|
|
113
|
+
setTimeout(() => {
|
|
114
|
+
uiHelpers.updateUi();
|
|
115
|
+
}, 50);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Cleanup event listeners
|
|
119
|
+
const cleanup = () => {
|
|
120
|
+
eventHelpers.cleanupEventListeners(interactionHandlers, keyboardHandlers);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Register with lifecycle
|
|
124
|
+
if (component.lifecycle) {
|
|
125
|
+
const originalDestroy = component.lifecycle.destroy || (() => {});
|
|
126
|
+
component.lifecycle.destroy = () => {
|
|
127
|
+
cleanup();
|
|
128
|
+
originalDestroy();
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Initialize slider
|
|
133
|
+
initSlider();
|
|
134
|
+
|
|
135
|
+
// Return enhanced component with the slider functionality
|
|
136
|
+
const enhancedComponent = {
|
|
137
|
+
...component,
|
|
138
|
+
slider: {
|
|
139
|
+
/**
|
|
140
|
+
* Sets slider value
|
|
141
|
+
* @param value New value
|
|
142
|
+
* @param triggerEvent Whether to trigger change event
|
|
143
|
+
*/
|
|
144
|
+
setValue(value, triggerEvent = true) {
|
|
145
|
+
// Validate and set value
|
|
146
|
+
const newValue = uiHelpers.clamp(value, state.min, state.max);
|
|
147
|
+
state.value = newValue;
|
|
148
|
+
|
|
149
|
+
// Update UI
|
|
150
|
+
uiHelpers.updateUi();
|
|
151
|
+
|
|
152
|
+
// Trigger events if needed
|
|
153
|
+
if (triggerEvent) {
|
|
154
|
+
eventHelpers.triggerEvent(SLIDER_EVENTS.CHANGE);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return this;
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Gets slider value
|
|
162
|
+
* @returns Current value
|
|
163
|
+
*/
|
|
164
|
+
getValue() {
|
|
165
|
+
return state.value;
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Sets secondary slider value (for range slider)
|
|
170
|
+
* @param value New secondary value
|
|
171
|
+
* @param triggerEvent Whether to trigger change event
|
|
172
|
+
*/
|
|
173
|
+
setSecondValue(value, triggerEvent = true) {
|
|
174
|
+
if (!config.range) return this;
|
|
175
|
+
|
|
176
|
+
// Validate and set value
|
|
177
|
+
const newValue = uiHelpers.clamp(value, state.min, state.max);
|
|
178
|
+
state.secondValue = newValue;
|
|
179
|
+
|
|
180
|
+
// Update UI
|
|
181
|
+
uiHelpers.updateUi();
|
|
182
|
+
|
|
183
|
+
// Trigger events if needed
|
|
184
|
+
if (triggerEvent) {
|
|
185
|
+
eventHelpers.triggerEvent(SLIDER_EVENTS.CHANGE);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return this;
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Gets secondary slider value
|
|
193
|
+
* @returns Current secondary value or null
|
|
194
|
+
*/
|
|
195
|
+
getSecondValue() {
|
|
196
|
+
return config.range ? state.secondValue : null;
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Sets slider minimum value
|
|
201
|
+
* @param min New minimum value
|
|
202
|
+
*/
|
|
203
|
+
setMin(min) {
|
|
204
|
+
state.min = min;
|
|
205
|
+
|
|
206
|
+
// Update ARIA attributes
|
|
207
|
+
component.element.setAttribute('aria-valuemin', String(min));
|
|
208
|
+
component.structure.thumb.setAttribute('aria-valuemin', String(min));
|
|
209
|
+
|
|
210
|
+
if (config.range && component.structure.secondThumb) {
|
|
211
|
+
component.structure.secondThumb.setAttribute('aria-valuemin', String(min));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Clamp values to new min
|
|
215
|
+
if (state.value < min) {
|
|
216
|
+
state.value = min;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (config.range && state.secondValue !== null && state.secondValue < min) {
|
|
220
|
+
state.secondValue = min;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Regenerate ticks if needed
|
|
224
|
+
if (config.ticks || config.tickLabels) {
|
|
225
|
+
uiHelpers.generateTicks();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Update UI
|
|
229
|
+
uiHelpers.updateUi();
|
|
230
|
+
|
|
231
|
+
return this;
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Gets slider minimum value
|
|
236
|
+
* @returns Current minimum value
|
|
237
|
+
*/
|
|
238
|
+
getMin() {
|
|
239
|
+
return state.min;
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Sets slider maximum value
|
|
244
|
+
* @param max New maximum value
|
|
245
|
+
*/
|
|
246
|
+
setMax(max) {
|
|
247
|
+
state.max = max;
|
|
248
|
+
|
|
249
|
+
// Update ARIA attributes
|
|
250
|
+
component.element.setAttribute('aria-valuemax', String(max));
|
|
251
|
+
component.structure.thumb.setAttribute('aria-valuemax', String(max));
|
|
252
|
+
|
|
253
|
+
if (config.range && component.structure.secondThumb) {
|
|
254
|
+
component.structure.secondThumb.setAttribute('aria-valuemax', String(max));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Clamp values to new max
|
|
258
|
+
if (state.value > max) {
|
|
259
|
+
state.value = max;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (config.range && state.secondValue !== null && state.secondValue > max) {
|
|
263
|
+
state.secondValue = max;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Regenerate ticks if needed
|
|
267
|
+
if (config.ticks || config.tickLabels) {
|
|
268
|
+
uiHelpers.generateTicks();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Update UI
|
|
272
|
+
uiHelpers.updateUi();
|
|
273
|
+
|
|
274
|
+
return this;
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Gets slider maximum value
|
|
279
|
+
* @returns Current maximum value
|
|
280
|
+
*/
|
|
281
|
+
getMax() {
|
|
282
|
+
return state.max;
|
|
283
|
+
},
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Sets slider step size
|
|
287
|
+
* @param step New step size
|
|
288
|
+
*/
|
|
289
|
+
setStep(step) {
|
|
290
|
+
state.step = step;
|
|
291
|
+
|
|
292
|
+
// Add or remove discrete class
|
|
293
|
+
if (step > 0) {
|
|
294
|
+
component.element.classList.add(`${component.getClass('slider')}--discrete`);
|
|
295
|
+
} else {
|
|
296
|
+
component.element.classList.remove(`${component.getClass('slider')}--discrete`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Regenerate ticks if needed
|
|
300
|
+
if (config.ticks || config.tickLabels) {
|
|
301
|
+
uiHelpers.generateTicks();
|
|
302
|
+
uiHelpers.updateTicks();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return this;
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Gets slider step size
|
|
310
|
+
* @returns Current step size
|
|
311
|
+
*/
|
|
312
|
+
getStep() {
|
|
313
|
+
return state.step;
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Regenerate tick marks and labels
|
|
318
|
+
*/
|
|
319
|
+
regenerateTicks() {
|
|
320
|
+
uiHelpers.generateTicks();
|
|
321
|
+
uiHelpers.updateTicks();
|
|
322
|
+
return this;
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Update all UI elements
|
|
327
|
+
*/
|
|
328
|
+
updateUi() {
|
|
329
|
+
uiHelpers.updateUi();
|
|
330
|
+
return this;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
return enhancedComponent;
|
|
336
|
+
};
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// src/components/slider/features/structure.ts - Fixed track initialization
|
|
2
|
+
import { SLIDER_COLORS, SLIDER_SIZES, SLIDER_ORIENTATIONS } from '../constants';
|
|
3
|
+
import { SliderConfig } from '../types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates the slider DOM structure following MD3 principles
|
|
7
|
+
* @param config Slider configuration
|
|
8
|
+
* @returns Component enhancer with DOM structure
|
|
9
|
+
*/
|
|
10
|
+
export const withStructure = (config: SliderConfig) => component => {
|
|
11
|
+
// Create track element
|
|
12
|
+
const track = document.createElement('div');
|
|
13
|
+
track.classList.add(component.getClass('slider-track'));
|
|
14
|
+
|
|
15
|
+
// Calculate initial percentages based on values
|
|
16
|
+
const min = config.min || 0;
|
|
17
|
+
const max = config.max || 100;
|
|
18
|
+
const range = max - min;
|
|
19
|
+
|
|
20
|
+
// Set default values
|
|
21
|
+
const value = config.value !== undefined ? config.value : min;
|
|
22
|
+
const secondValue = config.secondValue !== undefined ? config.secondValue : null;
|
|
23
|
+
|
|
24
|
+
// Calculate percentages
|
|
25
|
+
const getPercentage = (val) => ((val - min) / range) * 100;
|
|
26
|
+
const valuePercent = getPercentage(value);
|
|
27
|
+
|
|
28
|
+
// Create remaining track element (fills entire width initially)
|
|
29
|
+
const remainingTrack = document.createElement('div');
|
|
30
|
+
remainingTrack.classList.add(component.getClass('slider-remaining-track'));
|
|
31
|
+
|
|
32
|
+
// Create start track element (for range slider)
|
|
33
|
+
const startTrack = document.createElement('div');
|
|
34
|
+
startTrack.classList.add(component.getClass('slider-start-track'));
|
|
35
|
+
|
|
36
|
+
// Create active track element (filled part)
|
|
37
|
+
const activeTrack = document.createElement('div');
|
|
38
|
+
activeTrack.classList.add(component.getClass('slider-active-track'));
|
|
39
|
+
|
|
40
|
+
// Calculate padding adjustment (8px equivalent as percentage)
|
|
41
|
+
// We'll do a rough estimate initially, then recalculate once rendered
|
|
42
|
+
const paddingAdjustment = 8; // 8px padding
|
|
43
|
+
const estimatedTrackSize = 300; // A reasonable guess at track width
|
|
44
|
+
const paddingPercent = (paddingAdjustment / estimatedTrackSize) * 100;
|
|
45
|
+
|
|
46
|
+
// Set initial dimensions for all track segments
|
|
47
|
+
if (config.range && secondValue !== null) {
|
|
48
|
+
// Range slider
|
|
49
|
+
const lowerValue = Math.min(value, secondValue);
|
|
50
|
+
const higherValue = Math.max(value, secondValue);
|
|
51
|
+
const lowerPercent = getPercentage(lowerValue);
|
|
52
|
+
const higherPercent = getPercentage(higherValue);
|
|
53
|
+
|
|
54
|
+
// Adjust positions and width to account for spacing
|
|
55
|
+
let adjustedLowerPercent = lowerPercent + paddingPercent;
|
|
56
|
+
let adjustedHigherPercent = higherPercent - paddingPercent;
|
|
57
|
+
|
|
58
|
+
if (adjustedHigherPercent <= adjustedLowerPercent) {
|
|
59
|
+
adjustedLowerPercent = (lowerPercent + higherPercent) / 2 - 1;
|
|
60
|
+
adjustedHigherPercent = (lowerPercent + higherPercent) / 2 + 1;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Calculate track segment sizes
|
|
64
|
+
const startWidth = Math.max(0, lowerPercent - paddingPercent);
|
|
65
|
+
const activeWidth = Math.max(0, adjustedHigherPercent - adjustedLowerPercent);
|
|
66
|
+
const remainingWidth = Math.max(0, 100 - higherPercent - paddingPercent);
|
|
67
|
+
|
|
68
|
+
if (config.orientation === SLIDER_ORIENTATIONS.VERTICAL) {
|
|
69
|
+
// Vertical orientation
|
|
70
|
+
startTrack.style.display = 'block';
|
|
71
|
+
startTrack.style.height = `${startWidth}%`;
|
|
72
|
+
startTrack.style.bottom = '0';
|
|
73
|
+
startTrack.style.width = '100%';
|
|
74
|
+
|
|
75
|
+
activeTrack.style.display = 'block';
|
|
76
|
+
activeTrack.style.height = `${activeWidth}%`;
|
|
77
|
+
activeTrack.style.bottom = `${adjustedLowerPercent}%`;
|
|
78
|
+
activeTrack.style.width = '100%';
|
|
79
|
+
|
|
80
|
+
remainingTrack.style.display = 'block';
|
|
81
|
+
remainingTrack.style.height = `${remainingWidth}%`;
|
|
82
|
+
remainingTrack.style.bottom = `${higherPercent + paddingPercent}%`;
|
|
83
|
+
remainingTrack.style.width = '100%';
|
|
84
|
+
} else {
|
|
85
|
+
// Horizontal orientation
|
|
86
|
+
startTrack.style.display = 'block';
|
|
87
|
+
startTrack.style.width = `${startWidth}%`;
|
|
88
|
+
startTrack.style.left = '0';
|
|
89
|
+
startTrack.style.height = '100%';
|
|
90
|
+
|
|
91
|
+
activeTrack.style.display = 'block';
|
|
92
|
+
activeTrack.style.width = `${activeWidth}%`;
|
|
93
|
+
activeTrack.style.left = `${adjustedLowerPercent}%`;
|
|
94
|
+
activeTrack.style.height = '100%';
|
|
95
|
+
|
|
96
|
+
remainingTrack.style.display = 'block';
|
|
97
|
+
remainingTrack.style.width = `${remainingWidth}%`;
|
|
98
|
+
remainingTrack.style.left = `${higherPercent + paddingPercent}%`;
|
|
99
|
+
remainingTrack.style.height = '100%';
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
// Single thumb slider
|
|
103
|
+
const adjustedWidth = Math.max(0, valuePercent - paddingPercent);
|
|
104
|
+
const remainingWidth = Math.max(0, 100 - valuePercent - paddingPercent);
|
|
105
|
+
|
|
106
|
+
if (config.orientation === SLIDER_ORIENTATIONS.VERTICAL) {
|
|
107
|
+
// Vertical orientation
|
|
108
|
+
startTrack.style.display = 'none';
|
|
109
|
+
|
|
110
|
+
activeTrack.style.display = 'block';
|
|
111
|
+
activeTrack.style.height = `${adjustedWidth}%`;
|
|
112
|
+
activeTrack.style.bottom = '0';
|
|
113
|
+
activeTrack.style.width = '100%';
|
|
114
|
+
|
|
115
|
+
remainingTrack.style.display = 'block';
|
|
116
|
+
remainingTrack.style.height = `${remainingWidth}%`;
|
|
117
|
+
remainingTrack.style.bottom = `${valuePercent + paddingPercent}%`;
|
|
118
|
+
remainingTrack.style.width = '100%';
|
|
119
|
+
} else {
|
|
120
|
+
// Horizontal orientation
|
|
121
|
+
startTrack.style.display = 'none';
|
|
122
|
+
|
|
123
|
+
activeTrack.style.display = 'block';
|
|
124
|
+
activeTrack.style.width = `${adjustedWidth}%`;
|
|
125
|
+
activeTrack.style.left = '0';
|
|
126
|
+
activeTrack.style.height = '100%';
|
|
127
|
+
|
|
128
|
+
remainingTrack.style.display = 'block';
|
|
129
|
+
remainingTrack.style.width = `${remainingWidth}%`;
|
|
130
|
+
remainingTrack.style.left = `${valuePercent + paddingPercent}%`;
|
|
131
|
+
remainingTrack.style.height = '100%';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Add tracks to container
|
|
136
|
+
track.appendChild(remainingTrack);
|
|
137
|
+
track.appendChild(startTrack);
|
|
138
|
+
track.appendChild(activeTrack);
|
|
139
|
+
|
|
140
|
+
// Create thumb element
|
|
141
|
+
const thumb = document.createElement('div');
|
|
142
|
+
thumb.classList.add(component.getClass('slider-thumb'));
|
|
143
|
+
thumb.setAttribute('tabindex', '0');
|
|
144
|
+
thumb.setAttribute('role', 'slider');
|
|
145
|
+
|
|
146
|
+
// Set initial thumb position
|
|
147
|
+
if (config.orientation === SLIDER_ORIENTATIONS.VERTICAL) {
|
|
148
|
+
thumb.style.bottom = `${valuePercent}%`;
|
|
149
|
+
thumb.style.left = '50%';
|
|
150
|
+
thumb.style.top = 'auto';
|
|
151
|
+
} else {
|
|
152
|
+
thumb.style.left = `${valuePercent}%`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Create dots for the track ends
|
|
156
|
+
const startDot = document.createElement('div');
|
|
157
|
+
startDot.classList.add(component.getClass('slider-dot'));
|
|
158
|
+
startDot.classList.add(component.getClass('slider-dot--start'));
|
|
159
|
+
|
|
160
|
+
const endDot = document.createElement('div');
|
|
161
|
+
endDot.classList.add(component.getClass('slider-dot'));
|
|
162
|
+
endDot.classList.add(component.getClass('slider-dot--end'));
|
|
163
|
+
|
|
164
|
+
// Create value bubble element
|
|
165
|
+
const valueBubble = document.createElement('div');
|
|
166
|
+
valueBubble.classList.add(component.getClass('slider-value'));
|
|
167
|
+
|
|
168
|
+
// Format value and set initial bubble text
|
|
169
|
+
const formatter = config.valueFormatter || (val => val.toString());
|
|
170
|
+
valueBubble.textContent = formatter(value);
|
|
171
|
+
|
|
172
|
+
// For range slider: Create second thumb and value bubble
|
|
173
|
+
let secondThumb = null;
|
|
174
|
+
let secondValueBubble = null;
|
|
175
|
+
|
|
176
|
+
if (config.range && secondValue !== null) {
|
|
177
|
+
// Create second thumb
|
|
178
|
+
secondThumb = document.createElement('div');
|
|
179
|
+
secondThumb.classList.add(component.getClass('slider-thumb'));
|
|
180
|
+
secondThumb.setAttribute('tabindex', '0');
|
|
181
|
+
secondThumb.setAttribute('role', 'slider');
|
|
182
|
+
|
|
183
|
+
// Set initial second thumb position
|
|
184
|
+
const secondPercent = getPercentage(secondValue);
|
|
185
|
+
if (config.orientation === SLIDER_ORIENTATIONS.VERTICAL) {
|
|
186
|
+
secondThumb.style.bottom = `${secondPercent}%`;
|
|
187
|
+
secondThumb.style.left = '50%';
|
|
188
|
+
secondThumb.style.top = 'auto';
|
|
189
|
+
} else {
|
|
190
|
+
secondThumb.style.left = `${secondPercent}%`;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Create second value bubble
|
|
194
|
+
secondValueBubble = document.createElement('div');
|
|
195
|
+
secondValueBubble.classList.add(component.getClass('slider-value'));
|
|
196
|
+
secondValueBubble.textContent = formatter(secondValue);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Add elements to the slider
|
|
200
|
+
component.element.classList.add(component.getClass('slider'));
|
|
201
|
+
component.element.appendChild(track);
|
|
202
|
+
component.element.appendChild(startDot);
|
|
203
|
+
component.element.appendChild(endDot);
|
|
204
|
+
component.element.appendChild(thumb);
|
|
205
|
+
component.element.appendChild(valueBubble);
|
|
206
|
+
|
|
207
|
+
if (config.range && secondThumb && secondValueBubble) {
|
|
208
|
+
component.element.classList.add(`${component.getClass('slider')}--range`);
|
|
209
|
+
component.element.appendChild(secondThumb);
|
|
210
|
+
component.element.appendChild(secondValueBubble);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Apply size class
|
|
214
|
+
const size = config.size || SLIDER_SIZES.MEDIUM;
|
|
215
|
+
if (size !== SLIDER_SIZES.MEDIUM) {
|
|
216
|
+
component.element.classList.add(`${component.getClass('slider')}--${size}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Apply color class
|
|
220
|
+
const color = config.color || SLIDER_COLORS.PRIMARY;
|
|
221
|
+
if (color !== SLIDER_COLORS.PRIMARY) {
|
|
222
|
+
component.element.classList.add(`${component.getClass('slider')}--${color}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Apply orientation class
|
|
226
|
+
const orientation = config.orientation || SLIDER_ORIENTATIONS.HORIZONTAL;
|
|
227
|
+
if (orientation === SLIDER_ORIENTATIONS.VERTICAL) {
|
|
228
|
+
component.element.classList.add(`${component.getClass('slider')}--vertical`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Apply discrete class if step is specified
|
|
232
|
+
if (config.step !== undefined && config.step > 0) {
|
|
233
|
+
component.element.classList.add(`${component.getClass('slider')}--discrete`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Apply disabled class if needed
|
|
237
|
+
if (config.disabled) {
|
|
238
|
+
component.element.classList.add(`${component.getClass('slider')}--disabled`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Ensure proper initialization after DOM is attached by scheduling a UI update
|
|
242
|
+
setTimeout(() => {
|
|
243
|
+
if (component.slider && typeof component.slider.updateUi === 'function') {
|
|
244
|
+
component.slider.updateUi();
|
|
245
|
+
}
|
|
246
|
+
}, 0);
|
|
247
|
+
|
|
248
|
+
// Store elements in component
|
|
249
|
+
return {
|
|
250
|
+
...component,
|
|
251
|
+
structure: {
|
|
252
|
+
track,
|
|
253
|
+
activeTrack,
|
|
254
|
+
startTrack,
|
|
255
|
+
remainingTrack,
|
|
256
|
+
thumb,
|
|
257
|
+
valueBubble,
|
|
258
|
+
secondThumb,
|
|
259
|
+
secondValueBubble,
|
|
260
|
+
startDot,
|
|
261
|
+
endDot
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
};
|