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
@@ -2,7 +2,7 @@
2
2
  import { SliderConfig } from '../types';
3
3
 
4
4
  /**
5
- * Create UI update helpers for slider component with MD3 enhancements
5
+ * Create optimized UI update helpers for slider component
6
6
  *
7
7
  * @param config Slider configuration
8
8
  * @param state Slider state object
@@ -12,19 +12,29 @@ export const createUiHelpers = (config: SliderConfig, state) => {
12
12
  // Return empty implementations if component structure is missing
13
13
  if (!state.component?.structure) {
14
14
  console.error('Cannot create UI helpers: component structure is missing');
15
- return Object.fromEntries(['getPercentage', 'getValueFromPosition', 'roundToStep',
16
- 'clamp', 'setThumbPosition', 'updateActiveTrack', 'updateStartTrack',
17
- 'updateRemainingTrack', 'updateThumbPositions', 'updateValueBubbles',
18
- 'showValueBubble', 'generateTicks', 'updateTicks', 'updateUi']
19
- .map(method => [method, method === 'clamp' ? (v, min, max) => Math.min(Math.max(v, min), max) :
20
- method === 'roundToStep' ? v => v :
21
- method === 'getPercentage' || method === 'getValueFromPosition' ? () => 0 :
22
- () => {}]));
15
+ return {
16
+ getPercentage: () => 0,
17
+ getValueFromPosition: () => 0,
18
+ roundToStep: v => v,
19
+ clamp: (v, min, max) => Math.min(Math.max(v, min), max),
20
+ updateUi: () => {},
21
+ showValueBubble: () => {},
22
+ generateTicks: () => {},
23
+ updateTicks: () => {}
24
+ };
23
25
  }
24
26
 
25
27
  const {
26
- track, activeTrack, startTrack, remainingTrack, thumb,
27
- valueBubble, secondThumb, secondValueBubble, ticksContainer
28
+ container = null,
29
+ track = null,
30
+ activeTrack = null,
31
+ startTrack = null,
32
+ remainingTrack = null,
33
+ handle = null,
34
+ valueBubble = null,
35
+ secondHandle = null,
36
+ secondValueBubble = null,
37
+ ticksContainer = null
28
38
  } = state.component.structure;
29
39
 
30
40
  /**
@@ -39,21 +49,22 @@ export const createUiHelpers = (config: SliderConfig, state) => {
39
49
  * Gets track dimensions and constraints for positioning calculations
40
50
  */
41
51
  const getTrackDimensions = () => {
42
- if (!track || !thumb) return null;
52
+ if (!track || !handle || !container) return null;
43
53
 
44
- const thumbRect = thumb.getBoundingClientRect();
45
- const trackRect = track.getBoundingClientRect();
46
- const thumbSize = thumbRect.width || 20;
54
+ const handleRect = handle.getBoundingClientRect();
55
+ const trackRect = container.getBoundingClientRect();
56
+ const handleSize = handleRect.width || 20;
47
57
  const trackSize = trackRect.width;
48
58
 
49
- const edgeConstraint = (thumbSize / 2) / trackSize * 100;
50
- const paddingPercent = (8 / trackSize) * 100; // 8px padding
59
+ const edgeConstraint = (handleSize / 2) / trackSize * 100;
60
+ const paddingPixels = state.activeHandle ? 6 : 8;
61
+ const paddingPercent = (paddingPixels / trackSize) * 100;
51
62
 
52
- return { thumbSize, trackSize, edgeConstraint, paddingPercent };
63
+ return { handleSize, trackSize, edgeConstraint, paddingPercent };
53
64
  };
54
65
 
55
66
  /**
56
- * Map value percentage to visual position with edge constraints
67
+ * Maps value percentage to visual position with edge constraints
57
68
  */
