mtrl 0.2.4 → 0.2.5

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.
@@ -1,5 +1,5 @@
1
- // src/components/slider/features/structure.ts - Fixed track initialization
2
- import { SLIDER_COLORS, SLIDER_SIZES, SLIDER_ORIENTATIONS } from '../constants';
1
+ // src/components/slider/features/structure.ts
2
+ import { SLIDER_COLORS, SLIDER_SIZES } from '../constants';
3
3
  import { SliderConfig } from '../types';
4
4
 
5
5
  /**
@@ -8,244 +8,100 @@ import { SliderConfig } from '../types';
8
8
  * @returns Component enhancer with DOM structure
9
9
  */
10
10
  export const withStructure = (config: SliderConfig) => component => {
11
- // Create track element
12
- const track = document.createElement('div');
13
- track.classList.add(component.getClass('slider-track'));
14
-
15
- // Calculate initial percentages based on values
11
+ // Set default values
16
12
  const min = config.min || 0;
17
13
  const max = config.max || 100;
18
14
  const range = max - min;
19
-
20
- // Set default values
21
15
  const value = config.value !== undefined ? config.value : min;
22
16
  const secondValue = config.secondValue !== undefined ? config.secondValue : null;
17
+ const isRangeSlider = config.range && secondValue !== null;
23
18
 
24
- // Calculate percentages
19
+ // Helper function to calculate percentage
25
20
  const getPercentage = (val) => ((val - min) / range) * 100;
26
21
  const valuePercent = getPercentage(value);
27
22
 
28
- // Create remaining track element (fills entire width initially)
29
- const remainingTrack = document.createElement('div');
30
- remainingTrack.classList.add(component.getClass('slider-remaining-track'));
23
+ // Create track element and segments
24
+ const track = createElement('slider-track');
25
+ const remainingTrack = createElement('slider-remaining-track');
26
+ const startTrack = createElement('slider-start-track');
27
+ const activeTrack = createElement('slider-active-track');
31
28
 
32
- // Create start track element (for range slider)
33
- const startTrack = document.createElement('div');
34
- startTrack.classList.add(component.getClass('slider-start-track'));
29
+ // Create ticks container
30
+ const ticksContainer = createElement('slider-ticks-container');
35
31
 
36
- // Create active track element (filled part)
37
- const activeTrack = document.createElement('div');
38
- activeTrack.classList.add(component.getClass('slider-active-track'));
39
-
40
- // Calculate padding adjustment (8px equivalent as percentage)
41
- // We'll do a rough estimate initially, then recalculate once rendered
42
- const paddingAdjustment = 8; // 8px padding
43
- const estimatedTrackSize = 300; // A reasonable guess at track width
44
- const paddingPercent = (paddingAdjustment / estimatedTrackSize) * 100;
32
+ // Create dots for track ends
33
+ const startDot = createElement('slider-dot');
34
+ startDot.classList.add(component.getClass('slider-dot--start'));
45
35
 
46
- // Set initial dimensions for all track segments
47
- if (config.range && secondValue !== null) {
48
- // Range slider
49
- const lowerValue = Math.min(value, secondValue);
50
- const higherValue = Math.max(value, secondValue);
51
- const lowerPercent = getPercentage(lowerValue);
52
- const higherPercent = getPercentage(higherValue);
53
-
54
- // Adjust positions and width to account for spacing
55
- let adjustedLowerPercent = lowerPercent + paddingPercent;
56
- let adjustedHigherPercent = higherPercent - paddingPercent;
57
-
58
- if (adjustedHigherPercent <= adjustedLowerPercent) {
59
- adjustedLowerPercent = (lowerPercent + higherPercent) / 2 - 1;
60
- adjustedHigherPercent = (lowerPercent + higherPercent) / 2 + 1;
61
- }
62
-
63
- // Calculate track segment sizes
64
- const startWidth = Math.max(0, lowerPercent - paddingPercent);
65
- const activeWidth = Math.max(0, adjustedHigherPercent - adjustedLowerPercent);
66
- const remainingWidth = Math.max(0, 100 - higherPercent - paddingPercent);
67
-
68
- if (config.orientation === SLIDER_ORIENTATIONS.VERTICAL) {
69
- // Vertical orientation
70
- startTrack.style.display = 'block';
71
- startTrack.style.height = `${startWidth}%`;
72
- startTrack.style.bottom = '0';
73
- startTrack.style.width = '100%';
74
-
75
- activeTrack.style.display = 'block';
76
- activeTrack.style.height = `${activeWidth}%`;
77
- activeTrack.style.bottom = `${adjustedLowerPercent}%`;
78
- activeTrack.style.width = '100%';
79
-
80
- remainingTrack.style.display = 'block';
81
- remainingTrack.style.height = `${remainingWidth}%`;
82
- remainingTrack.style.bottom = `${higherPercent + paddingPercent}%`;
83
- remainingTrack.style.width = '100%';
84
- } else {
85
- // Horizontal orientation
86
- startTrack.style.display = 'block';
87
- startTrack.style.width = `${startWidth}%`;
88
- startTrack.style.left = '0';
89
- startTrack.style.height = '100%';
90
-
91
- activeTrack.style.display = 'block';
92
- activeTrack.style.width = `${activeWidth}%`;
93
- activeTrack.style.left = `${adjustedLowerPercent}%`;
94
- activeTrack.style.height = '100%';
95
-
96
- remainingTrack.style.display = 'block';
97
- remainingTrack.style.width = `${remainingWidth}%`;
98
- remainingTrack.style.left = `${higherPercent + paddingPercent}%`;
99
- remainingTrack.style.height = '100%';
100
- }
101
- } else {
102
- // Single thumb slider
103
- const adjustedWidth = Math.max(0, valuePercent - paddingPercent);
104
- const remainingWidth = Math.max(0, 100 - valuePercent - paddingPercent);
105
-
106
- if (config.orientation === SLIDER_ORIENTATIONS.VERTICAL) {
107
- // Vertical orientation
108
- startTrack.style.display = 'none';
109
-
110
- activeTrack.style.display = 'block';
111
- activeTrack.style.height = `${adjustedWidth}%`;
112
- activeTrack.style.bottom = '0';
113
- activeTrack.style.width = '100%';
114
-
115
- remainingTrack.style.display = 'block';
116
- remainingTrack.style.height = `${remainingWidth}%`;
117
- remainingTrack.style.bottom = `${valuePercent + paddingPercent}%`;
118
- remainingTrack.style.width = '100%';
119
- } else {
120
- // Horizontal orientation
121
- startTrack.style.display = 'none';
122
-
123
- activeTrack.style.display = 'block';
124
- activeTrack.style.width = `${adjustedWidth}%`;
125
- activeTrack.style.left = '0';
126
- activeTrack.style.height = '100%';
127
-
128
- remainingTrack.style.display = 'block';
129
- remainingTrack.style.width = `${remainingWidth}%`;
130
- remainingTrack.style.left = `${valuePercent + paddingPercent}%`;
131
- remainingTrack.style.height = '100%';
132
- }
133
- }
36
+ const endDot = createElement('slider-dot');
37
+ endDot.classList.add(component.getClass('slider-dot--end'));
134
38
 
135
- // Add tracks to container
136
- track.appendChild(remainingTrack);
137
- track.appendChild(startTrack);
138
- track.appendChild(activeTrack);
39
+ // Create value bubble and format the value
40
+ const formatter = config.valueFormatter || (val => val.toString());
41
+ const valueBubble = createElement('slider-value');
42
+ valueBubble.textContent = formatter(value);
139
43
 
140
44
  // Create thumb element
141
- const thumb = document.createElement('div');
142
- thumb.classList.add(component.getClass('slider-thumb'));
45
+ const thumb = createElement('slider-thumb');
143
46
  thumb.setAttribute('tabindex', '0');
144
47
  thumb.setAttribute('role', 'slider');
145
48
 
146
49
  // Set initial thumb position
147
- if (config.orientation === SLIDER_ORIENTATIONS.VERTICAL) {
148
- thumb.style.bottom = `${valuePercent}%`;
149
- thumb.style.left = '50%';
150
- thumb.style.top = 'auto';
151
- } else {
152
- thumb.style.left = `${valuePercent}%`;
153
- }
154
-
155
- // Create dots for the track ends
156
- const startDot = document.createElement('div');
157
- startDot.classList.add(component.getClass('slider-dot'));
158
- startDot.classList.add(component.getClass('slider-dot--start'));
50
+ thumb.style.left = `${valuePercent}%`;
159
51
 
160
- const endDot = document.createElement('div');
161
- endDot.classList.add(component.getClass('slider-dot'));
162
- endDot.classList.add(component.getClass('slider-dot--end'));
163
-
164
- // Create value bubble element
165
- const valueBubble = document.createElement('div');
166
- valueBubble.classList.add(component.getClass('slider-value'));
167
-
168
- // Format value and set initial bubble text
169
- const formatter = config.valueFormatter || (val => val.toString());
170
- valueBubble.textContent = formatter(value);
52
+ // Calculate padding adjustment (8px equivalent as percentage)
53
+ const paddingAdjustment = 8; // 8px padding
54
+ const estimatedTrackSize = 300; // A reasonable guess at track width
55
+ const paddingPercent = (paddingAdjustment / estimatedTrackSize) * 100;
171
56
 
172
- // For range slider: Create second thumb and value bubble
57
+ // Create second thumb and value bubble for range slider
173
58
  let secondThumb = null;
174
59
  let secondValueBubble = null;
175
60
 
176
- if (config.range && secondValue !== null) {
177
- // Create second thumb
178
- secondThumb = document.createElement('div');
179
- secondThumb.classList.add(component.getClass('slider-thumb'));
61
+ if (isRangeSlider) {
62
+ secondThumb = createElement('slider-thumb');
180
63
  secondThumb.setAttribute('tabindex', '0');
181
64
  secondThumb.setAttribute('role', 'slider');
182
65
 
183
- // Set initial second thumb position
184
66
  const secondPercent = getPercentage(secondValue);
185
- if (config.orientation === SLIDER_ORIENTATIONS.VERTICAL) {
186
- secondThumb.style.bottom = `${secondPercent}%`;
187
- secondThumb.style.left = '50%';
188
- secondThumb.style.top = 'auto';
189
- } else {
190
- secondThumb.style.left = `${secondPercent}%`;
191
- }
67
+ secondThumb.style.left = `${secondPercent}%`;
192
68
 
193
- // Create second value bubble
194
- secondValueBubble = document.createElement('div');
195
- secondValueBubble.classList.add(component.getClass('slider-value'));
69
+ secondValueBubble = createElement('slider-value');
196
70
  secondValueBubble.textContent = formatter(secondValue);
197
71
  }
198
72
 
73
+ // Set initial track segment dimensions
74
+ setupInitialTrackSegments();
75
+
76
+ // Add tracks to container
77
+ track.appendChild(remainingTrack);
78
+ track.appendChild(startTrack);
79
+ track.appendChild(activeTrack);
80
+
199
81
  // Add elements to the slider
200
82
  component.element.classList.add(component.getClass('slider'));
201
83
  component.element.appendChild(track);
84
+ component.element.appendChild(ticksContainer); // Add ticks container
202
85
  component.element.appendChild(startDot);
203
86
  component.element.appendChild(endDot);
204
87
  component.element.appendChild(thumb);
205
88
  component.element.appendChild(valueBubble);
206
89
 
207
- if (config.range && secondThumb && secondValueBubble) {
90
+ if (isRangeSlider && secondThumb && secondValueBubble) {
208
91
  component.element.classList.add(`${component.getClass('slider')}--range`);
209
92
  component.element.appendChild(secondThumb);
210
93
  component.element.appendChild(secondValueBubble);
211
94
  }
212
95
 
213
- // Apply size class
214
- const size = config.size || SLIDER_SIZES.MEDIUM;
215
- if (size !== SLIDER_SIZES.MEDIUM) {
216
- component.element.classList.add(`${component.getClass('slider')}--${size}`);
217
- }
218
-
219
- // Apply color class
220
- const color = config.color || SLIDER_COLORS.PRIMARY;
221
- if (color !== SLIDER_COLORS.PRIMARY) {
222
- component.element.classList.add(`${component.getClass('slider')}--${color}`);
223
- }
224
-
225
- // Apply orientation class
226
- const orientation = config.orientation || SLIDER_ORIENTATIONS.HORIZONTAL;
227
- if (orientation === SLIDER_ORIENTATIONS.VERTICAL) {
228
- component.element.classList.add(`${component.getClass('slider')}--vertical`);
229
- }
230
-
231
- // Apply discrete class if step is specified
232
- if (config.step !== undefined && config.step > 0) {
233
- component.element.classList.add(`${component.getClass('slider')}--discrete`);
234
- }
235
-
236
- // Apply disabled class if needed
237
- if (config.disabled) {
238
- component.element.classList.add(`${component.getClass('slider')}--disabled`);
239
- }
96
+ // Apply styling classes
97
+ applyStyleClasses();
240
98
 
241
- // Ensure proper initialization after DOM is attached by scheduling a UI update
99
+ // Schedule UI update after DOM is attached
242
100
  setTimeout(() => {
243
- if (component.slider && typeof component.slider.updateUi === 'function') {
244
- component.slider.updateUi();
245
- }
101
+ component.slider?.updateUi?.();
246
102
  }, 0);
247
103
 
248
- // Store elements in component
104
+ // Return enhanced component with structure
249
105
  return {
250
106
  ...component,
251
107
  structure: {
@@ -253,6 +109,7 @@ export const withStructure = (config: SliderConfig) => component => {
253
109
  activeTrack,
254
110
  startTrack,
255
111
  remainingTrack,
112
+ ticksContainer,
256
113
  thumb,
257
114
  valueBubble,
258
115
  secondThumb,
@@ -261,4 +118,107 @@ export const withStructure = (config: SliderConfig) => component => {
261
118
  endDot
262
119
  }
263
120
  };
121
+
122
+ /**
123
+ * Creates DOM element with slider class
124
+ * @param className Base class name
125
+ * @returns DOM element
126
+ */
127
+ function createElement(className) {
128
+ const element = document.createElement('div');
129
+ element.classList.add(component.getClass(className));
130
+ return element;
131
+ }
132
+
133
+ /**
134
+ * Sets up initial track segment positions and dimensions
135
+ */
136
+ function setupInitialTrackSegments() {
137
+ if (isRangeSlider) {
138
+ // Range slider with two thumbs
139
+ const lowerValue = Math.min(value, secondValue);
140
+ const higherValue = Math.max(value, secondValue);
141
+ const lowerPercent = getPercentage(lowerValue);
142
+ const higherPercent = getPercentage(higherValue);
143
+
144
+ // Adjust positions to account for spacing
145
+ let adjustedLowerPercent = lowerPercent + paddingPercent;
146
+ let adjustedHigherPercent = higherPercent - paddingPercent;
147
+
148
+ // Handle case when thumbs are very close
149
+ if (adjustedHigherPercent <= adjustedLowerPercent) {
150
+ adjustedLowerPercent = (lowerPercent + higherPercent) / 2 - 1;
151
+ adjustedHigherPercent = (lowerPercent + higherPercent) / 2 + 1;
152
+ }
153
+
154
+ // Calculate segment sizes
155
+ const startWidth = Math.max(0, lowerPercent - paddingPercent);
156
+ const activeWidth = Math.max(0, adjustedHigherPercent - adjustedLowerPercent);
157
+ const remainingWidth = Math.max(0, 100 - higherPercent - paddingPercent);
158
+
159
+ // Set styles
160
+ startTrack.style.display = 'block';
161
+ activeTrack.style.display = 'block';
162
+ remainingTrack.style.display = 'block';
163
+
164
+ // Horizontal orientation
165
+ setTrackStyles(startTrack, startWidth, 0);
166
+ setTrackStyles(activeTrack, activeWidth, adjustedLowerPercent);
167
+ setTrackStyles(remainingTrack, remainingWidth, higherPercent + paddingPercent);
168
+ } else {
169
+ // Single thumb slider
170
+ const adjustedWidth = Math.max(0, valuePercent - paddingPercent);
171
+ const remainingWidth = Math.max(0, 100 - valuePercent - paddingPercent);
172
+
173
+ // Hide start track for single thumb
174
+ startTrack.style.display = 'none';
175
+ activeTrack.style.display = 'block';
176
+ remainingTrack.style.display = 'block';
177
+
178
+ // Horizontal orientation
179
+ setTrackStyles(activeTrack, adjustedWidth, 0);
180
+ setTrackStyles(remainingTrack, remainingWidth, valuePercent + paddingPercent);
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Sets styles for track segments
186
+ * @param element Track segment element
187
+ * @param width Width as percentage
188
+ * @param left Left position as percentage
189
+ */
190
+ function setTrackStyles(element, width, left) {
191
+ element.style.width = `${width}%`;
192
+ element.style.left = `${left}%`;
193
+ element.style.height = '100%';
194
+ }
195
+
196
+ /**
197
+ * Applies style classes based on configuration
198
+ */
199
+ function applyStyleClasses() {
200
+ const baseClass = component.getClass('slider');
201
+
202
+ // Apply size class
203
+ const size = config.size || SLIDER_SIZES.MEDIUM;
204
+ if (size !== SLIDER_SIZES.MEDIUM) {
205
+ component.element.classList.add(`${baseClass}--${size}`);
206
+ }
207
+
208
+ // Apply color class
209
+ const color = config.color || SLIDER_COLORS.PRIMARY;
210
+ if (color !== SLIDER_COLORS.PRIMARY) {
211
+ component.element.classList.add(`${baseClass}--${color}`);
212
+ }
213
+
214
+ // Apply discrete class if step is specified
215
+ if (config.step !== undefined && config.step > 0) {
216
+ component.element.classList.add(`${baseClass}--discrete`);
217
+ }
218
+
219
+ // Apply disabled class if needed
220
+ if (config.disabled) {
221
+ component.element.classList.add(`${baseClass}--disabled`);
222
+ }
223
+ }
264
224
  };