mtrl 0.2.6 → 0.2.7

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 (147) hide show
  1. package/index.ts +18 -0
  2. package/package.json +1 -1
  3. package/src/components/badge/_styles.scss +117 -109
  4. package/src/components/badge/api.ts +57 -59
  5. package/src/components/badge/badge.ts +16 -2
  6. package/src/components/badge/config.ts +65 -11
  7. package/src/components/badge/constants.ts +22 -12
  8. package/src/components/badge/features.ts +44 -40
  9. package/src/components/badge/types.ts +42 -30
  10. package/src/components/bottom-app-bar/_styles.scss +103 -0
  11. package/src/components/bottom-app-bar/bottom-app-bar.ts +196 -0
  12. package/src/components/bottom-app-bar/config.ts +73 -0
  13. package/src/components/bottom-app-bar/index.ts +11 -0
  14. package/src/components/bottom-app-bar/types.ts +108 -0
  15. package/src/components/button/_styles.scss +0 -10
  16. package/src/components/button/api.ts +5 -0
  17. package/src/components/button/config.ts +5 -0
  18. package/src/components/button/types.ts +6 -0
  19. package/src/components/card/card.ts +13 -25
  20. package/src/components/card/config.ts +67 -22
  21. package/src/components/card/features.ts +3 -0
  22. package/src/components/card/types.ts +28 -0
  23. package/src/components/checkbox/_styles.scss +0 -2
  24. package/src/components/datepicker/_styles.scss +358 -0
  25. package/src/components/datepicker/api.ts +272 -0
  26. package/src/components/datepicker/config.ts +144 -0
  27. package/src/components/datepicker/constants.ts +98 -0
  28. package/src/components/datepicker/datepicker.ts +346 -0
  29. package/src/components/datepicker/index.ts +9 -0
  30. package/src/components/datepicker/render.ts +452 -0
  31. package/src/components/datepicker/types.ts +268 -0
  32. package/src/components/datepicker/utils.ts +290 -0
  33. package/src/components/dialog/_styles.scss +174 -128
  34. package/src/components/dialog/api.ts +48 -13
  35. package/src/components/dialog/config.ts +9 -5
  36. package/src/components/dialog/dialog.ts +6 -3
  37. package/src/components/dialog/features.ts +290 -130
  38. package/src/components/dialog/types.ts +7 -4
  39. package/src/components/divider/_styles.scss +57 -0
  40. package/src/components/divider/config.ts +81 -0
  41. package/src/components/divider/divider.ts +37 -0
  42. package/src/components/divider/features.ts +207 -0
  43. package/src/components/divider/index.ts +5 -0
  44. package/src/components/divider/types.ts +55 -0
  45. package/src/components/extended-fab/_styles.scss +267 -0
  46. package/src/components/extended-fab/api.ts +141 -0
  47. package/src/components/extended-fab/config.ts +108 -0
  48. package/src/components/extended-fab/constants.ts +36 -0
  49. package/src/components/extended-fab/extended-fab.ts +125 -0
  50. package/src/components/extended-fab/index.ts +4 -0
  51. package/src/components/extended-fab/types.ts +287 -0
  52. package/src/components/fab/_styles.scss +225 -0
  53. package/src/components/fab/api.ts +97 -0
  54. package/src/components/fab/config.ts +94 -0
  55. package/src/components/fab/constants.ts +41 -0
  56. package/src/components/fab/fab.ts +67 -0
  57. package/src/components/fab/index.ts +4 -0
  58. package/src/components/fab/types.ts +234 -0
  59. package/src/components/navigation/_styles.scss +1 -0
  60. package/src/components/navigation/api.ts +78 -50
  61. package/src/components/navigation/features/items.ts +280 -0
  62. package/src/components/navigation/nav-item.ts +72 -23
  63. package/src/components/navigation/navigation.ts +54 -2
  64. package/src/components/navigation/types.ts +210 -188
  65. package/src/components/search/_styles.scss +306 -0
  66. package/src/components/search/api.ts +203 -0
  67. package/src/components/search/config.ts +87 -0
  68. package/src/components/search/constants.ts +21 -0
  69. package/src/components/search/features/index.ts +4 -0
  70. package/src/components/search/features/search.ts +718 -0
  71. package/src/components/search/features/states.ts +165 -0
  72. package/src/components/search/features/structure.ts +198 -0
  73. package/src/components/search/index.ts +10 -0
  74. package/src/components/search/search.ts +52 -0
  75. package/src/components/search/types.ts +163 -0
  76. package/src/components/segmented-button/_styles.scss +117 -0
  77. package/src/components/segmented-button/config.ts +67 -0
  78. package/src/components/segmented-button/constants.ts +42 -0
  79. package/src/components/segmented-button/index.ts +4 -0
  80. package/src/components/segmented-button/segment.ts +155 -0
  81. package/src/components/segmented-button/segmented-button.ts +250 -0
  82. package/src/components/segmented-button/types.ts +219 -0
  83. package/src/components/slider/_styles.scss +83 -24
  84. package/src/components/slider/accessibility.md +5 -5
  85. package/src/components/slider/api.ts +41 -120
  86. package/src/components/slider/config.ts +51 -47
  87. package/src/components/slider/features/handlers.ts +495 -0
  88. package/src/components/slider/features/index.ts +1 -2
  89. package/src/components/slider/features/slider.ts +66 -84
  90. package/src/components/slider/features/states.ts +195 -0
  91. package/src/components/slider/features/structure.ts +136 -206
  92. package/src/components/slider/features/ui.ts +145 -206
  93. package/src/components/slider/index.ts +2 -11
  94. package/src/components/slider/slider.ts +9 -12
  95. package/src/components/slider/types.ts +39 -24
  96. package/src/components/switch/_styles.scss +0 -2
  97. package/src/components/tabs/_styles.scss +94 -32
  98. package/src/components/tabs/features.ts +4 -2
  99. package/src/components/tabs/indicator.ts +73 -13
  100. package/src/components/tabs/types.ts +10 -2
  101. package/src/components/timepicker/README.md +277 -0
  102. package/src/components/timepicker/_styles.scss +451 -0
  103. package/src/components/timepicker/api.ts +632 -0
  104. package/src/components/timepicker/clockdial.ts +482 -0
  105. package/src/components/timepicker/config.ts +130 -0
  106. package/src/components/timepicker/constants.ts +138 -0
  107. package/src/components/timepicker/index.ts +8 -0
  108. package/src/components/timepicker/render.ts +613 -0
  109. package/src/components/timepicker/timepicker.ts +117 -0
  110. package/src/components/timepicker/types.ts +336 -0
  111. package/src/components/timepicker/utils.ts +241 -0
  112. package/src/components/top-app-bar/_styles.scss +225 -0
  113. package/src/components/top-app-bar/config.ts +83 -0
  114. package/src/components/top-app-bar/index.ts +11 -0
  115. package/src/components/top-app-bar/top-app-bar.ts +316 -0
  116. package/src/components/top-app-bar/types.ts +140 -0
  117. package/src/core/build/_ripple.scss +6 -6
  118. package/src/core/build/ripple.ts +72 -95
  119. package/src/core/compose/features/icon.ts +3 -1
  120. package/src/core/compose/features/ripple.ts +4 -1
  121. package/src/core/compose/features/textlabel.ts +26 -2
  122. package/src/core/dom/create.ts +5 -0
  123. package/src/index.ts +9 -0
  124. package/src/styles/abstract/_theme.scss +9 -1
  125. package/src/styles/themes/_autumn.scss +21 -0
  126. package/src/styles/themes/_base-theme.scss +61 -0
  127. package/src/styles/themes/_baseline.scss +58 -0
  128. package/src/styles/themes/_bluekhaki.scss +125 -0
  129. package/src/styles/themes/_brownbeige.scss +125 -0
  130. package/src/styles/themes/_browngreen.scss +125 -0
  131. package/src/styles/themes/_forest.scss +6 -0
  132. package/src/styles/themes/_greenbeige.scss +125 -0
  133. package/src/styles/themes/_material.scss +125 -0
  134. package/src/styles/themes/_ocean.scss +6 -0
  135. package/src/styles/themes/_sageivory.scss +125 -0
  136. package/src/styles/themes/_spring.scss +6 -0
  137. package/src/styles/themes/_summer.scss +5 -0
  138. package/src/styles/themes/_sunset.scss +5 -0
  139. package/src/styles/themes/_tealcaramel.scss +125 -0
  140. package/src/styles/themes/_winter.scss +6 -0
  141. package/src/components/navigation/features/items.js +0 -192
  142. package/src/components/slider/features/appearance.ts +0 -94
  143. package/src/components/slider/features/disabled.ts +0 -68
  144. package/src/components/slider/features/events.ts +0 -164
  145. package/src/components/slider/features/interactions.ts +0 -396
  146. package/src/components/slider/features/keyboard.ts +0 -233
  147. package/src/core/collection/adapters/mongodb.js +0 -232