58
69
  const mapValueToVisualPercent = (valuePercent, edgeConstraint) => {
59
70
  const minEdge = edgeConstraint;
@@ -69,17 +80,15 @@ export const createUiHelpers = (config: SliderConfig, state) => {
69
80
  * Gets slider value from a position on the track
70
81
  */
71
82
  const getValueFromPosition = (position) => {
72
- if (!track) return state.min;
83
+ if (!track || !container) return state.min;
73
84
 
74
85
  try {
75
- const trackRect = track.getBoundingClientRect();
86
+ const containerRect = container.getBoundingClientRect();
76
87
  const range = state.max - state.min;
88
+ const handleWidth = handle.getBoundingClientRect().width || 20;
77
89
 
78
- const thumbRect = thumb.getBoundingClientRect();
79
- const thumbWidth = thumbRect.width || 20;
80
-
81
- const leftEdge = trackRect.left + (thumbWidth / 2);
82
- const rightEdge = trackRect.right - (thumbWidth / 2);
90
+ const leftEdge = containerRect.left + (handleWidth / 2);
91
+ const rightEdge = containerRect.right - (handleWidth / 2);
83
92
  const effectiveWidth = rightEdge - leftEdge;
84
93
 
85
94
  const adjustedPosition = Math.max(leftEdge, Math.min(rightEdge, position));
@@ -109,157 +118,116 @@ export const createUiHelpers = (config: SliderConfig, state) => {
109
118
  const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
110
119
 
111
120
  /**
112
- * Sets thumb position based on a value percentage with proper edge mapping
121
+ * Updates handle and bubble positions and transforms
113
122
  */
114
- const setThumbPosition = (thumbElement, bubbleElement, valuePercent) => {
115
- if (!thumbElement || !track) return;
123
+ const updateHandlePositions = () => {
124
+ if (!handle || !container) return;
116
125
 
117
126
  const dims = getTrackDimensions();
118
127
  if (!dims) return;
119
128
 
120
- const { thumbSize, trackSize } = dims;
121
- const edgeConstraint = (thumbSize / 2) / trackSize * 100;
122
- const adjustedPercent = mapValueToVisualPercent(valuePercent, edgeConstraint);
129
+ const { edgeConstraint } = dims;
123
130
 
124
- thumbElement.style.left = `${adjustedPercent}%`;
125
- thumbElement.style.transform = 'translate(-50%, -50%)';
131
+ // Update main handle position
132
+ const percent = getPercentage(state.value);
133
+ const adjustedPercent = mapValueToVisualPercent(percent, edgeConstraint);
126
134
 
127
- if (bubbleElement) {
128
- bubbleElement.style.left = `${adjustedPercent}%`;
129
- bubbleElement.style.transform = 'translateX(-50%)';
135
+ handle.style.left = `${adjustedPercent}%`;
136
+ handle.style.transform = 'translate(-50%, -50%)';
137
+ if (valueBubble) {
138
+ valueBubble.style.left = `${adjustedPercent}%`;
139
+ valueBubble.style.transform = 'translateX(-50%)';
130
140
  }
131
- };
132
-
133
- /**
134
- * Updates start track styles
135
- */
136
- const updateStartTrack = () => {
137
- if (!startTrack || !track || !thumb) return;
138
-
139
- const dims = getTrackDimensions();
140
- if (!dims) return;
141
-
142
- const { thumbSize, trackSize, paddingPercent } = dims;
143
141
 
144
- if (config.range && state.secondValue !== null) {
145
- const lowerValue = Math.min(state.value, state.secondValue);
146
- const lowerPercent = getPercentage(lowerValue);
147
-
148
- const edgeConstraint = (thumbSize / 2) / trackSize * 100;
149
- const adjustedPercent = mapValueToVisualPercent(lowerPercent, edgeConstraint);
150
- const finalPercent = Math.max(0, adjustedPercent - paddingPercent);
142
+ // Update second handle if range slider
143
+ if (config.range && secondHandle && state.secondValue !== null) {
144
+ const secondPercent = getPercentage(state.secondValue);
145
+ const adjustedSecondPercent = mapValueToVisualPercent(secondPercent, edgeConstraint);
151
146
 
152
- startTrack.style.display = 'block';
153
- startTrack.style.width = `${finalPercent}%`;
154
- startTrack.style.left = '0';
155
- startTrack.style.height = '100%';
156
- } else {
157
- startTrack.style.display = 'none';
147
+ secondHandle.style.left = `${adjustedSecondPercent}%`;
148
+ secondHandle.style.transform = 'translate(-50%, -50%)';
149
+ if (secondValueBubble) {
150
+ secondValueBubble.style.left = `${adjustedSecondPercent}%`;
151
+ secondValueBubble.style.transform = 'translateX(-50%)';
152
+ }
153
+ }
154
+
155
+ // Update ARIA attributes
156
+ handle.setAttribute('aria-valuenow', String(state.value));
157
+ if (config.range && secondHandle && state.secondValue !== null) {
158
+ secondHandle.setAttribute('aria-valuenow', String(state.secondValue));
158
159
  }
159
160
  };
160
-
161
+
161
162
  /**
162
- * Updates active track styles
163
+ * Updates all track segments at once with optimized positioning
163
164
  */
164
- const updateActiveTrack = () => {
165
- if (!activeTrack || !track || !thumb) return;
165
+ const updateTrackSegments = () => {
166
+ if (!track || !container || !handle) return;
166
167
 
167
168
  const dims = getTrackDimensions();
168
169
  if (!dims) return;
169
170
 
170
- const { thumbSize, trackSize, paddingPercent } = dims;
171
- const edgeConstraint = (thumbSize / 2) / trackSize * 100;
171
+ const { handleSize, trackSize, paddingPercent } = dims;
172
+ const edgeConstraint = (handleSize / 2) / trackSize * 100;
172
173
 
173
174
  if (config.range && state.secondValue !== null) {
174
- // Range slider (two thumbs)
175
+ // Range slider setup
175
176
  const lowerValue = Math.min(state.value, state.secondValue);
176
177
  const higherValue = Math.max(state.value, state.secondValue);
177
178
  const lowerPercent = getPercentage(lowerValue);
178
179
  const higherPercent = getPercentage(higherValue);
179
180
 
180
- const adjustedLower = mapValueToVisualPercent(lowerPercent, edgeConstraint) + paddingPercent;
181
- const adjustedHigher = mapValueToVisualPercent(higherPercent, edgeConstraint) - paddingPercent;
181
+ const adjustedLower = mapValueToVisualPercent(lowerPercent, edgeConstraint);
182
+ const adjustedHigher = mapValueToVisualPercent(higherPercent, edgeConstraint);
182
183
 
183
- // Calculate the actual percentage difference between thumbs
184
- const valueDiffPercent = Math.abs(higherPercent - lowerPercent);
184
+ // Start track (before first handle)
185
+ if (lowerPercent > 1) {
186
+ startTrack.style.display = 'block';
187
+ startTrack.style.left = '0';
188
+ startTrack.style.right = `${100 - (adjustedLower - paddingPercent)}%`;
189
+ startTrack.style.width = 'auto';
190
+ } else {
191
+ startTrack.style.display = 'none';
192
+ }
185
193
 
186
- // Define a threshold below which we'll hide the active track
187
- // This threshold is based on the thumb width plus some margin
188
- const hideThreshold = (thumbSize / trackSize) * 100;
194
+ // Active track (between handles)
195
+ const valueDiffPercent = Math.abs(higherPercent - lowerPercent);
196
+ const hideThreshold = (handleSize / trackSize) * 100;
189
197
 
190
198
  if (valueDiffPercent <= hideThreshold) {
191
- // Thumbs are too close together, hide the active track
192
199
  activeTrack.style.display = 'none';
193
200
  } else {
194
- // Normal display of active track
195
- let trackLength = Math.max(0, adjustedHigher - adjustedLower);
196
-
197
201
  activeTrack.style.display = 'block';
198
- activeTrack.style.width = `${trackLength}%`;
199
- activeTrack.style.left = `${adjustedLower}%`;
200
- activeTrack.style.height = '100%';
202
+ activeTrack.style.left = `${adjustedLower + paddingPercent}%`;
203
+ activeTrack.style.right = `${100 - (adjustedHigher - paddingPercent)}%`;
204
+ activeTrack.style.width = 'auto';
201
205
  }
206
+
207
+ // Remaining track (after second handle)
208
+ remainingTrack.style.display = 'block';
209
+ remainingTrack.style.left = `${adjustedHigher + paddingPercent}%`;
210
+ remainingTrack.style.right = '0';
211
+ remainingTrack.style.width = 'auto';
202
212
  } else {
203
- // Single thumb slider
213
+ // Single handle slider
204
214
  const valuePercent = getPercentage(state.value);
205
215
  const adjustedPercent = mapValueToVisualPercent(valuePercent, edgeConstraint);
206
- const adjustedWidth = Math.max(0, adjustedPercent - paddingPercent);
207
216
 
217
+ // Hide start track for single slider
218
+ startTrack.style.display = 'none';
219
+
220
+ // Active track (filled part)
208
221
  activeTrack.style.display = 'block';
209
- activeTrack.style.width = `${adjustedWidth}%`;
210
222
  activeTrack.style.left = '0';
211
- activeTrack.style.height = '100%';
212
- }
213
- };
214
-
215
- /**
216
- * Updates remaining track styles
217
- */
218
- const updateRemainingTrack = () => {
219
- if (!remainingTrack || !track || !thumb) return;
220
-
221
- const dims = getTrackDimensions();
222
- if (!dims) return;
223
-
224
- const { thumbSize, trackSize, paddingPercent } = dims;
225
- const edgeConstraint = (thumbSize / 2) / trackSize * 100;
226
-
227
- // Find the highest thumb value
228
- const highValue = config.range && state.secondValue !== null ?
229
- Math.max(state.value, state.secondValue) : state.value;
230
-
231
- const valuePercent = getPercentage(highValue);
232
-
233
- // Map percentage to visual range
234
- const adjustedPercent = mapValueToVisualPercent(valuePercent, edgeConstraint) + paddingPercent;
235
- const remainingSize = Math.max(0, 100 - adjustedPercent);
236
-
237
- remainingTrack.style.display = 'block';
238
- remainingTrack.style.width = `${remainingSize}%`;
239
- remainingTrack.style.left = `${adjustedPercent}%`;
240
- remainingTrack.style.height = '100%';
241
- };
242
-
243
- /**
244
- * Updates thumb positions
245
- */
246
- const updateThumbPositions = () => {
247
- if (!thumb) return;
248
-
249
- // Update main thumb
250
- const percent = getPercentage(state.value);
251
- setThumbPosition(thumb, valueBubble, percent);
252
-
253
- // Update second thumb if range slider
254
- if (config.range && secondThumb && secondValueBubble && state.secondValue !== null) {
255
- const secondPercent = getPercentage(state.secondValue);
256
- setThumbPosition(secondThumb, secondValueBubble, secondPercent);
257
- }
258
-
259
- // Update ARIA attributes
260
- thumb.setAttribute('aria-valuenow', String(state.value));
261
- if (config.range && secondThumb && state.secondValue !== null) {
262
- secondThumb.setAttribute('aria-valuenow', String(state.secondValue));
223
+ activeTrack.style.right = `${100 - (adjustedPercent - paddingPercent)}%`;
224
+ activeTrack.style.width = 'auto';
225
+
226
+ // Remaining track (unfilled part)
227
+ remainingTrack.style.display = 'block';
228
+ remainingTrack.style.left = `${adjustedPercent + paddingPercent}%`;
229
+ remainingTrack.style.right = '0';
230
+ remainingTrack.style.width = 'auto';
263
231
  }
264
232
  };
265
233
 
@@ -269,11 +237,9 @@ export const createUiHelpers = (config: SliderConfig, state) => {
269
237
  const updateValueBubbles = () => {
270
238
  if (!valueBubble) return;
271
239
 
272
- // Format the values
273
240
  const formatter = config.valueFormatter || (value => value.toString());
274
-
275
- // Update main and second value bubble if needed
276
241
  valueBubble.textContent = formatter(state.value);
242
+
277
243
  if (config.range && secondValueBubble && state.secondValue !== null) {
278
244
  secondValueBubble.textContent = formatter(state.secondValue);
279
245
  }
@@ -285,42 +251,30 @@ export const createUiHelpers = (config: SliderConfig, state) => {
285
251
  const showValueBubble = (bubbleElement, show) => {
286
252
  if (!bubbleElement || !config.showValue) return;
287
253
 
288
- const visibleClass = `${state.component.getClass('slider-value')}--visible`;
289
- bubbleElement.classList[show ? 'add' : 'remove'](visibleClass);
254
+ bubbleElement.classList[show ? 'add' : 'remove'](`${state.component.getClass('slider-value')}--visible`);
290
255
  };
291
256
 
292
257
  /**
293
258
  * Generates tick marks
294
259
  */
295
260
  const generateTicks = () => {
296
- if (!ticksContainer) {
297
- console.warn('Ticks container not found in component structure');
298
- return;
299
- }
261
+ if (!ticksContainer || !container) return;
300
262
 
301
263
  // Clear existing ticks
302
- const sliderElement = state.component.element;
303
- sliderElement.querySelectorAll(`.${state.component.getClass('slider-tick')}`)
304
- .forEach(tick => tick.parentNode.removeChild(tick));
305
-
306
264
  while (ticksContainer.firstChild) {
307
265
  ticksContainer.removeChild(ticksContainer.firstChild);
308
266
  }
309
267
 
310
- // Reset ticks array
311
268
  state.ticks = [];
312
-
313
269
  if (!config.ticks) return;
314
270
 
315
- // Generate tick values
316
271
  const numSteps = Math.floor((state.max - state.min) / state.step);
317
272
  const tickValues = [];
318
273
 
274
+ // Generate tick values
319
275
  for (let i = 0; i <= numSteps; i++) {
320
276
  const value = state.min + (i * state.step);
321
- if (value <= state.max) {
322
- tickValues.push(value);
323
- }
277
+ if (value <= state.max) tickValues.push(value);
324
278
  }
325
279
 
326
280
  // Ensure max value is included
@@ -329,41 +283,35 @@ export const createUiHelpers = (config: SliderConfig, state) => {
329
283
  }
330
284
 
331
285
  // CSS classes
332
- const activeClass = `${state.component.getClass('slider-tick')}--active`;
333
- const inactiveClass = `${state.component.getClass('slider-tick')}--inactive`;
334
- const hiddenClass = `${state.component.getClass('slider-tick')}--hidden`;
335
286
  const tickClass = state.component.getClass('slider-tick');
287
+ const activeClass = `${tickClass}--active`;
288
+ const inactiveClass = `${tickClass}--inactive`;
289
+ const hiddenClass = `${tickClass}--hidden`;
336
290
 
337
- // Create ticks
291
+ // Create tick elements
338
292
  tickValues.forEach(value => {
339
293
  const percent = getPercentage(value);
294
+ const tick = document.createElement('div');
295
+ tick.classList.add(tickClass);
296
+ tick.style.left = `${percent}%`;
340
297
 
341
- // Create tick mark if enabled
342
- if (config.ticks) {
343
- const tick = document.createElement('div');
344
- tick.classList.add(tickClass);
345
-
346
- // Position tick
347
- tick.style.left = `${percent}%`;
348
-
349
- // Check if this tick should be hidden (matches exactly a selected value)
350
- const isExactlySelected = value === state.value ||
351
- (config.range && state.secondValue !== null && value === state.secondValue);
352
-
353
- if (isExactlySelected) {
354
- tick.classList.add(hiddenClass);
355
- } else if (config.range && state.secondValue !== null) {
356
- const lowerValue = Math.min(state.value, state.secondValue);
357
- const higherValue = Math.max(state.value, state.secondValue);
358
-
359
- tick.classList.add(value >= lowerValue && value <= higherValue ? activeClass : inactiveClass);
360
- } else {
361
- tick.classList.add(value <= state.value ? activeClass : inactiveClass);
362
- }
298
+ // Determine tick active state
299
+ const isExactlySelected = (value === state.value ||
300
+ (config.range && state.secondValue !== null && value === state.secondValue));
301
+
302
+ if (isExactlySelected) {
303
+ tick.classList.add(hiddenClass);
304
+ } else if (config.range && state.secondValue !== null) {
305
+ const lowerValue = Math.min(state.value, state.secondValue);
306
+ const higherValue = Math.max(state.value, state.secondValue);
363
307
 
364
- ticksContainer.appendChild(tick);
365
- state.ticks.push(tick);
308
+ tick.classList.add(value >= lowerValue && value <= higherValue ? activeClass : inactiveClass);
309
+ } else {
310
+ tick.classList.add(value <= state.value ? activeClass : inactiveClass);
366
311
  }
312
+
313
+ ticksContainer.appendChild(tick);
314
+ state.ticks.push(tick);
367
315
  });
368
316
  };
369
317
 
@@ -373,24 +321,23 @@ export const createUiHelpers = (config: SliderConfig, state) => {
373
321
  const updateTicks = () => {
374
322
  if (!state.ticks || state.ticks.length === 0) return;
375
323
 
376
- const activeClass = `${state.component.getClass('slider-tick')}--active`;
377
- const inactiveClass = `${state.component.getClass('slider-tick')}--inactive`;
378
- const hiddenClass = `${state.component.getClass('slider-tick')}--hidden`;
324
+ const tickClass = state.component.getClass('slider-tick');
325
+ const activeClass = `${tickClass}--active`;
326
+ const inactiveClass = `${tickClass}--inactive`;
327
+ const hiddenClass = `${tickClass}--hidden`;
379
328
 
380
- // Update active ticks based on current value
381
329
  state.ticks.forEach((tick, index) => {
382
- // Calculate the value for this tick based on its index
330
+ // Calculate the value for this tick
383
331
  const tickValue = state.min + (index * state.step);
384
332
 
385
- // First, reset visibility
333
+ // Reset visibility first
386
334
  tick.classList.remove(hiddenClass);
387
335
 
388
336
  // Check if this tick should be hidden (matches exactly a selected value)
389
- const isExactlySelected = tickValue === state.value ||
390
- (config.range && state.secondValue !== null && tickValue === state.secondValue);
337
+ const isExactlySelected = (tickValue === state.value ||
338
+ (config.range && state.secondValue !== null && tickValue === state.secondValue));
391
339
 
392
340
  if (isExactlySelected) {
393
- // Hide this tick as it exactly matches a selected value
394
341
  tick.classList.add(hiddenClass);
395
342
  } else if (config.range && state.secondValue !== null) {
396
343
  // Range slider - ticks between values should be active
@@ -421,29 +368,21 @@ export const createUiHelpers = (config: SliderConfig, state) => {
421
368
  * Updates all UI elements
422
369
  */
423
370
  const updateUi = () => {
424
- updateThumbPositions();
425
- updateStartTrack();
426
- updateActiveTrack();
427
- updateRemainingTrack();
371
+ updateHandlePositions();
372
+ updateTrackSegments();
428
373
  updateValueBubbles();
429
374
  updateTicks();
430
375
  };
431
376
 
432
- // Return helper methods
377
+ // Return consolidated helper methods
433
378
  return {
434
379
  getPercentage,
435
380
  getValueFromPosition,
436
381
  roundToStep,
437
382
  clamp,
438
- setThumbPosition,
439
- updateActiveTrack,
440
- updateStartTrack,
441
- updateRemainingTrack,
442
- updateThumbPositions,
443
- updateValueBubbles,
444
383
  showValueBubble,
445
384
  generateTicks,
446
385
  updateTicks,
447
386
  updateUi
448
387
  };
449
- };
388
+ }
@@ -4,16 +4,7 @@
4
4
  export { default } from './slider';
5
5
 
6
6
  // Export constants
7
- export {
8
- SLIDER_COLORS,
9
- SLIDER_SIZES,
10
- SLIDER_ORIENTATIONS,
11
- SLIDER_EVENTS
12
- } from './constants';
7
+ export { SLIDER_COLORS, SLIDER_SIZES, SLIDER_EVENTS } from './constants';
13
8
 
14
9
  // Export types for TypeScript users
15
- export type {
16
- SliderConfig,
17
- SliderComponent,
18
- SliderEvent
19
- } from './types';
10
+ export type { SliderConfig, SliderComponent, SliderEvent } from './types';
@@ -1,13 +1,9 @@
1
1
  // src/components/slider/slider.ts
2
2
  import { pipe } from '../../core/compose/pipe';
3
3
  import { createBase, withElement } from '../../core/compose/component';
4
- import { withEvents, withLifecycle } from '../../core/compose/features';
5
- import {
6
- withStructure,
7
- withDisabled,
8
- withAppearance,
9
- withSlider
10
- } from './features';
4
+ import { withEvents, withLifecycle, withTextLabel, withIcon } from '../../core/compose/features';
5
+ import { withStructure, withSlider } from './features';
6
+ import { withStates } from './features/states';
11
7
  import { withAPI } from './api';
12
8
  import { SliderConfig, SliderComponent } from './types';
13
9
  import { createBaseConfig, getElementConfig, getApiConfig } from './config';
@@ -21,22 +17,23 @@ const createSlider = (config: SliderConfig = {}): SliderComponent => {
21
17
  const baseConfig = createBaseConfig(config);
22
18
 
23
19
  try {
24
- // First create the component with all required features
20
+ // Create the component with all required features
25
21
  const component = pipe(
26
22
  createBase,
27
23
  withEvents(),
28
24
  withElement(getElementConfig(baseConfig)),
25
+ withIcon(baseConfig),
26
+ withTextLabel(baseConfig),
29
27
  withStructure(baseConfig),
30
- withDisabled(baseConfig),
31
- withAppearance(baseConfig),
28
+ withStates(baseConfig),
32
29
  withSlider(baseConfig),
33
30
  withLifecycle()
34
31
  )(baseConfig);
35
32
 
36
- // Then generate the API configuration using the component
33
+ // Generate the API configuration
37
34
  const apiOptions = getApiConfig(component);
38
35
 
39
- // Finally, apply the API layer with the generated options
36
+ // Apply the API layer
40
37
  const slider = withAPI(apiOptions)(component);
41
38
 
42
39
  // Register event handlers from config