mtrl 0.2.4 → 0.2.6
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/package.json +6 -3
- package/src/components/badge/_styles.scss +9 -9
- package/src/components/button/_styles.scss +0 -56
- package/src/components/button/button.ts +0 -2
- package/src/components/button/constants.ts +0 -6
- package/src/components/button/index.ts +2 -2
- package/src/components/button/types.ts +1 -7
- package/src/components/card/_styles.scss +67 -25
- package/src/components/card/api.ts +54 -3
- package/src/components/card/card.ts +33 -2
- package/src/components/card/config.ts +143 -21
- package/src/components/card/constants.ts +20 -19
- package/src/components/card/content.ts +299 -2
- package/src/components/card/features.ts +155 -4
- package/src/components/card/index.ts +31 -9
- package/src/components/card/types.ts +138 -15
- package/src/components/chip/chip.ts +1 -9
- package/src/components/chip/constants.ts +0 -10
- package/src/components/chip/index.ts +1 -1
- package/src/components/chip/types.ts +1 -4
- package/src/components/progress/_styles.scss +0 -65
- package/src/components/progress/config.ts +1 -2
- package/src/components/progress/constants.ts +0 -14
- package/src/components/progress/index.ts +1 -1
- package/src/components/progress/progress.ts +1 -4
- package/src/components/progress/types.ts +1 -4
- package/src/components/radios/_styles.scss +0 -45
- package/src/components/radios/api.ts +85 -60
- package/src/components/radios/config.ts +1 -2
- package/src/components/radios/constants.ts +0 -9
- package/src/components/radios/index.ts +1 -1
- package/src/components/radios/radio.ts +34 -11
- package/src/components/radios/radios.ts +2 -1
- package/src/components/radios/types.ts +1 -7
- package/src/components/slider/_styles.scss +193 -281
- package/src/components/slider/accessibility.md +59 -0
- package/src/components/slider/api.ts +36 -101
- package/src/components/slider/config.ts +29 -78
- package/src/components/slider/constants.ts +12 -8
- package/src/components/slider/features/appearance.ts +1 -47
- package/src/components/slider/features/disabled.ts +41 -16
- package/src/components/slider/features/interactions.ts +166 -26
- package/src/components/slider/features/keyboard.ts +125 -6
- package/src/components/slider/features/structure.ts +182 -195
- package/src/components/slider/features/ui.ts +234 -303
- package/src/components/slider/index.ts +11 -1
- package/src/components/slider/slider.ts +1 -1
- package/src/components/slider/types.ts +10 -25
- package/src/components/tabs/_styles.scss +285 -155
- package/src/components/tabs/api.ts +178 -400
- package/src/components/tabs/config.ts +46 -52
- package/src/components/tabs/constants.ts +85 -8
- package/src/components/tabs/features.ts +401 -0
- package/src/components/tabs/index.ts +60 -3
- package/src/components/tabs/indicator.ts +225 -0
- package/src/components/tabs/responsive.ts +144 -0
- package/src/components/tabs/scroll-indicators.ts +149 -0
- package/src/components/tabs/state.ts +186 -0
- package/src/components/tabs/tab-api.ts +258 -0
- package/src/components/tabs/tab.ts +255 -0
- package/src/components/tabs/tabs.ts +50 -31
- package/src/components/tabs/types.ts +324 -128
- package/src/components/tabs/utils.ts +107 -0
- package/src/components/textfield/_styles.scss +0 -98
- package/src/components/textfield/config.ts +2 -3
- package/src/components/textfield/constants.ts +0 -14
- package/src/components/textfield/index.ts +2 -2
- package/src/components/textfield/textfield.ts +0 -2
- package/src/components/textfield/types.ts +1 -4
- package/src/core/compose/component.ts +1 -1
- package/src/core/compose/features/badge.ts +79 -0
- package/src/core/compose/features/index.ts +3 -1
- package/src/styles/abstract/_theme.scss +106 -2
- package/src/components/card/actions.ts +0 -48
- package/src/components/card/header.ts +0 -88
- package/src/components/card/media.ts +0 -52
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/slider/features/interactions.ts
|
|
2
|
-
import { SLIDER_EVENTS
|
|
2
|
+
import { SLIDER_EVENTS } from '../constants';
|
|
3
3
|
import { SliderConfig, SliderEvent } from '../types';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -42,7 +42,106 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
|
|
|
42
42
|
triggerEvent = () => ({ defaultPrevented: false })
|
|
43
43
|
} = handlers;
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Clear any existing bubble hide timers
|
|
47
|
+
*/
|
|
48
|
+
const clearBubbleHideTimer = () => {
|
|
49
|
+
if (state.valueHideTimer) {
|
|
50
|
+
clearTimeout(state.valueHideTimer);
|
|
51
|
+
state.valueHideTimer = null;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Hide all bubbles immediately
|
|
57
|
+
*/
|
|
58
|
+
const hideAllBubbles = () => {
|
|
59
|
+
// Clear any pending hide timers
|
|
60
|
+
clearBubbleHideTimer();
|
|
61
|
+
|
|
62
|
+
// Hide both bubbles immediately
|
|
63
|
+
if (valueBubble) {
|
|
64
|
+
showValueBubble(valueBubble, false);
|
|
65
|
+
}
|
|
66
|
+
if (secondValueBubble) {
|
|
67
|
+
showValueBubble(secondValueBubble, false);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Clear keyboard focus indicators across all sliders in the document
|
|
73
|
+
* Not just for this instance, but for any slider thumb
|
|
74
|
+
*/
|
|
75
|
+
const clearGlobalKeyboardFocus = () => {
|
|
76
|
+
// First clear local focus indicators
|
|
77
|
+
if (thumb) {
|
|
78
|
+
thumb.classList.remove(`${state.component.getClass('slider-thumb')}--focused`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (secondThumb) {
|
|
82
|
+
secondThumb.classList.remove(`${state.component.getClass('slider-thumb')}--focused`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Now look for all slider thumbs in the document with the focused class
|
|
86
|
+
// This covers cases where we switch between sliders
|
|
87
|
+
try {
|
|
88
|
+
const focusClass = state.component.getClass('slider-thumb--focused');
|
|
89
|
+
const allFocusedThumbs = document.querySelectorAll(`.${focusClass}`);
|
|
90
|
+
|
|
91
|
+
// Remove focus class from all thumbs
|
|
92
|
+
allFocusedThumbs.forEach(element => {
|
|
93
|
+
element.classList.remove(focusClass);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Also blur the active element if it's a thumb
|
|
97
|
+
if (document.activeElement &&
|
|
98
|
+
document.activeElement.classList.contains(state.component.getClass('slider-thumb'))) {
|
|
99
|
+
(document.activeElement as HTMLElement).blur();
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.warn('Error clearing global keyboard focus:', error);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Show the active bubble with consistent behavior
|
|
108
|
+
* @param bubble Bubble element to show
|
|
109
|
+
*/
|
|
110
|
+
const showActiveBubble = (bubble) => {
|
|
111
|
+
// First hide all bubbles
|
|
112
|
+
hideAllBubbles();
|
|
113
|
+
|
|
114
|
+
// Then show the active bubble if allowed by config
|
|
115
|
+
if (bubble && config.showValue) {
|
|
116
|
+
showValueBubble(bubble, true);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Hide the active bubble with optional delay
|
|
122
|
+
* @param bubble Bubble element to hide
|
|
123
|
+
* @param delay Delay in milliseconds before hiding
|
|
124
|
+
*/
|
|
125
|
+
const hideActiveBubble = (bubble, delay = 0) => {
|
|
126
|
+
// Clear any existing timers first
|
|
127
|
+
clearBubbleHideTimer();
|
|
128
|
+
|
|
129
|
+
if (!bubble || !config.showValue) return;
|
|
130
|
+
|
|
131
|
+
if (delay > 0) {
|
|
132
|
+
// Set delayed hide
|
|
133
|
+
state.valueHideTimer = setTimeout(() => {
|
|
134
|
+
showValueBubble(bubble, false);
|
|
135
|
+
}, delay);
|
|
136
|
+
} else {
|
|
137
|
+
// Hide immediately
|
|
138
|
+
showValueBubble(bubble, false);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Handle thumb mouse down with improved bubble handling
|
|
144
|
+
*/
|
|
46
145
|
const handleThumbMouseDown = (e, isSecondThumb = false) => {
|
|
47
146
|
// Verify component exists and check if disabled
|
|
48
147
|
if (!state.component || (state.component.disabled && state.component.disabled.isDisabled())) {
|
|
@@ -52,14 +151,21 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
|
|
|
52
151
|
e.preventDefault();
|
|
53
152
|
e.stopPropagation();
|
|
54
153
|
|
|
154
|
+
// First hide any existing visible bubbles
|
|
155
|
+
hideAllBubbles();
|
|
156
|
+
|
|
157
|
+
// Clear any keyboard focus indicators globally
|
|
158
|
+
clearGlobalKeyboardFocus();
|
|
159
|
+
|
|
55
160
|
state.dragging = true;
|
|
56
161
|
state.activeThumb = isSecondThumb ? secondThumb : thumb;
|
|
57
162
|
state.activeBubble = isSecondThumb ? secondValueBubble : valueBubble;
|
|
58
163
|
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
164
|
+
// Add dragging class to component element to style the thumb differently
|
|
165
|
+
state.component.element.classList.add(`${state.component.getClass('slider')}--dragging`);
|
|
166
|
+
|
|
167
|
+
// Show active bubble
|
|
168
|
+
showActiveBubble(state.activeBubble);
|
|
63
169
|
|
|
64
170
|
// Add global event listeners
|
|
65
171
|
document.addEventListener('mousemove', handleMouseMove);
|
|
@@ -75,6 +181,9 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
|
|
|
75
181
|
}
|
|
76
182
|
};
|
|
77
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Handle track mouse down with improved bubble handling
|
|
186
|
+
*/
|
|
78
187
|
const handleTrackMouseDown = (e) => {
|
|
79
188
|
// Verify component exists and check if disabled
|
|
80
189
|
if (!state.component || (state.component.disabled && state.component.disabled.isDisabled()) || !track) {
|
|
@@ -83,21 +192,26 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
|
|
|
83
192
|
|
|
84
193
|
e.preventDefault();
|
|
85
194
|
|
|
195
|
+
// Hide any existing visible bubbles
|
|
196
|
+
hideAllBubbles();
|
|
197
|
+
|
|
198
|
+
// Clear any keyboard focus indicators globally
|
|
199
|
+
clearGlobalKeyboardFocus();
|
|
200
|
+
|
|
86
201
|
// Determine which thumb to move based on click position
|
|
87
202
|
let isSecondThumb = false;
|
|
88
203
|
|
|
89
204
|
try {
|
|
90
205
|
// Get track rect for calculating position
|
|
91
206
|
const trackRect = track.getBoundingClientRect();
|
|
92
|
-
const isVertical = config.orientation === SLIDER_ORIENTATIONS.VERTICAL;
|
|
93
207
|
|
|
94
208
|
// Get position from mouse or touch event
|
|
95
209
|
const position = e.type.includes('touch')
|
|
96
|
-
?
|
|
97
|
-
:
|
|
210
|
+
? e.touches[0].clientX
|
|
211
|
+
: e.clientX;
|
|
98
212
|
|
|
99
213
|
// Calculate value at click position
|
|
100
|
-
let newValue = getValueFromPosition(position
|
|
214
|
+
let newValue = getValueFromPosition(position);
|
|
101
215
|
|
|
102
216
|
// Round to step if needed
|
|
103
217
|
if (config.snapToSteps && state.step > 0) {
|
|
@@ -143,6 +257,9 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
|
|
|
143
257
|
handleThumbMouseDown(e, isSecondThumb);
|
|
144
258
|
};
|
|
145
259
|
|
|
260
|
+
/**
|
|
261
|
+
* Handle mouse move with improved thumb and bubble switching
|
|
262
|
+
*/
|
|
146
263
|
const handleMouseMove = (e) => {
|
|
147
264
|
if (!state.dragging || !state.activeThumb) return;
|
|
148
265
|
|
|
@@ -150,13 +267,12 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
|
|
|
150
267
|
|
|
151
268
|
try {
|
|
152
269
|
// Get position
|
|
153
|
-
const isVertical = config.orientation === SLIDER_ORIENTATIONS.VERTICAL;
|
|
154
270
|
const position = e.type.includes('touch')
|
|
155
|
-
?
|
|
156
|
-
:
|
|
271
|
+
? e.touches[0].clientX
|
|
272
|
+
: e.clientX;
|
|
157
273
|
|
|
158
274
|
// Calculate new value
|
|
159
|
-
let newValue = getValueFromPosition(position
|
|
275
|
+
let newValue = getValueFromPosition(position);
|
|
160
276
|
|
|
161
277
|
// Round to step if needed
|
|
162
278
|
if (config.snapToSteps && state.step > 0) {
|
|
@@ -172,30 +288,50 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
|
|
|
172
288
|
// For range slider, ensure thumbs don't cross
|
|
173
289
|
if (config.range && state.secondValue !== null) {
|
|
174
290
|
if (isSecondThumb) {
|
|
291
|
+
// Second thumb is active
|
|
292
|
+
|
|
175
293
|
// Don't allow second thumb to go below first thumb
|
|
176
|
-
if (newValue
|
|
294
|
+
if (newValue >= state.value) {
|
|
177
295
|
state.secondValue = newValue;
|
|
178
296
|
} else {
|
|
179
|
-
// Thumbs are crossed, swap them
|
|
297
|
+
// Thumbs are crossed, need to swap them
|
|
298
|
+
|
|
299
|
+
// First hide current bubble
|
|
300
|
+
hideActiveBubble(state.activeBubble, 0);
|
|
301
|
+
|
|
302
|
+
// Swap values
|
|
180
303
|
state.secondValue = state.value;
|
|
181
304
|
state.value = newValue;
|
|
182
305
|
|
|
183
|
-
// Swap active
|
|
306
|
+
// Swap active elements
|
|
184
307
|
state.activeThumb = thumb;
|
|
185
308
|
state.activeBubble = valueBubble;
|
|
309
|
+
|
|
310
|
+
// Show new active bubble
|
|
311
|
+
showActiveBubble(state.activeBubble);
|
|
186
312
|
}
|
|
187
313
|
} else {
|
|
314
|
+
// First thumb is active
|
|
315
|
+
|
|
188
316
|
// Don't allow first thumb to go above second thumb
|
|
189
|
-
if (newValue
|
|
317
|
+
if (newValue <= state.secondValue) {
|
|
190
318
|
state.value = newValue;
|
|
191
319
|
} else {
|
|
192
|
-
// Thumbs are crossed, swap them
|
|
320
|
+
// Thumbs are crossed, need to swap them
|
|
321
|
+
|
|
322
|
+
// First hide current bubble
|
|
323
|
+
hideActiveBubble(state.activeBubble, 0);
|
|
324
|
+
|
|
325
|
+
// Swap values
|
|
193
326
|
state.value = state.secondValue;
|
|
194
327
|
state.secondValue = newValue;
|
|
195
328
|
|
|
196
|
-
// Swap active
|
|
329
|
+
// Swap active elements
|
|
197
330
|
state.activeThumb = secondThumb;
|
|
198
331
|
state.activeBubble = secondValueBubble;
|
|
332
|
+
|
|
333
|
+
// Show new active bubble
|
|
334
|
+
showActiveBubble(state.activeBubble);
|
|
199
335
|
}
|
|
200
336
|
}
|
|
201
337
|
} else {
|
|
@@ -213,6 +349,9 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
|
|
|
213
349
|
}
|
|
214
350
|
};
|
|
215
351
|
|
|
352
|
+
/**
|
|
353
|
+
* Handle mouse up with consistent bubble hiding
|
|
354
|
+
*/
|
|
216
355
|
const handleMouseUp = (e) => {
|
|
217
356
|
if (!state.dragging) return;
|
|
218
357
|
|
|
@@ -220,10 +359,12 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
|
|
|
220
359
|
|
|
221
360
|
state.dragging = false;
|
|
222
361
|
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
362
|
+
// Remove dragging class from component element
|
|
363
|
+
state.component.element.classList.remove(`${state.component.getClass('slider')}--dragging`);
|
|
364
|
+
|
|
365
|
+
// Hide bubble with delay
|
|
366
|
+
const currentBubble = state.activeBubble;
|
|
367
|
+
hideActiveBubble(currentBubble, 1000); // Hide after 1 second
|
|
227
368
|
|
|
228
369
|
// Remove global event listeners
|
|
229
370
|
document.removeEventListener('mousemove', handleMouseMove);
|
|
@@ -231,9 +372,8 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
|
|
|
231
372
|
document.removeEventListener('touchmove', handleMouseMove);
|
|
232
373
|
document.removeEventListener('touchend', handleMouseUp);
|
|
233
374
|
|
|
234
|
-
// Reset active
|
|
375
|
+
// Reset active thumb
|
|
235
376
|
state.activeThumb = null;
|
|
236
|
-
state.activeBubble = null;
|
|
237
377
|
|
|
238
378
|
try {
|
|
239
379
|
// Trigger change event (only when done dragging)
|
|
@@ -21,12 +21,73 @@ export const createKeyboardHandlers = (state, handlers) => {
|
|
|
21
21
|
triggerEvent
|
|
22
22
|
} = handlers;
|
|
23
23
|
|
|
24
|
+
// Last focused thumb tracker to handle tab sequences properly
|
|
25
|
+
let lastFocusedThumb = null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Clear any existing bubble hide timers
|
|
29
|
+
*/
|
|
30
|
+
const clearBubbleHideTimer = () => {
|
|
31
|
+
if (state.valueHideTimer) {
|
|
32
|
+
clearTimeout(state.valueHideTimer);
|
|
33
|
+
state.valueHideTimer = null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Hide all bubbles immediately
|
|
39
|
+
*/
|
|
40
|
+
const hideAllBubbles = () => {
|
|
41
|
+
// Clear any pending hide timers
|
|
42
|
+
clearBubbleHideTimer();
|
|
43
|
+
|
|
44
|
+
// Hide both bubbles immediately
|
|
45
|
+
if (valueBubble) {
|
|
46
|
+
showValueBubble(valueBubble, false);
|
|
47
|
+
}
|
|
48
|
+
if (secondValueBubble) {
|
|
49
|
+
showValueBubble(secondValueBubble, false);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Shows a bubble element with consistent behavior
|
|
55
|
+
*/
|
|
56
|
+
const showBubble = (bubble) => {
|
|
57
|
+
// First hide all bubbles
|
|
58
|
+
hideAllBubbles();
|
|
59
|
+
|
|
60
|
+
// Then show the active bubble
|
|
61
|
+
if (bubble) {
|
|
62
|
+
showValueBubble(bubble, true);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Hides a bubble element with optional delay
|
|
68
|
+
*/
|
|
69
|
+
const hideBubble = (bubble, delay = 0) => {
|
|
70
|
+
// Clear any existing timers
|
|
71
|
+
clearBubbleHideTimer();
|
|
72
|
+
|
|
73
|
+
if (!bubble) return;
|
|
74
|
+
|
|
75
|
+
if (delay > 0) {
|
|
76
|
+
state.valueHideTimer = setTimeout(() => {
|
|
77
|
+
showValueBubble(bubble, false);
|
|
78
|
+
}, delay);
|
|
79
|
+
} else {
|
|
80
|
+
showValueBubble(bubble, false);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
24
84
|
// Event handlers
|
|
25
85
|
const handleKeyDown = (e, isSecondThumb = false) => {
|
|
26
86
|
if (state.component.disabled && state.component.disabled.isDisabled()) return;
|
|
27
87
|
|
|
28
88
|
const step = state.step || 1;
|
|
29
89
|
let newValue;
|
|
90
|
+
let stepSize = step;
|
|
30
91
|
|
|
31
92
|
// Determine which value to modify
|
|
32
93
|
if (isSecondThumb) {
|
|
@@ -35,43 +96,71 @@ export const createKeyboardHandlers = (state, handlers) => {
|
|
|
35
96
|
newValue = state.value;
|
|
36
97
|
}
|
|
37
98
|
|
|
99
|
+
// Handle tab key specifically for range sliders
|
|
100
|
+
if (e.key === 'Tab') {
|
|
101
|
+
// Let the browser handle the tab navigation
|
|
102
|
+
// We'll deal with showing/hiding bubbles in the focus/blur handlers
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Determine step size based on modifier keys
|
|
107
|
+
if (e.shiftKey) {
|
|
108
|
+
stepSize = step * 10; // Large step when Shift is pressed
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let valueChanged = false;
|
|
112
|
+
|
|
38
113
|
switch (e.key) {
|
|
39
114
|
case 'ArrowRight':
|
|
40
115
|
case 'ArrowUp':
|
|
41
116
|
e.preventDefault();
|
|
42
|
-
newValue = Math.min(newValue +
|
|
117
|
+
newValue = Math.min(newValue + stepSize, state.max);
|
|
118
|
+
valueChanged = true;
|
|
43
119
|
break;
|
|
44
120
|
|
|
45
121
|
case 'ArrowLeft':
|
|
46
122
|
case 'ArrowDown':
|
|
47
123
|
e.preventDefault();
|
|
48
|
-
newValue = Math.max(newValue -
|
|
124
|
+
newValue = Math.max(newValue - stepSize, state.min);
|
|
125
|
+
valueChanged = true;
|
|
49
126
|
break;
|
|
50
127
|
|
|
51
128
|
case 'Home':
|
|
52
129
|
e.preventDefault();
|
|
53
130
|
newValue = state.min;
|
|
131
|
+
valueChanged = true;
|
|
54
132
|
break;
|
|
55
133
|
|
|
56
134
|
case 'End':
|
|
57
135
|
e.preventDefault();
|
|
58
136
|
newValue = state.max;
|
|
137
|
+
valueChanged = true;
|
|
59
138
|
break;
|
|
60
139
|
|
|
61
140
|
case 'PageUp':
|
|
62
141
|
e.preventDefault();
|
|
63
142
|
newValue = Math.min(newValue + (step * 10), state.max);
|
|
143
|
+
valueChanged = true;
|
|
64
144
|
break;
|
|
65
145
|
|
|
66
146
|
case 'PageDown':
|
|
67
147
|
e.preventDefault();
|
|
68
148
|
newValue = Math.max(newValue - (step * 10), state.min);
|
|
149
|
+
valueChanged = true;
|
|
69
150
|
break;
|
|
70
151
|
|
|
71
152
|
default:
|
|
72
153
|
return; // Exit if not a handled key
|
|
73
154
|
}
|
|
74
155
|
|
|
156
|
+
if (!valueChanged) return;
|
|
157
|
+
|
|
158
|
+
// Update active bubble reference
|
|
159
|
+
state.activeBubble = isSecondThumb ? secondValueBubble : valueBubble;
|
|
160
|
+
|
|
161
|
+
// Show value bubble during keyboard interaction
|
|
162
|
+
showBubble(state.activeBubble);
|
|
163
|
+
|
|
75
164
|
// Update the value
|
|
76
165
|
if (isSecondThumb) {
|
|
77
166
|
state.secondValue = newValue;
|
|
@@ -90,16 +179,46 @@ export const createKeyboardHandlers = (state, handlers) => {
|
|
|
90
179
|
const handleFocus = (e, isSecondThumb = false) => {
|
|
91
180
|
if (state.component.disabled && state.component.disabled.isDisabled()) return;
|
|
92
181
|
|
|
93
|
-
//
|
|
94
|
-
|
|
182
|
+
// Track the currently focused thumb for tab sequence handling
|
|
183
|
+
const currentThumb = isSecondThumb ? secondThumb : state.component.structure.thumb;
|
|
184
|
+
|
|
185
|
+
// If we're tabbing between thumbs, hide the previous bubble immediately
|
|
186
|
+
if (lastFocusedThumb && lastFocusedThumb !== currentThumb) {
|
|
187
|
+
hideAllBubbles();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Update the last focused thumb
|
|
191
|
+
lastFocusedThumb = currentThumb;
|
|
192
|
+
|
|
193
|
+
// Add a class to indicate keyboard focus
|
|
194
|
+
currentThumb.classList.add(`${state.component.getClass('slider-thumb')}--focused`);
|
|
195
|
+
|
|
196
|
+
// Show value bubble on focus
|
|
197
|
+
const bubble = isSecondThumb ? secondValueBubble : valueBubble;
|
|
198
|
+
showBubble(bubble);
|
|
199
|
+
|
|
200
|
+
// Update active bubble reference
|
|
201
|
+
state.activeBubble = bubble;
|
|
95
202
|
|
|
96
203
|
// Trigger focus event
|
|
97
204
|
triggerEvent(SLIDER_EVENTS.FOCUS, e);
|
|
98
205
|
};
|
|
99
206
|
|
|
100
207
|
const handleBlur = (e, isSecondThumb = false) => {
|
|
101
|
-
//
|
|
102
|
-
|
|
208
|
+
// Remove keyboard focus class
|
|
209
|
+
const thumb = isSecondThumb ? secondThumb : state.component.structure.thumb;
|
|
210
|
+
thumb.classList.remove(`${state.component.getClass('slider-thumb')}--focused`);
|
|
211
|
+
|
|
212
|
+
// Only hide the bubble if we're not tabbing to another thumb
|
|
213
|
+
// This check prevents the bubble from flickering when tabbing between thumbs
|
|
214
|
+
const relatedTarget = e.relatedTarget;
|
|
215
|
+
const otherThumb = isSecondThumb ? state.component.structure.thumb : secondThumb;
|
|
216
|
+
|
|
217
|
+
if (!relatedTarget || relatedTarget !== otherThumb) {
|
|
218
|
+
// We're not tabbing to the other thumb, so we can hide the bubble
|
|
219
|
+
const bubble = isSecondThumb ? secondValueBubble : valueBubble;
|
|
220
|
+
hideBubble(bubble, 200);
|
|
221
|
+
}
|
|
103
222
|
|
|
104
223
|
// Trigger blur event
|
|
105
224
|
triggerEvent(SLIDER_EVENTS.BLUR, e);
|