@@ -1,396 +0,0 @@
1
- // src/components/slider/features/interactions.ts
2
- import { SLIDER_EVENTS } from '../constants';
3
- import { SliderConfig, SliderEvent } from '../types';
4
-
5
- /**
6
- * Add interaction event handlers to slider component
7
- * This function contains the core event handlers for mouse/touch interactions
8
- *
9
- * @param config Slider configuration
10
- * @param state Slider state object
11
- * @param handlers Object containing handler methods
12
- * @returns Event handlers for slider interactions
13
- */
14
- export const createInteractionHandlers = (config: SliderConfig, state, handlers) => {
15
- // Ensure state and handlers exist
16
- if (!state || !handlers) {
17
- console.error('Cannot create interaction handlers: state or handlers missing');
18
- return {
19
- handleThumbMouseDown: () => {},
20
- handleTrackMouseDown: () => {},
21
- handleMouseMove: () => {},
22
- handleMouseUp: () => {}
23
- };
24
- }
25
-
26
- // Get required elements from structure (with fallbacks)
27
- const {
28
- track = null,
29
- thumb = null,
30
- valueBubble = null,
31
- secondThumb = null,
32
- secondValueBubble = null
33
- } = state.component?.structure || {};
34
-
35
- // Get required handler methods (with fallbacks)
36
- const {
37
- getValueFromPosition = () => 0,
38
- roundToStep = value => value,
39
- clamp = (value, min, max) => value,
40
- showValueBubble = () => {},
41
- updateUi = () => {},
42
- triggerEvent = () => ({ defaultPrevented: false })
43
- } = handlers;
44
-
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
- */
145
- const handleThumbMouseDown = (e, isSecondThumb = false) => {
146
- // Verify component exists and check if disabled
147
- if (!state.component || (state.component.disabled && state.component.disabled.isDisabled())) {
148
- return;
149
- }
150
-
151
- e.preventDefault();
152
- e.stopPropagation();
153
-
154
- // First hide any existing visible bubbles
155
- hideAllBubbles();
156
-
157
- // Clear any keyboard focus indicators globally
158
- clearGlobalKeyboardFocus();
159
-
160
- state.dragging = true;
161
- state.activeThumb = isSecondThumb ? secondThumb : thumb;
162
- state.activeBubble = isSecondThumb ? secondValueBubble : valueBubble;
163
-
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);
169
-
170
- // Add global event listeners
171
- document.addEventListener('mousemove', handleMouseMove);
172
- document.addEventListener('mouseup', handleMouseUp);
173
- document.addEventListener('touchmove', handleMouseMove, { passive: false });
174
- document.addEventListener('touchend', handleMouseUp);
175
-
176
- // Try to trigger start event (with error handling)
177
- try {
178
- triggerEvent(SLIDER_EVENTS.START, e);
179
- } catch (error) {
180
- console.warn('Error triggering START event:', error);
181
- }
182
- };
183
-
184
- /**
185
- * Handle track mouse down with improved bubble handling
186
- */
187
- const handleTrackMouseDown = (e) => {
188
- // Verify component exists and check if disabled
189
- if (!state.component || (state.component.disabled && state.component.disabled.isDisabled()) || !track) {
190
- return;
191
- }
192
-
193
- e.preventDefault();
194
-
195
- // Hide any existing visible bubbles
196
- hideAllBubbles();
197
-
198
- // Clear any keyboard focus indicators globally
199
- clearGlobalKeyboardFocus();
200
-
201
- // Determine which thumb to move based on click position
202
- let isSecondThumb = false;
203
-
204
- try {
205
- // Get track rect for calculating position
206
- const trackRect = track.getBoundingClientRect();
207
-
208
- // Get position from mouse or touch event
209
- const position = e.type.includes('touch')
210
- ? e.touches[0].clientX
211
- : e.clientX;
212
-
213
- // Calculate value at click position
214
- let newValue = getValueFromPosition(position);
215
-
216
- // Round to step if needed
217
- if (config.snapToSteps && state.step > 0) {
218
- newValue = roundToStep(newValue);
219
- }
220
-
221
- // Clamp value to min/max
222
- newValue = clamp(newValue, state.min, state.max);
223
-
224
- if (config.range && state.secondValue !== null) {
225
- // For range slider, determine which thumb to move (closest to click position)
226
- const distToFirst = Math.abs(newValue - state.value);
227
- const distToSecond = Math.abs(newValue - state.secondValue);
228
-
229
- isSecondThumb = distToSecond < distToFirst;
230
-
231
- // Update the appropriate value
232
- if (isSecondThumb) {
233
- state.secondValue = newValue;
234
- } else {
235
- state.value = newValue;
236
- }
237
- } else {
238
- // Single thumb slider - just update the value
239
- state.value = newValue;
240
- }
241
-
242
- // Update UI immediately
243
- updateUi();
244
-
245
- // Trigger events
246
- triggerEvent(SLIDER_EVENTS.INPUT, e);
247
- triggerEvent(SLIDER_EVENTS.CHANGE, e);
248
- } catch (error) {
249
- console.warn('Error handling track click:', error);
250
- }
251
-
252
- // Set active elements
253
- state.activeThumb = isSecondThumb ? secondThumb : thumb;
254
- state.activeBubble = isSecondThumb ? secondValueBubble : valueBubble;
255
-
256
- // Call thumb mouse down to start dragging
257
- handleThumbMouseDown(e, isSecondThumb);
258
- };
259
-
260
- /**
261
- * Handle mouse move with improved thumb and bubble switching
262
- */
263
- const handleMouseMove = (e) => {
264
- if (!state.dragging || !state.activeThumb) return;
265
-
266
- e.preventDefault();
267
-
268
- try {
269
- // Get position
270
- const position = e.type.includes('touch')
271
- ? e.touches[0].clientX
272
- : e.clientX;
273
-
274
- // Calculate new value
275
- let newValue = getValueFromPosition(position);
276
-
277
- // Round to step if needed
278
- if (config.snapToSteps && state.step > 0) {
279
- newValue = roundToStep(newValue);
280
- }
281
-
282
- // Clamp value to min/max
283
- newValue = clamp(newValue, state.min, state.max);
284
-
285
- // Check if this is the second thumb
286
- const isSecondThumb = state.activeThumb === secondThumb;
287
-
288
- // For range slider, ensure thumbs don't cross
289
- if (config.range && state.secondValue !== null) {
290
- if (isSecondThumb) {
291
- // Second thumb is active
292
-
293
- // Don't allow second thumb to go below first thumb
294
- if (newValue >= state.value) {
295
- state.secondValue = newValue;
296
- } else {
297
- // Thumbs are crossed, need to swap them
298
-
299
- // First hide current bubble
300
- hideActiveBubble(state.activeBubble, 0);
301
-
302
- // Swap values
303
- state.secondValue = state.value;
304
- state.value = newValue;
305
-
306
- // Swap active elements
307
- state.activeThumb = thumb;
308
- state.activeBubble = valueBubble;
309
-
310
- // Show new active bubble
311
- showActiveBubble(state.activeBubble);
312
- }
313
- } else {
314
- // First thumb is active
315
-
316
- // Don't allow first thumb to go above second thumb
317
- if (newValue <= state.secondValue) {
318
- state.value = newValue;
319
- } else {
320
- // Thumbs are crossed, need to swap them
321
-
322
- // First hide current bubble
323
- hideActiveBubble(state.activeBubble, 0);
324
-
325
- // Swap values
326
- state.value = state.secondValue;
327
- state.secondValue = newValue;
328
-
329
- // Swap active elements
330
- state.activeThumb = secondThumb;
331
- state.activeBubble = secondValueBubble;
332
-
333
- // Show new active bubble
334
- showActiveBubble(state.activeBubble);
335
- }
336
- }
337
- } else {
338
- // Regular slider
339
- state.value = newValue;
340
- }
341
-
342
- // Update UI
343
- updateUi();
344
-
345
- // Trigger input event (continuously while dragging)
346
- triggerEvent(SLIDER_EVENTS.INPUT, e);
347
- } catch (error) {
348
- console.warn('Error during slider drag:', error);
349
- }
350
- };
351
-
352
- /**
353
- * Handle mouse up with consistent bubble hiding
354
- */
355
- const handleMouseUp = (e) => {
356
- if (!state.dragging) return;
357
-
358
- e.preventDefault();
359
-
360
- state.dragging = false;
361
-
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
368
-
369
- // Remove global event listeners
370
- document.removeEventListener('mousemove', handleMouseMove);
371
- document.removeEventListener('mouseup', handleMouseUp);
372
- document.removeEventListener('touchmove', handleMouseMove);
373
- document.removeEventListener('touchend', handleMouseUp);
374
-
375
- // Reset active thumb
376
- state.activeThumb = null;
377
-
378
- try {
379
- // Trigger change event (only when done dragging)
380
- triggerEvent(SLIDER_EVENTS.CHANGE, e);
381
-
382
- // Trigger end event
383
- triggerEvent(SLIDER_EVENTS.END, e);
384
- } catch (error) {
385
- console.warn('Error triggering events on mouse up:', error);
386
- }
387
- };
388
-
389
- // Return handlers
390
- return {
391
- handleThumbMouseDown,
392
- handleTrackMouseDown,
393
- handleMouseMove,
394
- handleMouseUp
395
- };
396
- };
@@ -1,233 +0,0 @@
1
- // src/components/slider/features/keyboard.ts
2
- import { SLIDER_EVENTS } from '../constants';
3
-
4
- /**
5
- * Add keyboard interaction handlers to slider component
6
- *
7
- * @param state Slider state object
8
- * @param handlers Object containing handler methods
9
- * @returns Event handlers for keyboard interactions
10
- */
11
- export const createKeyboardHandlers = (state, handlers) => {
12
- const {
13
- secondThumb,
14
- secondValueBubble,
15
- valueBubble
16
- } = state.component.structure;
17
-
18
- const {
19
- showValueBubble,
20
- updateUi,
21
- triggerEvent
22
- } = handlers;
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
-
84
- // Event handlers
85
- const handleKeyDown = (e, isSecondThumb = false) => {
86
- if (state.component.disabled && state.component.disabled.isDisabled()) return;
87
-
88
- const step = state.step || 1;
89
- let newValue;
90
- let stepSize = step;
91
-
92
- // Determine which value to modify
93
- if (isSecondThumb) {
94
- newValue = state.secondValue;
95
- } else {
96
- newValue = state.value;
97
- }
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
-
113
- switch (e.key) {
114
- case 'ArrowRight':
115
- case 'ArrowUp':
116
- e.preventDefault();
117
- newValue = Math.min(newValue + stepSize, state.max);
118
- valueChanged = true;
119
- break;
120
-
121
- case 'ArrowLeft':
122
- case 'ArrowDown':
123
- e.preventDefault();
124
- newValue = Math.max(newValue - stepSize, state.min);
125
- valueChanged = true;
126
- break;
127
-
128
- case 'Home':
129
- e.preventDefault();
130
- newValue = state.min;
131
- valueChanged = true;
132
- break;
133
-
134
- case 'End':
135
- e.preventDefault();
136
- newValue = state.max;
137
- valueChanged = true;
138
- break;
139
-
140
- case 'PageUp':
141
- e.preventDefault();
142
- newValue = Math.min(newValue + (step * 10), state.max);
143
- valueChanged = true;
144
- break;
145
-
146
- case 'PageDown':
147
- e.preventDefault();
148
- newValue = Math.max(newValue - (step * 10), state.min);
149
- valueChanged = true;
150
- break;
151
-
152
- default:
153
- return; // Exit if not a handled key
154
- }
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
-
164
- // Update the value
165
- if (isSecondThumb) {
166
- state.secondValue = newValue;
167
- } else {
168
- state.value = newValue;
169
- }
170
-
171
- // Update UI
172
- updateUi();
173
-
174
- // Trigger events
175
- triggerEvent(SLIDER_EVENTS.INPUT, e);
176
- triggerEvent(SLIDER_EVENTS.CHANGE, e);
177
- };
178
-
179
- const handleFocus = (e, isSecondThumb = false) => {
180
- if (state.component.disabled && state.component.disabled.isDisabled()) return;
181
-
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;
202
-
203
- // Trigger focus event
204
- triggerEvent(SLIDER_EVENTS.FOCUS, e);
205
- };
206
-
207
- const handleBlur = (e, isSecondThumb = false) => {
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
- }
222
-
223
- // Trigger blur event
224
- triggerEvent(SLIDER_EVENTS.BLUR, e);
225
- };
226
-
227
- // Return handlers
228
- return {
229
- handleKeyDown,
230
- handleFocus,
231
- handleBlur
232
- };
233
- };