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
@@ -1,68 +0,0 @@
1
- // src/components/slider/features/disabled.ts
2
- import { SliderConfig } from '../types';
3
-
4
- /**
5
- * Add disabled state functionality to component
6
- * @param config Slider configuration
7
- * @returns Component enhancer with disabled functionality
8
- */
9
- export const withDisabled = (config: SliderConfig) => component => {
10
- // Initial disabled state
11
- const isDisabled = config.disabled === true;
12
-
13
- // Apply initial disabled state if needed
14
- if (isDisabled && component.structure) {
15
- setTimeout(() => {
16
- disableComponent();
17
- }, 0);
18
- }
19
-
20
- function disableComponent() {
21
- component.element.classList.add(`${component.getClass('slider')}--disabled`);
22
- component.element.setAttribute('aria-disabled', 'true');
23
-
24
- // Ensure thumbs cannot receive focus when disabled
25
- if (component.structure.thumb) {
26
- component.structure.thumb.tabIndex = -1;
27
- component.structure.thumb.setAttribute('aria-disabled', 'true');
28
- }
29
-
30
- if (config.range && component.structure.secondThumb) {
31
- component.structure.secondThumb.tabIndex = -1;
32
- component.structure.secondThumb.setAttribute('aria-disabled', 'true');
33
- }
34
- }
35
-
36
- function enableComponent() {
37
- component.element.classList.remove(`${component.getClass('slider')}--disabled`);
38
- component.element.setAttribute('aria-disabled', 'false');
39
-
40
- // Re-enable focus on thumbs
41
- if (component.structure.thumb) {
42
- component.structure.thumb.tabIndex = 0;
43
- component.structure.thumb.setAttribute('aria-disabled', 'false');
44
- }
45
-
46
- if (config.range && component.structure.secondThumb) {
47
- component.structure.secondThumb.tabIndex = 0;
48
- component.structure.secondThumb.setAttribute('aria-disabled', 'false');
49
- }
50
- }
51
-
52
- return {
53
- ...component,
54
- disabled: {
55
- enable() {
56
- enableComponent();
57
- },
58
-
59
- disable() {
60
- disableComponent();
61
- },
62
-
63
- isDisabled() {
64
- return component.element.classList.contains(`${component.getClass('slider')}--disabled`);
65
- }
66
- }
67
- };
68
- };
@@ -1,164 +0,0 @@
1
- // src/components/slider/features/events.ts
2
- import { SliderEvent } from '../types';
3
-
4
- /**
5
- * Create event helper functions for the slider component
6
- *
7
- * @param state Slider state object
8
- * @returns Event helper methods
9
- */
10
- export const createEventHelpers = (state) => {
11
- /**
12
- * Triggers a slider event
13
- * @param eventName Name of the event to trigger
14
- * @param originalEvent Original DOM event if applicable
15
- * @returns Event data object
16
- */
17
- const triggerEvent = (eventName, originalEvent = null) => {
18
- // Create event data object
19
- const eventData: SliderEvent = {
20
- slider: state.component,
21
- value: state.value,
22
- secondValue: state.secondValue,
23
- originalEvent,
24
- preventDefault: () => { eventData.defaultPrevented = true; },
25
- defaultPrevented: false
26
- };
27
-
28
- // Add a component events facade if it doesn't exist
29
- if (!state.component.events) {
30
- state.component.events = {
31
- trigger: () => {},
32
- on: () => {},
33
- off: () => {}
34
- };
35
- }
36
-
37
- // Now we can safely trigger the event
38
- state.component.events.trigger(eventName, eventData);
39
-
40
- return eventData;
41
- };
42
-
43
- /**
44
- * Set up event listeners for slider elements
45
- * @param interactionHandlers Mouse/touch interaction handlers
46
- * @param keyboardHandlers Keyboard interaction handlers
47
- */
48
- const setupEventListeners = (interactionHandlers, keyboardHandlers) => {
49
- // Ensure needed component parts exist
50
- if (!state.component || !state.component.structure) {
51
- console.warn('Cannot set up event listeners: component structure is missing');
52
- return;
53
- }
54
-
55
- const {
56
- track = null,
57
- thumb = null,
58
- secondThumb = null
59
- } = state.component.structure;
60
-
61
- if (!track || !thumb) {
62
- console.warn('Cannot set up event listeners: track or thumb is missing');
63
- return;
64
- }
65
-
66
- const {
67
- handleThumbMouseDown,
68
- handleTrackMouseDown
69
- } = interactionHandlers;
70
-
71
- const {
72
- handleKeyDown,
73
- handleFocus,
74
- handleBlur
75
- } = keyboardHandlers;
76
-
77
- // Track events
78
- track.addEventListener('mousedown', handleTrackMouseDown);
79
- track.addEventListener('touchstart', handleTrackMouseDown, { passive: false });
80
-
81
- // Thumb events
82
- thumb.addEventListener('mousedown', (e) => handleThumbMouseDown(e, false));
83
- thumb.addEventListener('touchstart', (e) => handleThumbMouseDown(e, false), { passive: false });
84
- thumb.addEventListener('keydown', (e) => handleKeyDown(e, false));
85
- thumb.addEventListener('focus', (e) => handleFocus(e, false));
86
- thumb.addEventListener('blur', (e) => handleBlur(e, false));
87
-
88
- // Second thumb events for range slider
89
- if (state.component.config && state.component.config.range && secondThumb) {
90
- secondThumb.addEventListener('mousedown', (e) => handleThumbMouseDown(e, true));
91
- secondThumb.addEventListener('touchstart', (e) => handleThumbMouseDown(e, true), { passive: false });
92
- secondThumb.addEventListener('keydown', (e) => handleKeyDown(e, true));
93
- secondThumb.addEventListener('focus', (e) => handleFocus(e, true));
94
- secondThumb.addEventListener('blur', (e) => handleBlur(e, true));
95
- }
96
- };
97
-
98
- /**
99
- * Clean up event listeners
100
- * @param interactionHandlers Mouse/touch interaction handlers
101
- * @param keyboardHandlers Keyboard interaction handlers
102
- */
103
- const cleanupEventListeners = (interactionHandlers, keyboardHandlers) => {
104
- // Ensure needed component parts exist
105
- if (!state.component || !state.component.structure) {
106
- return;
107
- }
108
-
109
- const {
110
- track = null,
111
- thumb = null,
112
- secondThumb = null
113
- } = state.component.structure;
114
-
115
- if (!track || !thumb) {
116
- return;
117
- }
118
-
119
- const {
120
- handleThumbMouseDown,
121
- handleTrackMouseDown,
122
- handleMouseMove,
123
- handleMouseUp
124
- } = interactionHandlers;
125
-
126
- const {
127
- handleKeyDown,
128
- handleFocus,
129
- handleBlur
130
- } = keyboardHandlers;
131
-
132
- // Track events
133
- track.removeEventListener('mousedown', handleTrackMouseDown);
134
- track.removeEventListener('touchstart', handleTrackMouseDown);
135
-
136
- // Thumb events
137
- thumb.removeEventListener('mousedown', (e) => handleThumbMouseDown(e, false));
138
- thumb.removeEventListener('touchstart', (e) => handleThumbMouseDown(e, false));
139
- thumb.removeEventListener('keydown', (e) => handleKeyDown(e, false));
140
- thumb.removeEventListener('focus', (e) => handleFocus(e, false));
141
- thumb.removeEventListener('blur', (e) => handleBlur(e, false));
142
-
143
- // Second thumb events
144
- if (state.component.config && state.component.config.range && secondThumb) {
145
- secondThumb.removeEventListener('mousedown', (e) => handleThumbMouseDown(e, true));
146
- secondThumb.removeEventListener('touchstart', (e) => handleThumbMouseDown(e, true));
147
- secondThumb.removeEventListener('keydown', (e) => handleKeyDown(e, true));
148
- secondThumb.removeEventListener('focus', (e) => handleFocus(e, true));
149
- secondThumb.removeEventListener('blur', (e) => handleBlur(e, true));
150
- }
151
-
152
- // Global events
153
- document.removeEventListener('mousemove', handleMouseMove);
154
- document.removeEventListener('mouseup', handleMouseUp);
155
- document.removeEventListener('touchmove', handleMouseMove);
156
- document.removeEventListener('touchend', handleMouseUp);
157
- };
158
-
159
- return {
160
- triggerEvent,
161
- setupEventListeners,
162
- cleanupEventListeners
163
- };
164
- };
@@ -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
- };