mtrl 0.2.6 → 0.2.8

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 (226) hide show
  1. package/demo/build.ts +349 -0
  2. package/demo/index.html +110 -0
  3. package/demo/main.js +448 -0
  4. package/demo/styles.css +239 -0
  5. package/index.ts +18 -0
  6. package/package.json +14 -3
  7. package/server.ts +86 -0
  8. package/src/components/badge/api.ts +70 -63
  9. package/src/components/badge/badge.ts +16 -2
  10. package/src/components/badge/config.ts +66 -13
  11. package/src/components/badge/features.ts +51 -42
  12. package/src/components/badge/index.ts +27 -2
  13. package/src/components/badge/types.ts +62 -30
  14. package/src/components/bottom-app-bar/bottom-app-bar.ts +154 -0
  15. package/src/components/bottom-app-bar/config.ts +29 -0
  16. package/src/components/bottom-app-bar/index.ts +17 -0
  17. package/src/components/bottom-app-bar/types.ts +114 -0
  18. package/src/components/button/api.ts +5 -0
  19. package/src/components/button/button.ts +0 -1
  20. package/src/components/button/config.ts +6 -2
  21. package/src/components/button/index.ts +10 -2
  22. package/src/components/button/types.ts +20 -2
  23. package/src/components/card/card.ts +13 -25
  24. package/src/components/card/config.ts +83 -30
  25. package/src/components/card/content.ts +8 -10
  26. package/src/components/card/features.ts +4 -3
  27. package/src/components/card/index.ts +29 -2
  28. package/src/components/card/types.ts +33 -22
  29. package/src/components/checkbox/config.ts +3 -4
  30. package/src/components/checkbox/index.ts +1 -2
  31. package/src/components/checkbox/types.ts +12 -3
  32. package/src/components/chip/api.ts +170 -221
  33. package/src/components/chip/chip.ts +34 -302
  34. package/src/components/chip/config.ts +1 -2
  35. package/src/components/chip/index.ts +10 -2
  36. package/src/components/chip/types.ts +224 -35
  37. package/src/components/datepicker/api.ts +265 -0
  38. package/src/components/datepicker/config.ts +141 -0
  39. package/src/components/datepicker/datepicker.ts +341 -0
  40. package/src/components/datepicker/index.ts +12 -0
  41. package/src/components/datepicker/render.ts +450 -0
  42. package/src/components/datepicker/types.ts +397 -0
  43. package/src/components/datepicker/utils.ts +289 -0
  44. package/src/components/dialog/api.ts +55 -21
  45. package/src/components/dialog/config.ts +12 -9
  46. package/src/components/dialog/dialog.ts +6 -3
  47. package/src/components/dialog/features.ts +345 -151
  48. package/src/components/dialog/index.ts +38 -8
  49. package/src/components/dialog/types.ts +40 -14
  50. package/src/components/divider/config.ts +81 -0
  51. package/src/components/divider/divider.ts +37 -0
  52. package/src/components/divider/features.ts +207 -0
  53. package/src/components/divider/index.ts +9 -0
  54. package/src/components/divider/types.ts +55 -0
  55. package/src/components/extended-fab/api.ts +141 -0
  56. package/src/components/extended-fab/config.ts +112 -0
  57. package/src/components/extended-fab/extended-fab.ts +125 -0
  58. package/src/components/extended-fab/index.ts +9 -0
  59. package/src/components/extended-fab/types.ts +304 -0
  60. package/src/components/fab/api.ts +97 -0
  61. package/src/components/fab/config.ts +93 -0
  62. package/src/components/fab/fab.ts +67 -0
  63. package/src/components/fab/index.ts +9 -0
  64. package/src/components/fab/types.ts +251 -0
  65. package/src/components/list/config.ts +4 -5
  66. package/src/components/list/features.ts +6 -7
  67. package/src/components/list/index.ts +7 -9
  68. package/src/components/list/list-item.ts +12 -13
  69. package/src/components/list/types.ts +50 -5
  70. package/src/components/list/utils.ts +30 -3
  71. package/src/components/menu/features/items-manager.ts +9 -9
  72. package/src/components/menu/features/positioning.ts +7 -7
  73. package/src/components/menu/features/visibility.ts +7 -7
  74. package/src/components/menu/index.ts +7 -9
  75. package/src/components/menu/menu-item.ts +6 -6
  76. package/src/components/menu/menu.ts +22 -0
  77. package/src/components/menu/types.ts +29 -10
  78. package/src/components/menu/utils.ts +67 -0
  79. package/src/components/navigation/api.ts +78 -50
  80. package/src/components/navigation/config.ts +22 -10
  81. package/src/components/navigation/features/items.ts +284 -0
  82. package/src/components/navigation/index.ts +0 -6
  83. package/src/components/navigation/nav-item.ts +70 -33
  84. package/src/components/navigation/navigation.ts +53 -3
  85. package/src/components/navigation/types.ts +117 -70
  86. package/src/components/progress/api.ts +2 -3
  87. package/src/components/progress/config.ts +2 -3
  88. package/src/components/progress/index.ts +0 -1
  89. package/src/components/progress/progress.ts +1 -2
  90. package/src/components/progress/types.ts +186 -33
  91. package/src/components/radios/config.ts +1 -1
  92. package/src/components/radios/index.ts +0 -1
  93. package/src/components/radios/types.ts +0 -7
  94. package/src/components/search/api.ts +203 -0
  95. package/src/components/search/config.ts +86 -0
  96. package/src/components/search/features/index.ts +4 -0
  97. package/src/components/search/features/search.ts +717 -0
  98. package/src/components/search/features/states.ts +169 -0
  99. package/src/components/search/features/structure.ts +197 -0
  100. package/src/components/search/index.ts +7 -0
  101. package/src/components/search/search.ts +52 -0
  102. package/src/components/search/types.ts +175 -0
  103. package/src/components/segmented-button/config.ts +80 -0
  104. package/src/components/segmented-button/index.ts +4 -0
  105. package/src/components/segmented-button/segment.ts +154 -0
  106. package/src/components/segmented-button/segmented-button.ts +249 -0
  107. package/src/components/segmented-button/types.ts +254 -0
  108. package/src/components/slider/accessibility.md +5 -5
  109. package/src/components/slider/api.ts +41 -120
  110. package/src/components/slider/config.ts +51 -47
  111. package/src/components/slider/features/handlers.ts +495 -0
  112. package/src/components/slider/features/index.ts +1 -2
  113. package/src/components/slider/features/slider.ts +66 -84
  114. package/src/components/slider/features/states.ts +195 -0
  115. package/src/components/slider/features/structure.ts +136 -206
  116. package/src/components/slider/features/ui.ts +145 -206
  117. package/src/components/slider/index.ts +2 -11
  118. package/src/components/slider/slider.ts +9 -12
  119. package/src/components/slider/types.ts +67 -26
  120. package/src/components/snackbar/config.ts +2 -3
  121. package/src/components/snackbar/constants.ts +0 -32
  122. package/src/components/snackbar/index.ts +0 -1
  123. package/src/components/snackbar/position.ts +9 -1
  124. package/src/components/snackbar/types.ts +122 -46
  125. package/src/components/switch/config.ts +2 -3
  126. package/src/components/switch/index.ts +0 -1
  127. package/src/components/switch/types.ts +3 -2
  128. package/src/components/tabs/config.ts +3 -4
  129. package/src/components/tabs/features.ts +4 -2
  130. package/src/components/tabs/index.ts +0 -15
  131. package/src/components/tabs/indicator.ts +73 -13
  132. package/src/components/tabs/tab-api.ts +12 -4
  133. package/src/components/tabs/tab.ts +18 -6
  134. package/src/components/tabs/types.ts +23 -5
  135. package/src/components/textfield/config.ts +2 -3
  136. package/src/components/textfield/index.ts +0 -1
  137. package/src/components/textfield/types.ts +17 -3
  138. package/src/components/timepicker/README.md +277 -0
  139. package/src/components/timepicker/api.ts +632 -0
  140. package/src/components/timepicker/clockdial.ts +482 -0
  141. package/src/components/timepicker/config.ts +228 -0
  142. package/src/components/timepicker/index.ts +3 -0
  143. package/src/components/timepicker/render.ts +613 -0
  144. package/src/components/timepicker/timepicker.ts +117 -0
  145. package/src/components/timepicker/types.ts +336 -0
  146. package/src/components/timepicker/utils.ts +241 -0
  147. package/src/components/tooltip/api.ts +1 -1
  148. package/src/components/tooltip/config.ts +27 -6
  149. package/src/components/tooltip/index.ts +0 -1
  150. package/src/components/tooltip/types.ts +13 -3
  151. package/src/components/top-app-bar/config.ts +83 -0
  152. package/src/components/top-app-bar/index.ts +11 -0
  153. package/src/components/top-app-bar/top-app-bar.ts +316 -0
  154. package/src/components/top-app-bar/types.ts +140 -0
  155. package/src/core/build/_ripple.scss +6 -6
  156. package/src/core/build/ripple.ts +72 -95
  157. package/src/core/compose/features/icon.ts +3 -1
  158. package/src/core/compose/features/ripple.ts +4 -1
  159. package/src/core/compose/features/textlabel.ts +23 -2
  160. package/src/core/dom/create.ts +5 -0
  161. package/src/index.ts +9 -0
  162. package/src/styles/abstract/_theme.scss +9 -1
  163. package/src/styles/components/_badge.scss +182 -0
  164. package/src/styles/components/_bottom-app-bar.scss +103 -0
  165. package/src/{components/button/_styles.scss → styles/components/_button.scss} +0 -10
  166. package/src/{components/checkbox/_styles.scss → styles/components/_checkbox.scss} +0 -2
  167. package/src/styles/components/_datepicker.scss +358 -0
  168. package/src/styles/components/_dialog.scss +259 -0
  169. package/src/styles/components/_divider.scss +57 -0
  170. package/src/styles/components/_extended-fab.scss +267 -0
  171. package/src/styles/components/_fab.scss +225 -0
  172. package/src/{components/navigation/_styles.scss → styles/components/_navigation.scss} +1 -0
  173. package/src/styles/components/_search.scss +306 -0
  174. package/src/styles/components/_segmented-button.scss +117 -0
  175. package/src/{components/slider/_styles.scss → styles/components/_slider.scss} +83 -24
  176. package/src/{components/switch/_styles.scss → styles/components/_switch.scss} +0 -2
  177. package/src/{components/tabs/_styles.scss → styles/components/_tabs.scss} +95 -33
  178. package/src/{components/textfield/_styles.scss → styles/components/_textfield.scss} +70 -67
  179. package/src/styles/components/_timepicker.scss +451 -0
  180. package/src/styles/components/_top-app-bar.scss +225 -0
  181. package/src/styles/main.scss +98 -49
  182. package/src/styles/themes/_autumn.scss +21 -0
  183. package/src/styles/themes/_base-theme.scss +61 -0
  184. package/src/styles/themes/_baseline.scss +58 -0
  185. package/src/styles/themes/_bluekhaki.scss +125 -0
  186. package/src/styles/themes/_brownbeige.scss +125 -0
  187. package/src/styles/themes/_browngreen.scss +125 -0
  188. package/src/styles/themes/_forest.scss +6 -0
  189. package/src/styles/themes/_greenbeige.scss +125 -0
  190. package/src/styles/themes/_material.scss +125 -0
  191. package/src/styles/themes/_ocean.scss +6 -0
  192. package/src/styles/themes/_sageivory.scss +125 -0
  193. package/src/styles/themes/_spring.scss +6 -0
  194. package/src/styles/themes/_summer.scss +5 -0
  195. package/src/styles/themes/_sunset.scss +5 -0
  196. package/src/styles/themes/_tealcaramel.scss +125 -0
  197. package/src/styles/themes/_winter.scss +6 -0
  198. package/src/components/badge/_styles.scss +0 -174
  199. package/src/components/badge/constants.ts +0 -30
  200. package/src/components/button/constants.ts +0 -11
  201. package/src/components/card/constants.ts +0 -84
  202. package/src/components/dialog/_styles.scss +0 -213
  203. package/src/components/dialog/constants.ts +0 -32
  204. package/src/components/menu/constants.ts +0 -154
  205. package/src/components/navigation/constants.ts +0 -200
  206. package/src/components/navigation/features/items.js +0 -192
  207. package/src/components/progress/constants.ts +0 -29
  208. package/src/components/slider/features/appearance.ts +0 -94
  209. package/src/components/slider/features/disabled.ts +0 -68
  210. package/src/components/slider/features/events.ts +0 -164
  211. package/src/components/slider/features/interactions.ts +0 -396
  212. package/src/components/slider/features/keyboard.ts +0 -233
  213. package/src/components/switch/constants.ts +0 -80
  214. package/src/components/tabs/constants.ts +0 -89
  215. package/src/core/collection/adapters/mongodb.js +0 -232
  216. /package/src/{components/card/_styles.scss → styles/components/_card.scss} +0 -0
  217. /package/src/{components/carousel/_styles.scss → styles/components/_carousel.scss} +0 -0
  218. /package/src/{components/chip/_styles.scss → styles/components/_chip.scss} +0 -0
  219. /package/src/{components/list/_styles.scss → styles/components/_list.scss} +0 -0
  220. /package/src/{components/menu/_styles.scss → styles/components/_menu.scss} +0 -0
  221. /package/src/{components/progress/_styles.scss → styles/components/_progress.scss} +0 -0
  222. /package/src/{components/radios/_styles.scss → styles/components/_radios.scss} +0 -0
  223. /package/src/{components/sheet/_styles.scss → styles/components/_sheet.scss} +0 -0
  224. /package/src/{components/snackbar/_styles.scss → styles/components/_snackbar.scss} +0 -0
  225. /package/src/{components/tooltip/_styles.scss → styles/components/_tooltip.scss} +0 -0
  226. /package/src/styles/utilities/{_color.scss → _colors.scss} +0 -0
