mtrl 0.2.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +4 -0
- package/package.json +1 -1
- package/src/components/button/button.ts +34 -5
- package/src/components/navigation/api.ts +131 -96
- package/src/components/navigation/features/controller.ts +273 -0
- package/src/components/navigation/features/items.ts +133 -64
- package/src/components/navigation/navigation.ts +17 -2
- package/src/components/navigation/system/core.ts +302 -0
- package/src/components/navigation/system/events.ts +240 -0
- package/src/components/navigation/system/index.ts +184 -0
- package/src/components/navigation/system/mobile.ts +278 -0
- package/src/components/navigation/system/state.ts +77 -0
- package/src/components/navigation/system/types.ts +364 -0
- package/src/components/slider/config.ts +20 -2
- package/src/components/slider/features/controller.ts +737 -0
- package/src/components/slider/features/handlers.ts +18 -16
- package/src/components/slider/features/index.ts +3 -2
- package/src/components/slider/features/range.ts +104 -0
- package/src/components/slider/schema.ts +141 -0
- package/src/components/slider/slider.ts +34 -13
- package/src/components/switch/api.ts +16 -0
- package/src/components/switch/config.ts +1 -18
- package/src/components/switch/features.ts +198 -0
- package/src/components/switch/index.ts +1 -0
- package/src/components/switch/switch.ts +3 -3
- package/src/components/switch/types.ts +14 -2
- package/src/components/textfield/api.ts +53 -0
- package/src/components/textfield/features.ts +322 -0
- package/src/components/textfield/textfield.ts +8 -0
- package/src/components/textfield/types.ts +12 -3
- package/src/components/timepicker/clockdial.ts +1 -4
- package/src/core/compose/features/textinput.ts +15 -2
- package/src/core/composition/features/dom.ts +45 -0
- package/src/core/composition/features/icon.ts +131 -0
- package/src/core/composition/features/index.ts +12 -0
- package/src/core/composition/features/label.ts +155 -0
- package/src/core/composition/features/layout.ts +47 -0
- package/src/core/composition/index.ts +26 -0
- package/src/core/index.ts +1 -1
- package/src/core/layout/README.md +350 -0
- package/src/core/layout/array.ts +181 -0
- package/src/core/layout/create.ts +55 -0
- package/src/core/layout/index.ts +26 -0
- package/src/core/layout/object.ts +124 -0
- package/src/core/layout/processor.ts +58 -0
- package/src/core/layout/result.ts +85 -0
- package/src/core/layout/types.ts +125 -0
- package/src/core/layout/utils.ts +136 -0
- package/src/index.ts +1 -0
- package/src/styles/abstract/_variables.scss +28 -0
- package/src/styles/components/_navigation-mobile.scss +244 -0
- package/src/styles/components/_navigation-system.scss +151 -0
- package/src/styles/components/_switch.scss +133 -69
- package/src/styles/components/_textfield.scss +259 -27
- package/demo/build.ts +0 -349
- package/demo/index.html +0 -110
- package/demo/main.js +0 -448
- package/demo/styles.css +0 -239
- package/server.ts +0 -86
- package/src/components/slider/features/slider.ts +0 -318
- package/src/components/slider/features/structure.ts +0 -181
- package/src/components/slider/features/ui.ts +0 -388
- package/src/components/textfield/constants.ts +0 -100
- package/src/core/layout/index.js +0 -95
package/server.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
// server.ts - TypeScript version of the Bun server for the component library demo
|
|
2
|
-
import { serve, type ServeOptions } from "bun";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
import { readFileSync, existsSync } from "fs";
|
|
5
|
-
|
|
6
|
-
// Content type mapping
|
|
7
|
-
const contentTypes: Record<string, string> = {
|
|
8
|
-
".html": "text/html",
|
|
9
|
-
".js": "text/javascript",
|
|
10
|
-
".mjs": "text/javascript", // Add this for ES modules
|
|
11
|
-
".css": "text/css",
|
|
12
|
-
".json": "application/json",
|
|
13
|
-
".png": "image/png",
|
|
14
|
-
".jpg": "image/jpeg",
|
|
15
|
-
".svg": "image/svg+xml"
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// Define the port (hardcoded instead of using environment variable)
|
|
19
|
-
const PORT: number = 3301;
|
|
20
|
-
|
|
21
|
-
// Define demo directory path
|
|
22
|
-
const DEMO_DIR: string = "./demo";
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Serves a file from the demo directory
|
|
26
|
-
* @param path Path to the file relative to the demo directory
|
|
27
|
-
* @returns Response object with the file content or error message
|
|
28
|
-
*/
|
|
29
|
-
function serveFile(path: string): Response {
|
|
30
|
-
// Add demo directory to path
|
|
31
|
-
const filePath: string = join(DEMO_DIR, path);
|
|
32
|
-
|
|
33
|
-
// Handle index.html for directory request
|
|
34
|
-
let resolvedPath: string = filePath;
|
|
35
|
-
if (filePath.endsWith('/') || !filePath.includes('.')) {
|
|
36
|
-
resolvedPath = join(filePath, 'index.html');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Check if file exists
|
|
40
|
-
if (!existsSync(resolvedPath)) {
|
|
41
|
-
return new Response("File not found", { status: 404 });
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Get file extension for content type
|
|
45
|
-
const ext: string = resolvedPath.substring(resolvedPath.lastIndexOf('.'));
|
|
46
|
-
const contentType: string = contentTypes[ext] || "application/octet-stream";
|
|
47
|
-
|
|
48
|
-
// Read and serve the file
|
|
49
|
-
try {
|
|
50
|
-
const content: Buffer = readFileSync(resolvedPath);
|
|
51
|
-
|
|
52
|
-
// For JavaScript files, ensure they're served with the correct MIME type
|
|
53
|
-
// and additionally add proper headers for modules
|
|
54
|
-
if (ext === '.js') {
|
|
55
|
-
return new Response(content, {
|
|
56
|
-
headers: {
|
|
57
|
-
"Content-Type": "text/javascript",
|
|
58
|
-
"Cache-Control": "no-cache"
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return new Response(content, {
|
|
64
|
-
headers: { "Content-Type": contentType }
|
|
65
|
-
});
|
|
66
|
-
} catch (error) {
|
|
67
|
-
console.error(`Error serving ${resolvedPath}:`, error);
|
|
68
|
-
return new Response("Server error", { status: 500 });
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Server options with typings
|
|
73
|
-
const serverOptions: ServeOptions = {
|
|
74
|
-
port: PORT,
|
|
75
|
-
fetch(req: Request): Response | Promise<Response> {
|
|
76
|
-
const url = new URL(req.url);
|
|
77
|
-
const path = url.pathname === '/' ? '/' : url.pathname;
|
|
78
|
-
|
|
79
|
-
return serveFile(path);
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
// Create and start the server
|
|
84
|
-
const server = serve(serverOptions);
|
|
85
|
-
|
|
86
|
-
console.log(`Server running at http://localhost:${PORT}`);
|
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
// src/components/slider/features/slider.ts
|
|
2
|
-
import { SLIDER_EVENTS } from '../constants';
|
|
3
|
-
import { SliderConfig } from '../types';
|
|
4
|
-
import { createUiHelpers } from './ui';
|
|
5
|
-
import { createHandlers } from './handlers';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Add main slider functionality to component
|
|
9
|
-
* @param config Slider configuration
|
|
10
|
-
* @returns Component enhancer with slider functionality
|
|
11
|
-
*/
|
|
12
|
-
export const withSlider = (config: SliderConfig) => component => {
|
|
13
|
-
// Ensure component has events
|
|
14
|
-
if (!component.events) {
|
|
15
|
-
component.events = {
|
|
16
|
-
listeners: {},
|
|
17
|
-
on(event, handler) {
|
|
18
|
-
if (!this.listeners[event]) this.listeners[event] = [];
|
|
19
|
-
this.listeners[event].push(handler);
|
|
20
|
-
return this;
|
|
21
|
-
},
|
|
22
|
-
off(event, handler) {
|
|
23
|
-
if (this.listeners[event]) {
|
|
24
|
-
this.listeners[event] = this.listeners[event].filter(h => h !== handler);
|
|
25
|
-
}
|
|
26
|
-
return this;
|
|
27
|
-
},
|
|
28
|
-
trigger(event, data) {
|
|
29
|
-
if (this.listeners[event]) {
|
|
30
|
-
this.listeners[event].forEach(handler => handler(data));
|
|
31
|
-
}
|
|
32
|
-
return this;
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Initialize state
|
|
38
|
-
const state = {
|
|
39
|
-
value: config.value !== undefined ? config.value : 0,
|
|
40
|
-
secondValue: config.secondValue !== undefined ? config.secondValue : null,
|
|
41
|
-
min: config.min !== undefined ? config.min : 0,
|
|
42
|
-
max: config.max !== undefined ? config.max : 100,
|
|
43
|
-
step: config.step !== undefined ? config.step : 1,
|
|
44
|
-
dragging: false,
|
|
45
|
-
activeBubble: null,
|
|
46
|
-
activeHandle: null,
|
|
47
|
-
ticks: [],
|
|
48
|
-
valueHideTimer: null,
|
|
49
|
-
component
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
// Create event helpers
|
|
53
|
-
const eventHelpers = {
|
|
54
|
-
triggerEvent(eventName, originalEvent = null) {
|
|
55
|
-
const eventData = {
|
|
56
|
-
slider: state.component,
|
|
57
|
-
value: state.value,
|
|
58
|
-
secondValue: state.secondValue,
|
|
59
|
-
originalEvent,
|
|
60
|
-
preventDefault: () => { eventData.defaultPrevented = true; },
|
|
61
|
-
defaultPrevented: false
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
state.component.events.trigger(eventName, eventData);
|
|
65
|
-
return eventData;
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// Create UI helpers and handlers
|
|
70
|
-
const uiHelpers = createUiHelpers(config, state);
|
|
71
|
-
const handlers = createHandlers(config, state, uiHelpers, eventHelpers);
|
|
72
|
-
|
|
73
|
-
// Initialize slider
|
|
74
|
-
const initSlider = () => {
|
|
75
|
-
// Set ARIA attributes
|
|
76
|
-
component.element.setAttribute('aria-valuemin', String(state.min));
|
|
77
|
-
component.element.setAttribute('aria-valuemax', String(state.max));
|
|
78
|
-
component.element.setAttribute('aria-valuenow', String(state.value));
|
|
79
|
-
|
|
80
|
-
if (!component.structure) {
|
|
81
|
-
console.warn('Cannot initialize slider: missing structure');
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const { handle, secondHandle } = component.structure;
|
|
86
|
-
|
|
87
|
-
if (handle) {
|
|
88
|
-
handle.setAttribute('aria-valuemin', String(state.min));
|
|
89
|
-
handle.setAttribute('aria-valuemax', String(state.max));
|
|
90
|
-
handle.setAttribute('aria-valuenow', String(state.value));
|
|
91
|
-
|
|
92
|
-
if (config.range && secondHandle && state.secondValue !== null) {
|
|
93
|
-
secondHandle.setAttribute('aria-valuemin', String(state.min));
|
|
94
|
-
secondHandle.setAttribute('aria-valuemax', String(state.max));
|
|
95
|
-
secondHandle.setAttribute('aria-valuenow', String(state.secondValue));
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Initial UI update
|
|
100
|
-
uiHelpers.updateUi();
|
|
101
|
-
|
|
102
|
-
// Generate ticks if needed
|
|
103
|
-
if (config.ticks || config.tickLabels) {
|
|
104
|
-
uiHelpers.generateTicks();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Setup event listeners
|
|
108
|
-
handlers.setupEventListeners();
|
|
109
|
-
|
|
110
|
-
// Force one more UI update after a delay to ensure proper positioning
|
|
111
|
-
setTimeout(() => {
|
|
112
|
-
uiHelpers.updateUi();
|
|
113
|
-
}, 50);
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
// Register with lifecycle if available
|
|
117
|
-
if (component.lifecycle) {
|
|
118
|
-
const originalDestroy = component.lifecycle.destroy || (() => {});
|
|
119
|
-
component.lifecycle.destroy = () => {
|
|
120
|
-
handlers.cleanupEventListeners();
|
|
121
|
-
originalDestroy();
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Initialize slider
|
|
126
|
-
initSlider();
|
|
127
|
-
|
|
128
|
-
// Return enhanced component
|
|
129
|
-
return {
|
|
130
|
-
...component,
|
|
131
|
-
slider: {
|
|
132
|
-
/**
|
|
133
|
-
* Sets slider value
|
|
134
|
-
* @param value New value
|
|
135
|
-
* @param triggerEvent Whether to trigger change event
|
|
136
|
-
* @returns Slider controller for chaining
|
|
137
|
-
*/
|
|
138
|
-
setValue(value, triggerEvent = true) {
|
|
139
|
-
const newValue = uiHelpers.clamp(value, state.min, state.max);
|
|
140
|
-
state.value = newValue;
|
|
141
|
-
uiHelpers.updateUi();
|
|
142
|
-
|
|
143
|
-
if (triggerEvent) {
|
|
144
|
-
eventHelpers.triggerEvent(SLIDER_EVENTS.CHANGE);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return this;
|
|
148
|
-
},
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Gets slider value
|
|
152
|
-
* @returns Current value
|
|
153
|
-
*/
|
|
154
|
-
getValue() {
|
|
155
|
-
return state.value;
|
|
156
|
-
},
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Sets secondary slider value (for range slider)
|
|
160
|
-
* @param value New secondary value
|
|
161
|
-
* @param triggerEvent Whether to trigger change event
|
|
162
|
-
* @returns Slider controller for chaining
|
|
163
|
-
*/
|
|
164
|
-
setSecondValue(value, triggerEvent = true) {
|
|
165
|
-
if (!config.range) return this;
|
|
166
|
-
|
|
167
|
-
const newValue = uiHelpers.clamp(value, state.min, state.max);
|
|
168
|
-
state.secondValue = newValue;
|
|
169
|
-
uiHelpers.updateUi();
|
|
170
|
-
|
|
171
|
-
if (triggerEvent) {
|
|
172
|
-
eventHelpers.triggerEvent(SLIDER_EVENTS.CHANGE);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return this;
|
|
176
|
-
},
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Gets secondary slider value
|
|
180
|
-
* @returns Current secondary value or null
|
|
181
|
-
*/
|
|
182
|
-
getSecondValue() {
|
|
183
|
-
return config.range ? state.secondValue : null;
|
|
184
|
-
},
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Sets slider minimum value
|
|
188
|
-
* @param min New minimum value
|
|
189
|
-
* @returns Slider controller for chaining
|
|
190
|
-
*/
|
|
191
|
-
setMin(min) {
|
|
192
|
-
state.min = min;
|
|
193
|
-
|
|
194
|
-
// Update ARIA attributes
|
|
195
|
-
component.element.setAttribute('aria-valuemin', String(min));
|
|
196
|
-
if (component.structure.handle) {
|
|
197
|
-
component.structure.handle.setAttribute('aria-valuemin', String(min));
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (config.range && component.structure.secondHandle) {
|
|
201
|
-
component.structure.secondHandle.setAttribute('aria-valuemin', String(min));
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Clamp values to new min
|
|
205
|
-
if (state.value < min) state.value = min;
|
|
206
|
-
if (config.range && state.secondValue !== null && state.secondValue < min) {
|
|
207
|
-
state.secondValue = min;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Regenerate ticks if needed
|
|
211
|
-
if (config.ticks || config.tickLabels) {
|
|
212
|
-
uiHelpers.generateTicks();
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
uiHelpers.updateUi();
|
|
216
|
-
return this;
|
|
217
|
-
},
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Gets slider minimum value
|
|
221
|
-
* @returns Current minimum value
|
|
222
|
-
*/
|
|
223
|
-
getMin() {
|
|
224
|
-
return state.min;
|
|
225
|
-
},
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Sets slider maximum value
|
|
229
|
-
* @param max New maximum value
|
|
230
|
-
* @returns Slider controller for chaining
|
|
231
|
-
*/
|
|
232
|
-
setMax(max) {
|
|
233
|
-
state.max = max;
|
|
234
|
-
|
|
235
|
-
// Update ARIA attributes
|
|
236
|
-
component.element.setAttribute('aria-valuemax', String(max));
|
|
237
|
-
if (component.structure.handle) {
|
|
238
|
-
component.structure.handle.setAttribute('aria-valuemax', String(max));
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (config.range && component.structure.secondHandle) {
|
|
242
|
-
component.structure.secondHandle.setAttribute('aria-valuemax', String(max));
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Clamp values to new max
|
|
246
|
-
if (state.value > max) state.value = max;
|
|
247
|
-
if (config.range && state.secondValue !== null && state.secondValue > max) {
|
|
248
|
-
state.secondValue = max;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Regenerate ticks if needed
|
|
252
|
-
if (config.ticks || config.tickLabels) {
|
|
253
|
-
uiHelpers.generateTicks();
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
uiHelpers.updateUi();
|
|
257
|
-
return this;
|
|
258
|
-
},
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Gets slider maximum value
|
|
262
|
-
* @returns Current maximum value
|
|
263
|
-
*/
|
|
264
|
-
getMax() {
|
|
265
|
-
return state.max;
|
|
266
|
-
},
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Sets slider step size
|
|
270
|
-
* @param step New step size
|
|
271
|
-
* @returns Slider controller for chaining
|
|
272
|
-
*/
|
|
273
|
-
setStep(step) {
|
|
274
|
-
state.step = step;
|
|
275
|
-
|
|
276
|
-
// Add or remove discrete class
|
|
277
|
-
component.element.classList[step > 0 ? 'add' : 'remove'](
|
|
278
|
-
`${component.getClass('slider')}--discrete`
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
// Regenerate ticks if needed
|
|
282
|
-
if (config.ticks || config.tickLabels) {
|
|
283
|
-
uiHelpers.generateTicks();
|
|
284
|
-
uiHelpers.updateTicks();
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
return this;
|
|
288
|
-
},
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Gets slider step size
|
|
292
|
-
* @returns Current step size
|
|
293
|
-
*/
|
|
294
|
-
getStep() {
|
|
295
|
-
return state.step;
|
|
296
|
-
},
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Regenerate tick marks and labels
|
|
300
|
-
* @returns Slider controller for chaining
|
|
301
|
-
*/
|
|
302
|
-
regenerateTicks() {
|
|
303
|
-
uiHelpers.generateTicks();
|
|
304
|
-
uiHelpers.updateTicks();
|
|
305
|
-
return this;
|
|
306
|
-
},
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Update all UI elements
|
|
310
|
-
* @returns Slider controller for chaining
|
|
311
|
-
*/
|
|
312
|
-
updateUi() {
|
|
313
|
-
uiHelpers.updateUi();
|
|
314
|
-
return this;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
};
|
|
318
|
-
};
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
// src/components/slider/features/structure.ts
|
|
2
|
-
import { SLIDER_COLORS, SLIDER_SIZES } from '../constants';
|
|
3
|
-
import { SliderConfig } from '../types';
|
|
4
|
-
import createLayout from '../../../core/layout';
|
|
5
|
-
import { createElement } from '../../../core/dom/create';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Creates the slider DOM structure using layout system
|
|
9
|
-
* @param config Slider configuration
|
|
10
|
-
* @returns Component enhancer with DOM structure
|
|
11
|
-
*/
|
|
12
|
-
export const withStructure = (config: SliderConfig) => component => {
|
|
13
|
-
// Set default values
|
|
14
|
-
const min = config.min || 0;
|
|
15
|
-
const max = config.max || 100;
|
|
16
|
-
const value = config.value !== undefined ? config.value : min;
|
|
17
|
-
const secondValue = config.secondValue !== undefined ? config.secondValue : null;
|
|
18
|
-
const isRangeSlider = config.range && secondValue !== null;
|
|
19
|
-
const isDisabled = config.disabled === true;
|
|
20
|
-
|
|
21
|
-
// Format the values
|
|
22
|
-
const formatter = config.valueFormatter || (val => val.toString());
|
|
23
|
-
|
|
24
|
-
// Get prefixed class names
|
|
25
|
-
const getClass = (className) => component.getClass(className);
|
|
26
|
-
|
|
27
|
-
// Define slider structure
|
|
28
|
-
const structure = [
|
|
29
|
-
[createElement, 'container', {
|
|
30
|
-
className: getClass('slider-container')
|
|
31
|
-
}, [
|
|
32
|
-
// Track with segments
|
|
33
|
-
[createElement, 'track', {
|
|
34
|
-
className: getClass('slider-track')
|
|
35
|
-
}, [
|
|
36
|
-
[createElement, 'startTrack', {
|
|
37
|
-
className: getClass('slider-start-track')
|
|
38
|
-
}],
|
|
39
|
-
[createElement, 'activeTrack', {
|
|
40
|
-
className: getClass('slider-active-track')
|
|
41
|
-
}],
|
|
42
|
-
[createElement, 'remainingTrack', {
|
|
43
|
-
className: getClass('slider-remaining-track')
|
|
44
|
-
}]
|
|
45
|
-
]],
|
|
46
|
-
|
|
47
|
-
// Ticks container
|
|
48
|
-
[createElement, 'ticksContainer', {
|
|
49
|
-
className: getClass('slider-ticks-container')
|
|
50
|
-
}],
|
|
51
|
-
|
|
52
|
-
// Dots for ends
|
|
53
|
-
[createElement, 'startDot', {
|
|
54
|
-
className: [
|
|
55
|
-
getClass('slider-dot'),
|
|
56
|
-
getClass('slider-dot--start')
|
|
57
|
-
]
|
|
58
|
-
}],
|
|
59
|
-
[createElement, 'endDot', {
|
|
60
|
-
className: [
|
|
61
|
-
getClass('slider-dot'),
|
|
62
|
-
getClass('slider-dot--end')
|
|
63
|
-
]
|
|
64
|
-
}],
|
|
65
|
-
|
|
66
|
-
// Main handle
|
|
67
|
-
[createElement, 'handle', {
|
|
68
|
-
className: getClass('slider-handle'),
|
|
69
|
-
attrs: {
|
|
70
|
-
'role': 'slider',
|
|
71
|
-
'aria-valuemin': String(min),
|
|
72
|
-
'aria-valuemax': String(max),
|
|
73
|
-
'aria-valuenow': String(value),
|
|
74
|
-
'aria-orientation': 'horizontal',
|
|
75
|
-
'tabindex': isDisabled ? '-1' : '0',
|
|
76
|
-
'aria-disabled': isDisabled ? 'true' : 'false'
|
|
77
|
-
},
|
|
78
|
-
style: {
|
|
79
|
-
left: `${((value - min) / (max - min)) * 100}%`
|
|
80
|
-
}
|
|
81
|
-
}],
|
|
82
|
-
|
|
83
|
-
// Main value bubble
|
|
84
|
-
[createElement, 'valueBubble', {
|
|
85
|
-
className: getClass('slider-value'),
|
|
86
|
-
text: formatter(value)
|
|
87
|
-
}]
|
|
88
|
-
]]
|
|
89
|
-
];
|
|
90
|
-
|
|
91
|
-
// Add second handle and bubble for range slider
|
|
92
|
-
if (isRangeSlider) {
|
|
93
|
-
const secondHandlePos = ((secondValue - min) / (max - min)) * 100;
|
|
94
|
-
|
|
95
|
-
// Add second handle to structure
|
|
96
|
-
structure[0][3].push(
|
|
97
|
-
[createElement, 'secondHandle', {
|
|
98
|
-
className: getClass('slider-handle'),
|
|
99
|
-
attrs: {
|
|
100
|
-
'role': 'slider',
|
|
101
|
-
'aria-valuemin': String(min),
|
|
102
|
-
'aria-valuemax': String(max),
|
|
103
|
-
'aria-valuenow': String(secondValue),
|
|
104
|
-
'aria-orientation': 'horizontal',
|
|
105
|
-
'tabindex': isDisabled ? '-1' : '0',
|
|
106
|
-
'aria-disabled': isDisabled ? 'true' : 'false'
|
|
107
|
-
},
|
|
108
|
-
style: {
|
|
109
|
-
left: `${secondHandlePos}%`
|
|
110
|
-
}
|
|
111
|
-
}]
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
// Add second bubble to structure
|
|
115
|
-
structure[0][3].push(
|
|
116
|
-
[createElement, 'secondValueBubble', {
|
|
117
|
-
className: getClass('slider-value'),
|
|
118
|
-
text: formatter(secondValue)
|
|
119
|
-
}]
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Create layout and get structure elements
|
|
124
|
-
const components = createLayout(structure, component.element).component;
|
|
125
|
-
|
|
126
|
-
// Add component base class and accessibility attributes
|
|
127
|
-
component.element.classList.add(component.getClass('slider'));
|
|
128
|
-
component.element.setAttribute('tabindex', '-1');
|
|
129
|
-
component.element.setAttribute('role', 'none');
|
|
130
|
-
component.element.setAttribute('aria-disabled', isDisabled ? 'true' : 'false');
|
|
131
|
-
|
|
132
|
-
// Position any icon properly
|
|
133
|
-
const iconElement = component.element.querySelector(`.${component.getClass('icon')}`);
|
|
134
|
-
if (iconElement && config.label) {
|
|
135
|
-
iconElement.classList.add(component.getClass('slider-icon'));
|
|
136
|
-
component.element.classList.add(component.getClass('slider--icon'));
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Apply style classes
|
|
140
|
-
applyStyleClasses(component, config, isRangeSlider, isDisabled);
|
|
141
|
-
|
|
142
|
-
// Return enhanced component with structure
|
|
143
|
-
return {
|
|
144
|
-
...component,
|
|
145
|
-
structure: components
|
|
146
|
-
};
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Applies style classes based on configuration
|
|
151
|
-
*/
|
|
152
|
-
function applyStyleClasses(component, config, isRangeSlider, isDisabled) {
|
|
153
|
-
const baseClass = component.getClass('slider');
|
|
154
|
-
|
|
155
|
-
// Apply size class
|
|
156
|
-
const size = config.size || SLIDER_SIZES.MEDIUM;
|
|
157
|
-
if (size !== SLIDER_SIZES.MEDIUM) {
|
|
158
|
-
component.element.classList.add(`${baseClass}--${size}`);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Apply color class
|
|
162
|
-
const color = config.color || SLIDER_COLORS.PRIMARY;
|
|
163
|
-
if (color !== SLIDER_COLORS.PRIMARY) {
|
|
164
|
-
component.element.classList.add(`${baseClass}--${color}`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Apply discrete class if step is specified
|
|
168
|
-
if (config.step !== undefined && config.step > 0) {
|
|
169
|
-
component.element.classList.add(`${baseClass}--discrete`);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Apply disabled class if needed
|
|
173
|
-
if (isDisabled) {
|
|
174
|
-
component.element.classList.add(`${baseClass}--disabled`);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Apply range class if needed
|
|
178
|
-
if (isRangeSlider) {
|
|
179
|
-
component.element.classList.add(`${baseClass}--range`);
|
|
180
|
-
}
|
|
181
|
-
}
|