mtrl 0.2.4 → 0.2.6
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.
- package/package.json +6 -3
- package/src/components/badge/_styles.scss +9 -9
- package/src/components/button/_styles.scss +0 -56
- package/src/components/button/button.ts +0 -2
- package/src/components/button/constants.ts +0 -6
- package/src/components/button/index.ts +2 -2
- package/src/components/button/types.ts +1 -7
- package/src/components/card/_styles.scss +67 -25
- package/src/components/card/api.ts +54 -3
- package/src/components/card/card.ts +33 -2
- package/src/components/card/config.ts +143 -21
- package/src/components/card/constants.ts +20 -19
- package/src/components/card/content.ts +299 -2
- package/src/components/card/features.ts +155 -4
- package/src/components/card/index.ts +31 -9
- package/src/components/card/types.ts +138 -15
- package/src/components/chip/chip.ts +1 -9
- package/src/components/chip/constants.ts +0 -10
- package/src/components/chip/index.ts +1 -1
- package/src/components/chip/types.ts +1 -4
- package/src/components/progress/_styles.scss +0 -65
- package/src/components/progress/config.ts +1 -2
- package/src/components/progress/constants.ts +0 -14
- package/src/components/progress/index.ts +1 -1
- package/src/components/progress/progress.ts +1 -4
- package/src/components/progress/types.ts +1 -4
- package/src/components/radios/_styles.scss +0 -45
- package/src/components/radios/api.ts +85 -60
- package/src/components/radios/config.ts +1 -2
- package/src/components/radios/constants.ts +0 -9
- package/src/components/radios/index.ts +1 -1
- package/src/components/radios/radio.ts +34 -11
- package/src/components/radios/radios.ts +2 -1
- package/src/components/radios/types.ts +1 -7
- package/src/components/slider/_styles.scss +193 -281
- package/src/components/slider/accessibility.md +59 -0
- package/src/components/slider/api.ts +36 -101
- package/src/components/slider/config.ts +29 -78
- package/src/components/slider/constants.ts +12 -8
- package/src/components/slider/features/appearance.ts +1 -47
- package/src/components/slider/features/disabled.ts +41 -16
- package/src/components/slider/features/interactions.ts +166 -26
- package/src/components/slider/features/keyboard.ts +125 -6
- package/src/components/slider/features/structure.ts +182 -195
- package/src/components/slider/features/ui.ts +234 -303
- package/src/components/slider/index.ts +11 -1
- package/src/components/slider/slider.ts +1 -1
- package/src/components/slider/types.ts +10 -25
- package/src/components/tabs/_styles.scss +285 -155
- package/src/components/tabs/api.ts +178 -400
- package/src/components/tabs/config.ts +46 -52
- package/src/components/tabs/constants.ts +85 -8
- package/src/components/tabs/features.ts +401 -0
- package/src/components/tabs/index.ts +60 -3
- package/src/components/tabs/indicator.ts +225 -0
- package/src/components/tabs/responsive.ts +144 -0
- package/src/components/tabs/scroll-indicators.ts +149 -0
- package/src/components/tabs/state.ts +186 -0
- package/src/components/tabs/tab-api.ts +258 -0
- package/src/components/tabs/tab.ts +255 -0
- package/src/components/tabs/tabs.ts +50 -31
- package/src/components/tabs/types.ts +324 -128
- package/src/components/tabs/utils.ts +107 -0
- package/src/components/textfield/_styles.scss +0 -98
- package/src/components/textfield/config.ts +2 -3
- package/src/components/textfield/constants.ts +0 -14
- package/src/components/textfield/index.ts +2 -2
- package/src/components/textfield/textfield.ts +0 -2
- package/src/components/textfield/types.ts +1 -4
- package/src/core/compose/component.ts +1 -1
- package/src/core/compose/features/badge.ts +79 -0
- package/src/core/compose/features/index.ts +3 -1
- package/src/styles/abstract/_theme.scss +106 -2
- package/src/components/card/actions.ts +0 -48
- package/src/components/card/header.ts +0 -88
- package/src/components/card/media.ts +0 -52
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
// src/components/slider/features/ui.ts
|
|
2
|
-
import { SLIDER_ORIENTATIONS } from '../constants';
|
|
1
|
+
// src/components/slider/features/ui.ts
|
|
3
2
|
import { SliderConfig } from '../types';
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -10,77 +9,83 @@ import { SliderConfig } from '../types';
|
|
|
10
9
|
* @returns UI update helper methods
|
|
11
10
|
*/
|
|
12
11
|
export const createUiHelpers = (config: SliderConfig, state) => {
|
|
13
|
-
//
|
|
14
|
-
if (!state.component
|
|
12
|
+
// Return empty implementations if component structure is missing
|
|
13
|
+
if (!state.component?.structure) {
|
|
15
14
|
console.error('Cannot create UI helpers: component structure is missing');
|
|
16
|
-
return
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
clamp
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
updateRemainingTrack: () => {},
|
|
25
|
-
updateThumbPositions: () => {},
|
|
26
|
-
updateValueBubbles: () => {},
|
|
27
|
-
showValueBubble: () => {},
|
|
28
|
-
generateTicks: () => {},
|
|
29
|
-
updateTicks: () => {},
|
|
30
|
-
updateUi: () => {}
|
|
31
|
-
};
|
|
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
|
+
() => {}]));
|
|
32
23
|
}
|
|
33
24
|
|
|
34
25
|
const {
|
|
35
|
-
track,
|
|
36
|
-
|
|
37
|
-
startTrack,
|
|
38
|
-
remainingTrack,
|
|
39
|
-
thumb,
|
|
40
|
-
valueBubble,
|
|
41
|
-
secondThumb,
|
|
42
|
-
secondValueBubble
|
|
26
|
+
track, activeTrack, startTrack, remainingTrack, thumb,
|
|
27
|
+
valueBubble, secondThumb, secondValueBubble, ticksContainer
|
|
43
28
|
} = state.component.structure;
|
|
44
29
|
|
|
45
30
|
/**
|
|
46
31
|
* Calculates percentage position for a value
|
|
47
|
-
* @param value Value to convert to percentage
|
|
48
|
-
* @returns Percentage position (0-100)
|
|
49
32
|
*/
|
|
50
33
|
const getPercentage = (value) => {
|
|
51
34
|
const range = state.max - state.min;
|
|
52
|
-
|
|
53
|
-
|
|
35
|
+
return range === 0 ? 0 : ((value - state.min) / range) * 100;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Gets track dimensions and constraints for positioning calculations
|
|
40
|
+
*/
|
|
41
|
+
const getTrackDimensions = () => {
|
|
42
|
+
if (!track || !thumb) return null;
|
|
43
|
+
|
|
44
|
+
const thumbRect = thumb.getBoundingClientRect();
|
|
45
|
+
const trackRect = track.getBoundingClientRect();
|
|
46
|
+
const thumbSize = thumbRect.width || 20;
|
|
47
|
+
const trackSize = trackRect.width;
|
|
48
|
+
|
|
49
|
+
const edgeConstraint = (thumbSize / 2) / trackSize * 100;
|
|
50
|
+
const paddingPercent = (8 / trackSize) * 100; // 8px padding
|
|
51
|
+
|
|
52
|
+
return { thumbSize, trackSize, edgeConstraint, paddingPercent };
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Map value percentage to visual position with edge constraints
|
|
57
|
+
*/
|
|
58
|
+
const mapValueToVisualPercent = (valuePercent, edgeConstraint) => {
|
|
59
|
+
const minEdge = edgeConstraint;
|
|
60
|
+
const maxEdge = 100 - edgeConstraint;
|
|
61
|
+
const visualRange = maxEdge - minEdge;
|
|
62
|
+
|
|
63
|
+
if (valuePercent <= 0) return minEdge;
|
|
64
|
+
if (valuePercent >= 100) return maxEdge;
|
|
65
|
+
return minEdge + (valuePercent / 100) * visualRange;
|
|
54
66
|
};
|
|
55
67
|
|
|
56
68
|
/**
|
|
57
69
|
* Gets slider value from a position on the track
|
|
58
|
-
* @param position Screen coordinate (clientX/clientY)
|
|
59
|
-
* @param vertical Whether slider is vertical
|
|
60
|
-
* @returns Calculated value
|
|
61
70
|
*/
|
|
62
|
-
const getValueFromPosition = (position
|
|
71
|
+
const getValueFromPosition = (position) => {
|
|
63
72
|
if (!track) return state.min;
|
|
64
73
|
|
|
65
74
|
try {
|
|
66
75
|
const trackRect = track.getBoundingClientRect();
|
|
67
76
|
const range = state.max - state.min;
|
|
68
77
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// Clamp percentage between 0 and 1
|
|
81
|
-
const clampedPercentage = Math.max(0, Math.min(1, percentageFromLeft));
|
|
82
|
-
return state.min + clampedPercentage * range;
|
|
83
|
-
}
|
|
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);
|
|
83
|
+
const effectiveWidth = rightEdge - leftEdge;
|
|
84
|
+
|
|
85
|
+
const adjustedPosition = Math.max(leftEdge, Math.min(rightEdge, position));
|
|
86
|
+
const percentageFromLeft = (adjustedPosition - leftEdge) / effectiveWidth;
|
|
87
|
+
|
|
88
|
+
return state.min + percentageFromLeft * range;
|
|
84
89
|
} catch (error) {
|
|
85
90
|
console.warn('Error calculating value from position:', error);
|
|
86
91
|
return state.min;
|
|
@@ -89,8 +94,6 @@ export const createUiHelpers = (config: SliderConfig, state) => {
|
|
|
89
94
|
|
|
90
95
|
/**
|
|
91
96
|
* Rounds a value to the nearest step
|
|
92
|
-
* @param value Value to round
|
|
93
|
-
* @returns Rounded value
|
|
94
97
|
*/
|
|
95
98
|
const roundToStep = (value) => {
|
|
96
99
|
const step = state.step;
|
|
@@ -102,208 +105,140 @@ export const createUiHelpers = (config: SliderConfig, state) => {
|
|
|
102
105
|
|
|
103
106
|
/**
|
|
104
107
|
* Clamps a value between min and max
|
|
105
|
-
* @param value Value to clamp
|
|
106
|
-
* @param min Minimum allowed value
|
|
107
|
-
* @param max Maximum allowed value
|
|
108
|
-
* @returns Clamped value
|
|
109
108
|
*/
|
|
110
109
|
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
111
110
|
|
|
112
111
|
/**
|
|
113
|
-
* Sets thumb position based on a value percentage
|
|
114
|
-
* @param thumbElement Thumb element to position
|
|
115
|
-
* @param valueBubbleElement Value bubble element to position
|
|
116
|
-
* @param valuePercent Percentage position (0-100)
|
|
112
|
+
* Sets thumb position based on a value percentage with proper edge mapping
|
|
117
113
|
*/
|
|
118
|
-
const setThumbPosition = (thumbElement,
|
|
119
|
-
if (!thumbElement) return;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
valueBubbleElement.style.left = `${valuePercent}%`;
|
|
135
|
-
}
|
|
114
|
+
const setThumbPosition = (thumbElement, bubbleElement, valuePercent) => {
|
|
115
|
+
if (!thumbElement || !track) return;
|
|
116
|
+
|
|
117
|
+
const dims = getTrackDimensions();
|
|
118
|
+
if (!dims) return;
|
|
119
|
+
|
|
120
|
+
const { thumbSize, trackSize } = dims;
|
|
121
|
+
const edgeConstraint = (thumbSize / 2) / trackSize * 100;
|
|
122
|
+
const adjustedPercent = mapValueToVisualPercent(valuePercent, edgeConstraint);
|
|
123
|
+
|
|
124
|
+
thumbElement.style.left = `${adjustedPercent}%`;
|
|
125
|
+
thumbElement.style.transform = 'translate(-50%, -50%)';
|
|
126
|
+
|
|
127
|
+
if (bubbleElement) {
|
|
128
|
+
bubbleElement.style.left = `${adjustedPercent}%`;
|
|
129
|
+
bubbleElement.style.transform = 'translateX(-50%)';
|
|
136
130
|
}
|
|
137
131
|
};
|
|
138
|
-
|
|
139
|
-
|
|
140
132
|
|
|
141
|
-
/**
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (!startTrack) return;
|
|
147
|
-
|
|
148
|
-
if (config.range && state.secondValue !== null) {
|
|
149
|
-
// For range slider, calculate the track from start to first thumb
|
|
150
|
-
const lowerValue = Math.min(state.value, state.secondValue);
|
|
151
|
-
const lowerPercent = getPercentage(lowerValue);
|
|
133
|
+
/**
|
|
134
|
+
* Updates start track styles
|
|
135
|
+
*/
|
|
136
|
+
const updateStartTrack = () => {
|
|
137
|
+
if (!startTrack || !track || !thumb) return;
|
|
152
138
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const isVertical = config.orientation === SLIDER_ORIENTATIONS.VERTICAL;
|
|
156
|
-
const trackSize = isVertical ? trackRect.height : trackRect.width;
|
|
139
|
+
const dims = getTrackDimensions();
|
|
140
|
+
if (!dims) return;
|
|
157
141
|
|
|
158
|
-
|
|
159
|
-
const paddingAdjustment = 8; // 8px padding
|
|
160
|
-
const paddingPercent = (paddingAdjustment / trackSize) * 100;
|
|
142
|
+
const { thumbSize, trackSize, paddingPercent } = dims;
|
|
161
143
|
|
|
162
|
-
if (
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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);
|
|
151
|
+
|
|
170
152
|
startTrack.style.display = 'block';
|
|
171
|
-
startTrack.style.width = `${
|
|
153
|
+
startTrack.style.width = `${finalPercent}%`;
|
|
172
154
|
startTrack.style.left = '0';
|
|
173
155
|
startTrack.style.height = '100%';
|
|
156
|
+
} else {
|
|
157
|
+
startTrack.style.display = 'none';
|
|
174
158
|
}
|
|
175
|
-
}
|
|
176
|
-
// For single thumb slider, no start track needed
|
|
177
|
-
startTrack.style.display = 'none';
|
|
178
|
-
}
|
|
179
|
-
};
|
|
159
|
+
};
|
|
180
160
|
|
|
181
|
-
/**
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const updateActiveTrack = () => {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
// Get track width for pixel calculations
|
|
188
|
-
const trackRect = track.getBoundingClientRect();
|
|
189
|
-
const isVertical = config.orientation === SLIDER_ORIENTATIONS.VERTICAL;
|
|
190
|
-
const trackSize = isVertical ? trackRect.height : trackRect.width;
|
|
191
|
-
|
|
192
|
-
// Calculate padding adjustment
|
|
193
|
-
const paddingAdjustment = 8; // 8px padding
|
|
194
|
-
const paddingPercent = (paddingAdjustment / trackSize) * 100;
|
|
195
|
-
|
|
196
|
-
if (config.range && state.secondValue !== null) {
|
|
197
|
-
// Range slider (two thumbs)
|
|
198
|
-
const lowerValue = Math.min(state.value, state.secondValue);
|
|
199
|
-
const higherValue = Math.max(state.value, state.secondValue);
|
|
200
|
-
const lowerPercent = getPercentage(lowerValue);
|
|
201
|
-
const higherPercent = getPercentage(higherValue);
|
|
202
|
-
|
|
203
|
-
// Adjust positions and width to account for spacing
|
|
204
|
-
let adjustedLowerPercent = lowerPercent;
|
|
205
|
-
let adjustedHigherPercent = higherPercent;
|
|
206
|
-
|
|
207
|
-
if (higherPercent - lowerPercent > paddingPercent * 2) {
|
|
208
|
-
// If there's enough space, add padding to both sides
|
|
209
|
-
adjustedLowerPercent = lowerPercent + paddingPercent;
|
|
210
|
-
adjustedHigherPercent = higherPercent - paddingPercent;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const trackLength = Math.max(0, adjustedHigherPercent - adjustedLowerPercent);
|
|
161
|
+
/**
|
|
162
|
+
* Updates active track styles
|
|
163
|
+
*/
|
|
164
|
+
const updateActiveTrack = () => {
|
|
165
|
+
if (!activeTrack || !track || !thumb) return;
|
|
214
166
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
activeTrack.style.height = `${trackLength}%`;
|
|
218
|
-
activeTrack.style.bottom = `${adjustedLowerPercent}%`;
|
|
219
|
-
activeTrack.style.top = 'auto';
|
|
220
|
-
activeTrack.style.width = '100%';
|
|
221
|
-
} else {
|
|
222
|
-
activeTrack.style.display = 'block';
|
|
223
|
-
activeTrack.style.width = `${trackLength}%`;
|
|
224
|
-
activeTrack.style.left = `${adjustedLowerPercent}%`;
|
|
225
|
-
activeTrack.style.height = '100%';
|
|
226
|
-
}
|
|
227
|
-
} else {
|
|
228
|
-
// Single thumb slider
|
|
229
|
-
const percent = getPercentage(state.value);
|
|
167
|
+
const dims = getTrackDimensions();
|
|
168
|
+
if (!dims) return;
|
|
230
169
|
|
|
231
|
-
|
|
232
|
-
const
|
|
170
|
+
const { thumbSize, trackSize, paddingPercent } = dims;
|
|
171
|
+
const edgeConstraint = (thumbSize / 2) / trackSize * 100;
|
|
233
172
|
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
173
|
+
if (config.range && state.secondValue !== null) {
|
|
174
|
+
// Range slider (two thumbs)
|
|
175
|
+
const lowerValue = Math.min(state.value, state.secondValue);
|
|
176
|
+
const higherValue = Math.max(state.value, state.secondValue);
|
|
177
|
+
const lowerPercent = getPercentage(lowerValue);
|
|
178
|
+
const higherPercent = getPercentage(higherValue);
|
|
179
|
+
|
|
180
|
+
const adjustedLower = mapValueToVisualPercent(lowerPercent, edgeConstraint) + paddingPercent;
|
|
181
|
+
const adjustedHigher = mapValueToVisualPercent(higherPercent, edgeConstraint) - paddingPercent;
|
|
182
|
+
|
|
183
|
+
// Calculate the actual percentage difference between thumbs
|
|
184
|
+
const valueDiffPercent = Math.abs(higherPercent - lowerPercent);
|
|
185
|
+
|
|
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;
|
|
189
|
+
|
|
190
|
+
if (valueDiffPercent <= hideThreshold) {
|
|
191
|
+
// Thumbs are too close together, hide the active track
|
|
192
|
+
activeTrack.style.display = 'none';
|
|
193
|
+
} else {
|
|
194
|
+
// Normal display of active track
|
|
195
|
+
let trackLength = Math.max(0, adjustedHigher - adjustedLower);
|
|
196
|
+
|
|
197
|
+
activeTrack.style.display = 'block';
|
|
198
|
+
activeTrack.style.width = `${trackLength}%`;
|
|
199
|
+
activeTrack.style.left = `${adjustedLower}%`;
|
|
200
|
+
activeTrack.style.height = '100%';
|
|
201
|
+
}
|
|
240
202
|
} else {
|
|
203
|
+
// Single thumb slider
|
|
204
|
+
const valuePercent = getPercentage(state.value);
|
|
205
|
+
const adjustedPercent = mapValueToVisualPercent(valuePercent, edgeConstraint);
|
|
206
|
+
const adjustedWidth = Math.max(0, adjustedPercent - paddingPercent);
|
|
207
|
+
|
|
241
208
|
activeTrack.style.display = 'block';
|
|
242
209
|
activeTrack.style.width = `${adjustedWidth}%`;
|
|
243
210
|
activeTrack.style.left = '0';
|
|
244
211
|
activeTrack.style.height = '100%';
|
|
245
212
|
}
|
|
246
|
-
}
|
|
247
|
-
};
|
|
213
|
+
};
|
|
248
214
|
|
|
249
|
-
/**
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const updateRemainingTrack = () => {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
// Get track width for pixel calculations
|
|
256
|
-
const trackRect = track.getBoundingClientRect();
|
|
257
|
-
const isVertical = config.orientation === SLIDER_ORIENTATIONS.VERTICAL;
|
|
258
|
-
const trackSize = isVertical ? trackRect.height : trackRect.width;
|
|
259
|
-
|
|
260
|
-
// Calculate padding adjustment
|
|
261
|
-
const paddingAdjustment = 8; // 8px padding
|
|
262
|
-
const paddingPercent = (paddingAdjustment / trackSize) * 100;
|
|
263
|
-
|
|
264
|
-
if (config.range && state.secondValue !== null) {
|
|
265
|
-
// Range slider (two thumbs)
|
|
266
|
-
const higherValue = Math.max(state.value, state.secondValue);
|
|
267
|
-
const higherPercent = getPercentage(higherValue);
|
|
268
|
-
|
|
269
|
-
// Adjust for right padding
|
|
270
|
-
const adjustedPercent = higherPercent + paddingPercent;
|
|
271
|
-
const adjustedWidth = Math.max(0, 100 - adjustedPercent);
|
|
272
|
-
|
|
273
|
-
if (isVertical) {
|
|
274
|
-
remainingTrack.style.display = 'block';
|
|
275
|
-
remainingTrack.style.height = `${adjustedWidth}%`;
|
|
276
|
-
remainingTrack.style.bottom = `${adjustedPercent}%`;
|
|
277
|
-
remainingTrack.style.top = 'auto';
|
|
278
|
-
remainingTrack.style.width = '100%';
|
|
279
|
-
} else {
|
|
280
|
-
remainingTrack.style.display = 'block';
|
|
281
|
-
remainingTrack.style.width = `${adjustedWidth}%`;
|
|
282
|
-
remainingTrack.style.left = `${adjustedPercent}%`;
|
|
283
|
-
remainingTrack.style.height = '100%';
|
|
284
|
-
}
|
|
285
|
-
} else {
|
|
286
|
-
// Single thumb slider
|
|
287
|
-
const percent = getPercentage(state.value);
|
|
215
|
+
/**
|
|
216
|
+
* Updates remaining track styles
|
|
217
|
+
*/
|
|
218
|
+
const updateRemainingTrack = () => {
|
|
219
|
+
if (!remainingTrack || !track || !thumb) return;
|
|
288
220
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const adjustedWidth = Math.max(0, 100 - adjustedPercent);
|
|
221
|
+
const dims = getTrackDimensions();
|
|
222
|
+
if (!dims) return;
|
|
292
223
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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
|
+
};
|
|
307
242
|
|
|
308
243
|
/**
|
|
309
244
|
* Updates thumb positions
|
|
@@ -336,56 +271,48 @@ const updateRemainingTrack = () => {
|
|
|
336
271
|
|
|
337
272
|
// Format the values
|
|
338
273
|
const formatter = config.valueFormatter || (value => value.toString());
|
|
339
|
-
const formattedValue = formatter(state.value);
|
|
340
|
-
|
|
341
|
-
// Update main value bubble
|
|
342
|
-
valueBubble.textContent = formattedValue;
|
|
343
274
|
|
|
344
|
-
// Update second value bubble if
|
|
275
|
+
// Update main and second value bubble if needed
|
|
276
|
+
valueBubble.textContent = formatter(state.value);
|
|
345
277
|
if (config.range && secondValueBubble && state.secondValue !== null) {
|
|
346
|
-
|
|
347
|
-
secondValueBubble.textContent = formattedSecondValue;
|
|
278
|
+
secondValueBubble.textContent = formatter(state.secondValue);
|
|
348
279
|
}
|
|
349
280
|
};
|
|
350
281
|
|
|
351
282
|
/**
|
|
352
283
|
* Shows or hides value bubble
|
|
353
|
-
* @param bubbleElement Value bubble element
|
|
354
|
-
* @param show Whether to show the bubble
|
|
355
284
|
*/
|
|
356
285
|
const showValueBubble = (bubbleElement, show) => {
|
|
357
286
|
if (!bubbleElement || !config.showValue) return;
|
|
358
287
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
} else {
|
|
362
|
-
bubbleElement.classList.remove(`${state.component.getClass('slider-value')}--visible`);
|
|
363
|
-
}
|
|
288
|
+
const visibleClass = `${state.component.getClass('slider-value')}--visible`;
|
|
289
|
+
bubbleElement.classList[show ? 'add' : 'remove'](visibleClass);
|
|
364
290
|
};
|
|
365
291
|
|
|
366
292
|
/**
|
|
367
|
-
* Generates tick marks
|
|
293
|
+
* Generates tick marks
|
|
368
294
|
*/
|
|
369
295
|
const generateTicks = () => {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
});
|
|
296
|
+
if (!ticksContainer) {
|
|
297
|
+
console.warn('Ticks container not found in component structure');
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
376
300
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
});
|
|
301
|
+
// Clear existing ticks
|
|
302
|
+
const sliderElement = state.component.element;
|
|
303
|
+
sliderElement.querySelectorAll(`.${state.component.getClass('slider-tick')}`)
|
|
304
|
+
.forEach(tick => tick.parentNode.removeChild(tick));
|
|
382
305
|
|
|
306
|
+
while (ticksContainer.firstChild) {
|
|
307
|
+
ticksContainer.removeChild(ticksContainer.firstChild);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Reset ticks array
|
|
383
311
|
state.ticks = [];
|
|
384
|
-
state.tickLabels = [];
|
|
385
312
|
|
|
386
|
-
if (!config.ticks
|
|
313
|
+
if (!config.ticks) return;
|
|
387
314
|
|
|
388
|
-
// Generate
|
|
315
|
+
// Generate tick values
|
|
389
316
|
const numSteps = Math.floor((state.max - state.min) / state.step);
|
|
390
317
|
const tickValues = [];
|
|
391
318
|
|
|
@@ -401,58 +328,41 @@ const updateRemainingTrack = () => {
|
|
|
401
328
|
tickValues.push(state.max);
|
|
402
329
|
}
|
|
403
330
|
|
|
331
|
+
// 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
|
+
const tickClass = state.component.getClass('slider-tick');
|
|
336
|
+
|
|
404
337
|
// Create ticks
|
|
405
338
|
tickValues.forEach(value => {
|
|
339
|
+
const percent = getPercentage(value);
|
|
340
|
+
|
|
341
|
+
// Create tick mark if enabled
|
|
406
342
|
if (config.ticks) {
|
|
407
343
|
const tick = document.createElement('div');
|
|
408
|
-
tick.classList.add(
|
|
344
|
+
tick.classList.add(tickClass);
|
|
409
345
|
|
|
410
346
|
// Position tick
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
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);
|
|
417
352
|
|
|
418
|
-
|
|
419
|
-
|
|
353
|
+
if (isExactlySelected) {
|
|
354
|
+
tick.classList.add(hiddenClass);
|
|
355
|
+
} else if (config.range && state.secondValue !== null) {
|
|
420
356
|
const lowerValue = Math.min(state.value, state.secondValue);
|
|
421
357
|
const higherValue = Math.max(state.value, state.secondValue);
|
|
422
358
|
|
|
423
|
-
|
|
424
|
-
tick.classList.add(`${state.component.getClass('slider-tick')}--active`);
|
|
425
|
-
}
|
|
426
|
-
} else if (value <= state.value) {
|
|
427
|
-
tick.classList.add(`${state.component.getClass('slider-tick')}--active`);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
state.component.element.appendChild(tick);
|
|
431
|
-
state.ticks.push(tick);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
if (config.tickLabels) {
|
|
435
|
-
const label = document.createElement('div');
|
|
436
|
-
label.classList.add(state.component.getClass('slider-label'));
|
|
437
|
-
|
|
438
|
-
// Position label
|
|
439
|
-
const percent = getPercentage(value);
|
|
440
|
-
if (config.orientation === SLIDER_ORIENTATIONS.VERTICAL) {
|
|
441
|
-
label.style.bottom = `${percent}%`;
|
|
442
|
-
} else {
|
|
443
|
-
label.style.left = `${percent}%`;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// Set label text
|
|
447
|
-
if (Array.isArray(config.tickLabels) && config.tickLabels[tickValues.indexOf(value)]) {
|
|
448
|
-
label.textContent = config.tickLabels[tickValues.indexOf(value)];
|
|
359
|
+
tick.classList.add(value >= lowerValue && value <= higherValue ? activeClass : inactiveClass);
|
|
449
360
|
} else {
|
|
450
|
-
|
|
451
|
-
label.textContent = formatter(value);
|
|
361
|
+
tick.classList.add(value <= state.value ? activeClass : inactiveClass);
|
|
452
362
|
}
|
|
453
363
|
|
|
454
|
-
|
|
455
|
-
state.
|
|
364
|
+
ticksContainer.appendChild(tick);
|
|
365
|
+
state.ticks.push(tick);
|
|
456
366
|
}
|
|
457
367
|
});
|
|
458
368
|
};
|
|
@@ -461,26 +371,47 @@ const updateRemainingTrack = () => {
|
|
|
461
371
|
* Updates active state of tick marks
|
|
462
372
|
*/
|
|
463
373
|
const updateTicks = () => {
|
|
374
|
+
if (!state.ticks || state.ticks.length === 0) return;
|
|
375
|
+
|
|
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`;
|
|
379
|
+
|
|
464
380
|
// Update active ticks based on current value
|
|
465
381
|
state.ticks.forEach((tick, index) => {
|
|
382
|
+
// Calculate the value for this tick based on its index
|
|
466
383
|
const tickValue = state.min + (index * state.step);
|
|
467
384
|
|
|
468
|
-
|
|
469
|
-
|
|
385
|
+
// First, reset visibility
|
|
386
|
+
tick.classList.remove(hiddenClass);
|
|
387
|
+
|
|
388
|
+
// 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);
|
|
391
|
+
|
|
392
|
+
if (isExactlySelected) {
|
|
393
|
+
// Hide this tick as it exactly matches a selected value
|
|
394
|
+
tick.classList.add(hiddenClass);
|
|
395
|
+
} else if (config.range && state.secondValue !== null) {
|
|
396
|
+
// Range slider - ticks between values should be active
|
|
470
397
|
const lowerValue = Math.min(state.value, state.secondValue);
|
|
471
398
|
const higherValue = Math.max(state.value, state.secondValue);
|
|
472
399
|
|
|
473
400
|
if (tickValue >= lowerValue && tickValue <= higherValue) {
|
|
474
|
-
tick.classList.add(
|
|
401
|
+
tick.classList.add(activeClass);
|
|
402
|
+
tick.classList.remove(inactiveClass);
|
|
475
403
|
} else {
|
|
476
|
-
tick.classList.remove(
|
|
404
|
+
tick.classList.remove(activeClass);
|
|
405
|
+
tick.classList.add(inactiveClass);
|
|
477
406
|
}
|
|
478
407
|
} else {
|
|
479
|
-
// Single slider - ticks below
|
|
408
|
+
// Single slider - ticks below value should be active
|
|
480
409
|
if (tickValue <= state.value) {
|
|
481
|
-
tick.classList.add(
|
|
410
|
+
tick.classList.add(activeClass);
|
|
411
|
+
tick.classList.remove(inactiveClass);
|
|
482
412
|
} else {
|
|
483
|
-
tick.classList.remove(
|
|
413
|
+
tick.classList.remove(activeClass);
|
|
414
|
+
tick.classList.add(inactiveClass);
|
|
484
415
|
}
|
|
485
416
|
}
|
|
486
417
|
});
|
|
@@ -491,7 +422,7 @@ const updateRemainingTrack = () => {
|
|
|
491
422
|
*/
|
|
492
423
|
const updateUi = () => {
|
|
493
424
|
updateThumbPositions();
|
|
494
|
-
updateStartTrack();
|
|
425
|
+
updateStartTrack();
|
|
495
426
|
updateActiveTrack();
|
|
496
427
|
updateRemainingTrack();
|
|
497
428
|
updateValueBubbles();
|