@@ -0,0 +1,495 @@
1
+ // src/components/slider/features/handlers.ts
2
+ import { SLIDER_EVENTS } from '../constants';
3
+ import { SliderConfig, SliderEvent } from '../types';
4
+
5
+ /**
6
+ * Create consolidated event handlers for slider component (mouse, touch, keyboard)
7
+ *
8
+ * @param config Slider configuration
9
+ * @param state Slider state object
10
+ * @param uiHelpers UI helper methods
11
+ * @param eventHelpers Event helper methods
12
+ * @returns Event handlers for all slider interactions
13
+ */
14
+ export const createHandlers = (config: SliderConfig, state, uiHelpers, eventHelpers) => {
15
+ // Get required elements from structure (with fallbacks)
16
+ const {
17
+ container = null,
18
+ track = null,
19
+ handle = null,
20
+ valueBubble = null,
21
+ secondHandle = null,
22
+ secondValueBubble = null
23
+ } = state.component?.structure || {};
24
+
25
+ // Get required helper methods (with fallbacks)
26
+ const {
27
+ getValueFromPosition = () => 0,
28
+ roundToStep = value => value,
29
+ clamp = (value, min, max) => value,
30
+ showValueBubble = () => {},
31
+ updateUi = () => {}
32
+ } = uiHelpers;
33
+
34
+ const { triggerEvent = () => ({ defaultPrevented: false }) } = eventHelpers;
35
+
36
+ // Track whether a real drag has started
37
+ let hasActualDragStarted = false;
38
+ let initialX = 0;
39
+ const DRAG_THRESHOLD = 3;
40
+
41
+ // Last focused handle tracker for keyboard navigation
42
+ let lastFocusedHandle = null;
43
+
44
+ // Bubble management
45
+ const clearBubbleHideTimer = () => {
46
+ if (state.valueHideTimer) {
47
+ clearTimeout(state.valueHideTimer);
48
+ state.valueHideTimer = null;
49
+ }
50
+ };
51
+
52
+ const hideAllBubbles = () => {
53
+ clearBubbleHideTimer();
54
+ if (valueBubble) showValueBubble(valueBubble, false);
55
+ if (secondValueBubble) showValueBubble(secondValueBubble, false);
56
+ };
57
+
58
+ const showActiveBubble = (bubble) => {
59
+ hideAllBubbles();
60
+ if (bubble && config.showValue) showValueBubble(bubble, true);
61
+ };
62
+
63
+ const hideActiveBubble = (bubble, delay = 0) => {
64
+ clearBubbleHideTimer();
65
+ if (!bubble || !config.showValue) return;
66
+
67
+ if (delay > 0) {
68
+ state.valueHideTimer = setTimeout(() => {
69
+ showValueBubble(bubble, false);
70
+ }, delay);
71
+ } else {
72
+ showValueBubble(bubble, false);
73
+ }
74
+ };
75
+
76
+ // Focus management
77
+ const clearKeyboardFocus = () => {
78
+ // Clear local focus indicators
79
+ if (handle) handle.classList.remove(`${state.component.getClass('slider-handle')}--focused`);
80
+ if (secondHandle) secondHandle.classList.remove(`${state.component.getClass('slider-handle')}--focused`);
81
+
82
+ // Clear global focus indicators
83
+ try {
84
+ const focusClass = state.component.getClass('slider-handle--focused');
85
+ document.querySelectorAll(`.${focusClass}`).forEach(el => {
86
+ el.classList.remove(focusClass);
87
+ });
88
+
89
+ if (document.activeElement?.classList.contains(state.component.getClass('slider-handle'))) {
90
+ (document.activeElement as HTMLElement).blur();
91
+ }
92
+ } catch (error) {
93
+ console.warn('Error clearing keyboard focus:', error);
94
+ }
95
+ };
96
+
97
+ /**
98
+ * Handle mouse/touch down on a handle
99
+ */
100
+ const handleHandleMouseDown = (e, isSecondHandle = false) => {
101
+ // Check if disabled
102
+ if (!state.component || (state.component.disabled && state.component.disabled.isDisabled())) {
103
+ return;
104
+ }
105
+
106
+ e.preventDefault();
107
+ e.stopPropagation();
108
+
109
+ hideAllBubbles();
110
+ clearKeyboardFocus();
111
+
112
+ // Capture initial position
113
+ initialX = e.type.includes('touch') ? e.touches[0].clientX : e.clientX;
114
+
115
+ // Setup drag state
116
+ state.dragging = false;
117
+ hasActualDragStarted = false;
118
+ state.activeHandle = isSecondHandle ? secondHandle : handle;
119
+ state.activeBubble = isSecondHandle ? secondValueBubble : valueBubble;
120
+
121
+ // Show bubble immediately
122
+ showActiveBubble(state.activeBubble);
123
+
124
+ // Add global event listeners
125
+ document.addEventListener('mousemove', handleMouseMove);
126
+ document.addEventListener('mouseup', handleMouseUp);
127
+ document.addEventListener('touchmove', handleMouseMove, { passive: false });
128
+ document.addEventListener('touchend', handleMouseUp);
129
+
130
+ updateUi();
131
+ triggerEvent(SLIDER_EVENTS.START, e);
132
+ };
133
+
134
+ /**
135
+ * Handle mouse/touch down on the track
136
+ */
137
+ const handleTrackMouseDown = (e) => {
138
+ // Check if disabled
139
+ if (!state.component || (state.component.disabled && state.component.disabled.isDisabled()) || !track || !container) {
140
+ return;
141
+ }
142
+
143
+ e.preventDefault();
144
+ hideAllBubbles();
145
+ clearKeyboardFocus();
146
+
147
+ // Determine which handle to move
148
+ let isSecondHandle = false;
149
+
150
+ try {
151
+ const containerRect = container.getBoundingClientRect();
152
+ const position = e.type.includes('touch') ? e.touches[0].clientX : e.clientX;
153
+
154
+ // Calculate value at click position and apply constraints
155
+ let newValue = getValueFromPosition(position);
156
+ if (config.snapToSteps && state.step > 0) newValue = roundToStep(newValue);
157
+ newValue = clamp(newValue, state.min, state.max);
158
+
159
+ if (config.range && state.secondValue !== null) {
160
+ // For range slider, move the closest handle
161
+ const distToFirst = Math.abs(newValue - state.value);
162
+ const distToSecond = Math.abs(newValue - state.secondValue);
163
+
164
+ isSecondHandle = distToSecond < distToFirst;
165
+ isSecondHandle ? (state.secondValue = newValue) : (state.value = newValue);
166
+ } else {
167
+ // Single handle slider - just update value
168
+ state.value = newValue;
169
+ }
170
+
171
+ // Update UI and trigger events
172
+ updateUi();
173
+ triggerEvent(SLIDER_EVENTS.INPUT, e);
174
+ triggerEvent(SLIDER_EVENTS.CHANGE, e);
175
+ } catch (error) {
176
+ console.warn('Error handling track click:', error);
177
+ }
178
+
179
+ // Set active elements
180
+ state.activeHandle = isSecondHandle ? secondHandle : handle;
181
+ state.activeBubble = isSecondHandle ? secondValueBubble : valueBubble;
182
+
183
+ // Store the initial position and start dragging
184
+ initialX = e.type.includes('touch') ? e.touches[0].clientX : e.clientX;
185
+ handleHandleMouseDown(e, isSecondHandle);
186
+ };
187
+
188
+ /**
189
+ * Handle mouse/touch move during drag
190
+ */
191
+ const handleMouseMove = (e) => {
192
+ if (!state.activeHandle || !container) return;
193
+ e.preventDefault();
194
+
195
+ try {
196
+ // Get current position
197
+ const currentX = e.type.includes('touch') ? e.touches[0].clientX : e.clientX;
198
+
199
+ // Determine if we've started a real drag
200
+ if (!hasActualDragStarted && Math.abs(currentX - initialX) >= DRAG_THRESHOLD) {
201
+ hasActualDragStarted = true;
202
+ state.dragging = true;
203
+ state.component.element.classList.add(`${state.component.getClass('slider')}--dragging`);
204
+ }
205
+
206
+ // Calculate new value and apply constraints
207
+ let newValue = getValueFromPosition(currentX);
208
+ if (config.snapToSteps && state.step > 0) newValue = roundToStep(newValue);
209
+ newValue = clamp(newValue, state.min, state.max);
210
+
211
+ // Handle crossing points and handle swapping for range sliders
212
+ const isSecondHandle = state.activeHandle === secondHandle;
213
+
214
+ if (config.range && state.secondValue !== null) {
215
+ if (isSecondHandle) {
216
+ // Second handle is active
217
+ if (newValue >= state.value) {
218
+ state.secondValue = newValue;
219
+ } else {
220
+ // Handles are crossed, swap them
221
+ hideActiveBubble(state.activeBubble, 0);
222
+ state.secondValue = state.value;
223
+ state.value = newValue;
224
+ state.activeHandle = handle;
225
+ state.activeBubble = valueBubble;
226
+ showActiveBubble(state.activeBubble);
227
+ }
228
+ } else {
229
+ // First handle is active
230
+ if (newValue <= state.secondValue) {
231
+ state.value = newValue;
232
+ } else {
233
+ // Handles are crossed, swap them
234
+ hideActiveBubble(state.activeBubble, 0);
235
+ state.value = state.secondValue;
236
+ state.secondValue = newValue;
237
+ state.activeHandle = secondHandle;
238
+ state.activeBubble = secondValueBubble;
239
+ showActiveBubble(state.activeBubble);
240
+ }
241
+ }
242
+ } else {
243
+ // Regular slider
244
+ state.value = newValue;
245
+ }
246
+
247
+ updateUi();
248
+ triggerEvent(SLIDER_EVENTS.INPUT, e);
249
+ } catch (error) {
250
+ console.warn('Error during slider drag:', error);
251
+ }
252
+ };
253
+
254
+ /**
255
+ * Handle mouse/touch up after drag
256
+ */
257
+ const handleMouseUp = (e) => {
258
+ if (!state.activeHandle) return;
259
+ e.preventDefault();
260
+
261
+ // Reset drag states
262
+ state.dragging = false;
263
+ hasActualDragStarted = false;
264
+ state.component.element.classList.remove(`${state.component.getClass('slider')}--dragging`);
265
+
266
+ // Hide bubble with delay
267
+ hideActiveBubble(state.activeBubble, 1000);
268
+
269
+ // Remove global event listeners
270
+ document.removeEventListener('mousemove', handleMouseMove);
271
+ document.removeEventListener('mouseup', handleMouseUp);
272
+ document.removeEventListener('touchmove', handleMouseMove);
273
+ document.removeEventListener('touchend', handleMouseUp);
274
+
275
+ // Reset active handle and update UI
276
+ state.activeHandle = null;
277
+ updateUi();
278
+
279
+ // Trigger events
280
+ triggerEvent(SLIDER_EVENTS.CHANGE, e);
281
+ triggerEvent(SLIDER_EVENTS.END, e);
282
+ };
283
+
284
+ /**
285
+ * Handle keyboard input
286
+ */
287
+ const handleKeyDown = (e, isSecondHandle = false) => {
288
+ if (state.component.disabled && state.component.disabled.isDisabled()) return;
289
+
290
+ const step = state.step || 1;
291
+ let newValue = isSecondHandle ? state.secondValue : state.value;
292
+ let stepSize = e.shiftKey ? step * 10 : step;
293
+
294
+ // Handle tab key separately
295
+ if (e.key === 'Tab') return;
296
+
297
+ let valueChanged = false;
298
+
299
+ switch (e.key) {
300
+ case 'ArrowRight':
301
+ case 'ArrowUp':
302
+ e.preventDefault();
303
+ newValue = Math.min(newValue + stepSize, state.max);
304
+ valueChanged = true;
305
+ break;
306
+
307
+ case 'ArrowLeft':
308
+ case 'ArrowDown':
309
+ e.preventDefault();
310
+ newValue = Math.max(newValue - stepSize, state.min);
311
+ valueChanged = true;
312
+ break;
313
+
314
+ case 'Home':
315
+ e.preventDefault();
316
+ newValue = state.min;
317
+ valueChanged = true;
318
+ break;
319
+
320
+ case 'End':
321
+ e.preventDefault();
322
+ newValue = state.max;
323
+ valueChanged = true;
324
+ break;
325
+
326
+ case 'PageUp':
327
+ e.preventDefault();
328
+ newValue = Math.min(newValue + (step * 10), state.max);
329
+ valueChanged = true;
330
+ break;
331
+
332
+ case 'PageDown':
333
+ e.preventDefault();
334
+ newValue = Math.max(newValue - (step * 10), state.min);
335
+ valueChanged = true;
336
+ break;
337
+
338
+ default:
339
+ return; // Exit if not a handled key
340
+ }
341
+
342
+ if (!valueChanged) return;
343
+
344
+ // Update active bubble reference
345
+ state.activeBubble = isSecondHandle ? secondValueBubble : valueBubble;
346
+
347
+ // Show value bubble during keyboard interaction
348
+ showActiveBubble(state.activeBubble);
349
+
350
+ // Update the value
351
+ if (isSecondHandle) {
352
+ state.secondValue = newValue;
353
+ } else {
354
+ state.value = newValue;
355
+ }
356
+
357
+ // Update UI and trigger events
358
+ updateUi();
359
+ triggerEvent(SLIDER_EVENTS.INPUT, e);
360
+ triggerEvent(SLIDER_EVENTS.CHANGE, e);
361
+ };
362
+
363
+ /**
364
+ * Handle focus events
365
+ */
366
+ const handleFocus = (e, isSecondHandle = false) => {
367
+ if (state.component.disabled && state.component.disabled.isDisabled()) return;
368
+
369
+ // Track the currently focused handle
370
+ const currentHandle = isSecondHandle ? secondHandle : handle;
371
+
372
+ // Hide previous bubble if switching between handles
373
+ if (lastFocusedHandle && lastFocusedHandle !== currentHandle) {
374
+ hideAllBubbles();
375
+ }
376
+
377
+ lastFocusedHandle = currentHandle;
378
+
379
+ // Add focus class and show bubble
380
+ currentHandle.classList.add(`${state.component.getClass('slider-handle')}--focused`);
381
+ showActiveBubble(isSecondHandle ? secondValueBubble : valueBubble);
382
+ state.activeBubble = isSecondHandle ? secondValueBubble : valueBubble;
383
+
384
+ triggerEvent(SLIDER_EVENTS.FOCUS, e);
385
+ };
386
+
387
+ /**
388
+ * Handle blur events
389
+ */
390
+ const handleBlur = (e, isSecondHandle = false) => {
391
+ const handleElement = isSecondHandle ? secondHandle : handle;
392
+ handleElement.classList.remove(`${state.component.getClass('slider-handle')}--focused`);
393
+
394
+ // Only hide bubble if not tabbing to another handle
395
+ const relatedTarget = e.relatedTarget;
396
+ const otherHandle = isSecondHandle ? handle : secondHandle;
397
+
398
+ if (!relatedTarget || relatedTarget !== otherHandle) {
399
+ hideActiveBubble(isSecondHandle ? secondValueBubble : valueBubble, 200);
400
+ }
401
+
402
+ triggerEvent(SLIDER_EVENTS.BLUR, e);
403
+ };
404
+
405
+ /**
406
+ * Set up all event listeners
407
+ */
408
+ const setupEventListeners = () => {
409
+ if (!state.component || !state.component.structure) {
410
+ console.warn('Cannot set up event listeners: missing component structure');
411
+ return;
412
+ }
413
+
414
+ // Use container for track events instead of track for better UX
415
+ container.addEventListener('mousedown', handleTrackMouseDown);
416
+ container.addEventListener('touchstart', handleTrackMouseDown, { passive: false });
417
+
418
+ // Handle events
419
+ handle.addEventListener('mousedown', e => handleHandleMouseDown(e, false));
420
+ handle.addEventListener('touchstart', e => handleHandleMouseDown(e, false), { passive: false });
421
+ handle.addEventListener('keydown', e => handleKeyDown(e, false));
422
+ handle.addEventListener('focus', e => handleFocus(e, false));
423
+ handle.addEventListener('blur', e => handleBlur(e, false));
424
+
425
+ // Second handle events for range slider
426
+ if (config.range && secondHandle) {
427
+ secondHandle.addEventListener('mousedown', e => handleHandleMouseDown(e, true));
428
+ secondHandle.addEventListener('touchstart', e => handleHandleMouseDown(e, true), { passive: false });
429
+ secondHandle.addEventListener('keydown', e => handleKeyDown(e, true));
430
+ secondHandle.addEventListener('focus', e => handleFocus(e, true));
431
+ secondHandle.addEventListener('blur', e => handleBlur(e, true));
432
+ }
433
+ };
434
+
435
+ /**
436
+ * Clean up all event listeners
437
+ */
438
+ const cleanupEventListeners = () => {
439
+ if (!state.component || !state.component.structure) return;
440
+
441
+ // Clean up container listeners
442
+ if (container) {
443
+ container.removeEventListener('mousedown', handleTrackMouseDown);
444
+ container.removeEventListener('touchstart', handleTrackMouseDown);
445
+ }
446
+
447
+ // Clean up handle listeners
448
+ if (handle) {
449
+ handle.removeEventListener('mousedown', e => handleHandleMouseDown(e, false));
450
+ handle.removeEventListener('touchstart', e => handleHandleMouseDown(e, false));
451
+ handle.removeEventListener('keydown', e => handleKeyDown(e, false));
452
+ handle.removeEventListener('focus', e => handleFocus(e, false));
453
+ handle.removeEventListener('blur', e => handleBlur(e, false));
454
+ }
455
+
456
+ // Clean up second handle listeners
457
+ if (config.range && secondHandle) {
458
+ secondHandle.removeEventListener('mousedown', e => handleHandleMouseDown(e, true));
459
+ secondHandle.removeEventListener('touchstart', e => handleHandleMouseDown(e, true));
460
+ secondHandle.removeEventListener('keydown', e => handleKeyDown(e, true));
461
+ secondHandle.removeEventListener('focus', e => handleFocus(e, true));
462
+ secondHandle.removeEventListener('blur', e => handleBlur(e, true));
463
+ }
464
+
465
+ // Clean up document listeners
466
+ document.removeEventListener('mousemove', handleMouseMove);
467
+ document.removeEventListener('mouseup', handleMouseUp);
468
+ document.removeEventListener('touchmove', handleMouseMove);
469
+ document.removeEventListener('touchend', handleMouseUp);
470
+ };
471
+
472
+ // Return consolidated handlers
473
+ return {
474
+ // Mouse/Touch handlers
475
+ handleHandleMouseDown,
476
+ handleTrackMouseDown,
477
+ handleMouseMove,
478
+ handleMouseUp,
479
+
480
+ // Keyboard handlers
481
+ handleKeyDown,
482
+ handleFocus,
483
+ handleBlur,
484
+
485
+ // Event management
486
+ setupEventListeners,
487
+ cleanupEventListeners,
488
+
489
+ // Bubble management
490
+ showActiveBubble,
491
+ hideActiveBubble,
492
+ hideAllBubbles,
493
+ clearKeyboardFocus
494
+ };
495
+ };
@@ -1,5 +1,4 @@
1
1
  // src/components/slider/features/index.ts
2
2
  export { withStructure } from './structure';
3
- export { withDisabled } from './disabled';
4
- export { withAppearance } from './appearance';
3
+ export { withStates } from './states';
5
4
  export { withSlider } from './slider';