mtrl 0.2.7 → 0.2.9
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/index.ts +2 -0
- package/package.json +14 -3
- package/src/components/badge/api.ts +23 -14
- package/src/components/badge/badge.ts +2 -2
- package/src/components/badge/config.ts +10 -11
- package/src/components/badge/features.ts +15 -10
- package/src/components/badge/index.ts +27 -2
- package/src/components/badge/types.ts +28 -8
- package/src/components/bottom-app-bar/bottom-app-bar.ts +2 -44
- package/src/components/bottom-app-bar/config.ts +1 -45
- package/src/components/bottom-app-bar/index.ts +7 -1
- package/src/components/bottom-app-bar/types.ts +7 -1
- package/src/components/button/button.ts +0 -1
- package/src/components/button/config.ts +1 -2
- package/src/components/button/index.ts +10 -2
- package/src/components/button/types.ts +14 -2
- package/src/components/card/config.ts +17 -9
- package/src/components/card/content.ts +8 -10
- package/src/components/card/features.ts +4 -6
- package/src/components/card/index.ts +29 -2
- package/src/components/card/types.ts +6 -23
- package/src/components/checkbox/config.ts +3 -4
- package/src/components/checkbox/index.ts +1 -2
- package/src/components/checkbox/types.ts +12 -3
- package/src/components/chip/api.ts +170 -221
- package/src/components/chip/chip.ts +34 -302
- package/src/components/chip/config.ts +1 -2
- package/src/components/chip/index.ts +10 -2
- package/src/components/chip/types.ts +224 -35
- package/src/components/datepicker/api.ts +18 -25
- package/src/components/datepicker/config.ts +9 -12
- package/src/components/datepicker/datepicker.ts +7 -12
- package/src/components/datepicker/index.ts +10 -7
- package/src/components/datepicker/render.ts +16 -18
- package/src/components/datepicker/types.ts +164 -35
- package/src/components/datepicker/utils.ts +1 -2
- package/src/components/dialog/api.ts +7 -8
- package/src/components/dialog/config.ts +3 -4
- package/src/components/dialog/features.ts +56 -22
- package/src/components/dialog/index.ts +38 -8
- package/src/components/dialog/types.ts +33 -10
- package/src/components/divider/index.ts +5 -1
- package/src/components/extended-fab/config.ts +6 -2
- package/src/components/extended-fab/index.ts +7 -2
- package/src/components/extended-fab/types.ts +21 -4
- package/src/components/fab/config.ts +3 -4
- package/src/components/fab/fab.ts +1 -1
- package/src/components/fab/index.ts +7 -2
- package/src/components/fab/types.ts +21 -4
- package/src/components/list/config.ts +4 -5
- package/src/components/list/features.ts +6 -7
- package/src/components/list/index.ts +7 -9
- package/src/components/list/list-item.ts +12 -13
- package/src/components/list/types.ts +50 -5
- package/src/components/list/utils.ts +30 -3
- package/src/components/menu/features/items-manager.ts +9 -9
- package/src/components/menu/features/positioning.ts +7 -7
- package/src/components/menu/features/visibility.ts +7 -7
- package/src/components/menu/index.ts +7 -9
- package/src/components/menu/menu-item.ts +6 -6
- package/src/components/menu/menu.ts +22 -0
- package/src/components/menu/types.ts +29 -10
- package/src/components/menu/utils.ts +67 -0
- package/src/components/navigation/api.ts +131 -96
- package/src/components/navigation/config.ts +22 -10
- package/src/components/navigation/features/controller.ts +273 -0
- package/src/components/navigation/features/items.ts +160 -87
- package/src/components/navigation/index.ts +0 -6
- package/src/components/navigation/nav-item.ts +12 -24
- package/src/components/navigation/navigation.ts +21 -8
- package/src/components/navigation/system-types.ts +124 -0
- package/src/components/navigation/system.ts +776 -0
- package/src/components/navigation/types.ts +228 -203
- package/src/components/progress/api.ts +2 -3
- package/src/components/progress/config.ts +2 -3
- package/src/components/progress/index.ts +0 -1
- package/src/components/progress/progress.ts +1 -2
- package/src/components/progress/types.ts +186 -33
- package/src/components/radios/config.ts +1 -1
- package/src/components/radios/index.ts +0 -1
- package/src/components/radios/types.ts +0 -7
- package/src/components/search/config.ts +1 -2
- package/src/components/search/features/search.ts +14 -15
- package/src/components/search/features/states.ts +5 -1
- package/src/components/search/features/structure.ts +3 -4
- package/src/components/search/index.ts +0 -3
- package/src/components/search/types.ts +18 -6
- package/src/components/segmented-button/config.ts +20 -7
- package/src/components/segmented-button/segment.ts +6 -7
- package/src/components/segmented-button/segmented-button.ts +4 -5
- package/src/components/segmented-button/types.ts +37 -2
- package/src/components/slider/config.ts +20 -2
- package/src/components/slider/features/controller.ts +761 -0
- package/src/components/slider/features/handlers.ts +18 -15
- package/src/components/slider/features/index.ts +3 -2
- package/src/components/slider/features/range.ts +104 -0
- package/src/components/slider/slider.ts +34 -14
- package/src/components/slider/structure.ts +152 -0
- package/src/components/slider/types.ts +34 -8
- package/src/components/snackbar/config.ts +2 -3
- package/src/components/snackbar/constants.ts +0 -32
- package/src/components/snackbar/index.ts +0 -1
- package/src/components/snackbar/position.ts +9 -1
- package/src/components/snackbar/types.ts +122 -46
- package/src/components/switch/config.ts +2 -3
- package/src/components/switch/index.ts +0 -1
- package/src/components/switch/types.ts +3 -2
- package/src/components/tabs/config.ts +3 -4
- package/src/components/tabs/index.ts +0 -15
- package/src/components/tabs/tab-api.ts +12 -4
- package/src/components/tabs/tab.ts +18 -6
- package/src/components/tabs/types.ts +13 -3
- package/src/components/textfield/api.ts +53 -0
- package/src/components/textfield/config.ts +2 -3
- package/src/components/textfield/features.ts +322 -0
- package/src/components/textfield/index.ts +0 -1
- package/src/components/textfield/textfield.ts +8 -0
- package/src/components/textfield/types.ts +29 -6
- package/src/components/timepicker/api.ts +1 -1
- package/src/components/timepicker/clockdial.ts +2 -5
- package/src/components/timepicker/config.ts +102 -4
- package/src/components/timepicker/index.ts +1 -6
- package/src/components/timepicker/render.ts +1 -1
- package/src/components/timepicker/timepicker.ts +1 -1
- package/src/components/tooltip/api.ts +1 -1
- package/src/components/tooltip/config.ts +27 -6
- package/src/components/tooltip/index.ts +0 -1
- package/src/components/tooltip/types.ts +13 -3
- package/src/core/compose/features/textinput.ts +15 -2
- package/src/core/compose/features/textlabel.ts +0 -3
- package/src/core/composition/features/dom.ts +33 -0
- package/src/core/composition/features/icon.ts +131 -0
- package/src/core/composition/features/index.ts +11 -0
- package/src/core/composition/features/label.ts +156 -0
- package/src/core/composition/features/structure.ts +22 -0
- package/src/core/composition/index.ts +26 -0
- package/src/core/index.ts +1 -1
- package/src/core/structure.ts +288 -0
- package/src/index.ts +1 -0
- package/src/styles/components/_navigation-mobile.scss +244 -0
- package/src/styles/components/_navigation-system.scss +151 -0
- package/src/{components/tabs/_styles.scss → styles/components/_tabs.scss} +1 -1
- package/src/{components/textfield/_styles.scss → styles/components/_textfield.scss} +314 -72
- package/src/styles/main.scss +98 -49
- package/src/components/badge/constants.ts +0 -40
- package/src/components/button/constants.ts +0 -11
- package/src/components/card/constants.ts +0 -84
- package/src/components/datepicker/constants.ts +0 -98
- package/src/components/dialog/constants.ts +0 -32
- package/src/components/extended-fab/constants.ts +0 -36
- package/src/components/fab/constants.ts +0 -41
- package/src/components/menu/constants.ts +0 -154
- package/src/components/navigation/constants.ts +0 -200
- package/src/components/progress/constants.ts +0 -29
- package/src/components/search/constants.ts +0 -21
- package/src/components/segmented-button/constants.ts +0 -42
- package/src/components/slider/features/slider.ts +0 -318
- package/src/components/slider/features/structure.ts +0 -181
- package/src/components/slider/features/ui.ts +0 -388
- package/src/components/switch/constants.ts +0 -80
- package/src/components/tabs/constants.ts +0 -89
- package/src/components/textfield/constants.ts +0 -100
- package/src/components/timepicker/constants.ts +0 -138
- /package/src/{components/badge/_styles.scss → styles/components/_badge.scss} +0 -0
- /package/src/{components/bottom-app-bar/_styles.scss → styles/components/_bottom-app-bar.scss} +0 -0
- /package/src/{components/button/_styles.scss → styles/components/_button.scss} +0 -0
- /package/src/{components/card/_styles.scss → styles/components/_card.scss} +0 -0
- /package/src/{components/carousel/_styles.scss → styles/components/_carousel.scss} +0 -0
- /package/src/{components/checkbox/_styles.scss → styles/components/_checkbox.scss} +0 -0
- /package/src/{components/chip/_styles.scss → styles/components/_chip.scss} +0 -0
- /package/src/{components/datepicker/_styles.scss → styles/components/_datepicker.scss} +0 -0
- /package/src/{components/dialog/_styles.scss → styles/components/_dialog.scss} +0 -0
- /package/src/{components/divider/_styles.scss → styles/components/_divider.scss} +0 -0
- /package/src/{components/extended-fab/_styles.scss → styles/components/_extended-fab.scss} +0 -0
- /package/src/{components/fab/_styles.scss → styles/components/_fab.scss} +0 -0
- /package/src/{components/list/_styles.scss → styles/components/_list.scss} +0 -0
- /package/src/{components/menu/_styles.scss → styles/components/_menu.scss} +0 -0
- /package/src/{components/navigation/_styles.scss → styles/components/_navigation.scss} +0 -0
- /package/src/{components/progress/_styles.scss → styles/components/_progress.scss} +0 -0
- /package/src/{components/radios/_styles.scss → styles/components/_radios.scss} +0 -0
- /package/src/{components/search/_styles.scss → styles/components/_search.scss} +0 -0
- /package/src/{components/segmented-button/_styles.scss → styles/components/_segmented-button.scss} +0 -0
- /package/src/{components/sheet/_styles.scss → styles/components/_sheet.scss} +0 -0
- /package/src/{components/slider/_styles.scss → styles/components/_slider.scss} +0 -0
- /package/src/{components/snackbar/_styles.scss → styles/components/_snackbar.scss} +0 -0
- /package/src/{components/switch/_styles.scss → styles/components/_switch.scss} +0 -0
- /package/src/{components/timepicker/_styles.scss → styles/components/_timepicker.scss} +0 -0
- /package/src/{components/tooltip/_styles.scss → styles/components/_tooltip.scss} +0 -0
- /package/src/{components/top-app-bar/_styles.scss → styles/components/_top-app-bar.scss} +0 -0
- /package/src/styles/utilities/{_color.scss → _colors.scss} +0 -0
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
// src/components/slider/features/structure.ts
|
|
2
|
-
import { SLIDER_COLORS, SLIDER_SIZES } from '../constants';
|
|
3
|
-
import { SliderConfig } from '../types';
|
|
4
|
-
import createLayout from '../../../core/layout';
|
|
5
|
-
import { createElement } from '../../../core/dom/create';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Creates the slider DOM structure using layout system
|
|
9
|
-
* @param config Slider configuration
|
|
10
|
-
* @returns Component enhancer with DOM structure
|
|
11
|
-
*/
|
|
12
|
-
export const withStructure = (config: SliderConfig) => component => {
|
|
13
|
-
// Set default values
|
|
14
|
-
const min = config.min || 0;
|
|
15
|
-
const max = config.max || 100;
|
|
16
|
-
const value = config.value !== undefined ? config.value : min;
|
|
17
|
-
const secondValue = config.secondValue !== undefined ? config.secondValue : null;
|
|
18
|
-
const isRangeSlider = config.range && secondValue !== null;
|
|
19
|
-
const isDisabled = config.disabled === true;
|
|
20
|
-
|
|
21
|
-
// Format the values
|
|
22
|
-
const formatter = config.valueFormatter || (val => val.toString());
|
|
23
|
-
|
|
24
|
-
// Get prefixed class names
|
|
25
|
-
const getClass = (className) => component.getClass(className);
|
|
26
|
-
|
|
27
|
-
// Define slider structure
|
|
28
|
-
const structure = [
|
|
29
|
-
[createElement, 'container', {
|
|
30
|
-
className: getClass('slider-container')
|
|
31
|
-
}, [
|
|
32
|
-
// Track with segments
|
|
33
|
-
[createElement, 'track', {
|
|
34
|
-
className: getClass('slider-track')
|
|
35
|
-
}, [
|
|
36
|
-
[createElement, 'startTrack', {
|
|
37
|
-
className: getClass('slider-start-track')
|
|
38
|
-
}],
|
|
39
|
-
[createElement, 'activeTrack', {
|
|
40
|
-
className: getClass('slider-active-track')
|
|
41
|
-
}],
|
|
42
|
-
[createElement, 'remainingTrack', {
|
|
43
|
-
className: getClass('slider-remaining-track')
|
|
44
|
-
}]
|
|
45
|
-
]],
|
|
46
|
-
|
|
47
|
-
// Ticks container
|
|
48
|
-
[createElement, 'ticksContainer', {
|
|
49
|
-
className: getClass('slider-ticks-container')
|
|
50
|
-
}],
|
|
51
|
-
|
|
52
|
-
// Dots for ends
|
|
53
|
-
[createElement, 'startDot', {
|
|
54
|
-
className: [
|
|
55
|
-
getClass('slider-dot'),
|
|
56
|
-
getClass('slider-dot--start')
|
|
57
|
-
]
|
|
58
|
-
}],
|
|
59
|
-
[createElement, 'endDot', {
|
|
60
|
-
className: [
|
|
61
|
-
getClass('slider-dot'),
|
|
62
|
-
getClass('slider-dot--end')
|
|
63
|
-
]
|
|
64
|
-
}],
|
|
65
|
-
|
|
66
|
-
// Main handle
|
|
67
|
-
[createElement, 'handle', {
|
|
68
|
-
className: getClass('slider-handle'),
|
|
69
|
-
attrs: {
|
|
70
|
-
'role': 'slider',
|
|
71
|
-
'aria-valuemin': String(min),
|
|
72
|
-
'aria-valuemax': String(max),
|
|
73
|
-
'aria-valuenow': String(value),
|
|
74
|
-
'aria-orientation': 'horizontal',
|
|
75
|
-
'tabindex': isDisabled ? '-1' : '0',
|
|
76
|
-
'aria-disabled': isDisabled ? 'true' : 'false'
|
|
77
|
-
},
|
|
78
|
-
style: {
|
|
79
|
-
left: `${((value - min) / (max - min)) * 100}%`
|
|
80
|
-
}
|
|
81
|
-
}],
|
|
82
|
-
|
|
83
|
-
// Main value bubble
|
|
84
|
-
[createElement, 'valueBubble', {
|
|
85
|
-
className: getClass('slider-value'),
|
|
86
|
-
text: formatter(value)
|
|
87
|
-
}]
|
|
88
|
-
]]
|
|
89
|
-
];
|
|
90
|
-
|
|
91
|
-
// Add second handle and bubble for range slider
|
|
92
|
-
if (isRangeSlider) {
|
|
93
|
-
const secondHandlePos = ((secondValue - min) / (max - min)) * 100;
|
|
94
|
-
|
|
95
|
-
// Add second handle to structure
|
|
96
|
-
structure[0][3].push(
|
|
97
|
-
[createElement, 'secondHandle', {
|
|
98
|
-
className: getClass('slider-handle'),
|
|
99
|
-
attrs: {
|
|
100
|
-
'role': 'slider',
|
|
101
|
-
'aria-valuemin': String(min),
|
|
102
|
-
'aria-valuemax': String(max),
|
|
103
|
-
'aria-valuenow': String(secondValue),
|
|
104
|
-
'aria-orientation': 'horizontal',
|
|
105
|
-
'tabindex': isDisabled ? '-1' : '0',
|
|
106
|
-
'aria-disabled': isDisabled ? 'true' : 'false'
|
|
107
|
-
},
|
|
108
|
-
style: {
|
|
109
|
-
left: `${secondHandlePos}%`
|
|
110
|
-
}
|
|
111
|
-
}]
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
// Add second bubble to structure
|
|
115
|
-
structure[0][3].push(
|
|
116
|
-
[createElement, 'secondValueBubble', {
|
|
117
|
-
className: getClass('slider-value'),
|
|
118
|
-
text: formatter(secondValue)
|
|
119
|
-
}]
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Create layout and get structure elements
|
|
124
|
-
const components = createLayout(structure, component.element).component;
|
|
125
|
-
|
|
126
|
-
// Add component base class and accessibility attributes
|
|
127
|
-
component.element.classList.add(component.getClass('slider'));
|
|
128
|
-
component.element.setAttribute('tabindex', '-1');
|
|
129
|
-
component.element.setAttribute('role', 'none');
|
|
130
|
-
component.element.setAttribute('aria-disabled', isDisabled ? 'true' : 'false');
|
|
131
|
-
|
|
132
|
-
// Position any icon properly
|
|
133
|
-
const iconElement = component.element.querySelector(`.${component.getClass('icon')}`);
|
|
134
|
-
if (iconElement && config.label) {
|
|
135
|
-
iconElement.classList.add(component.getClass('slider-icon'));
|
|
136
|
-
component.element.classList.add(component.getClass('slider--icon'));
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Apply style classes
|
|
140
|
-
applyStyleClasses(component, config, isRangeSlider, isDisabled);
|
|
141
|
-
|
|
142
|
-
// Return enhanced component with structure
|
|
143
|
-
return {
|
|
144
|
-
...component,
|
|
145
|
-
structure: components
|
|
146
|
-
};
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Applies style classes based on configuration
|
|
151
|
-
*/
|
|
152
|
-
function applyStyleClasses(component, config, isRangeSlider, isDisabled) {
|
|
153
|
-
const baseClass = component.getClass('slider');
|
|
154
|
-
|
|
155
|
-
// Apply size class
|
|
156
|
-
const size = config.size || SLIDER_SIZES.MEDIUM;
|
|
157
|
-
if (size !== SLIDER_SIZES.MEDIUM) {
|
|
158
|
-
component.element.classList.add(`${baseClass}--${size}`);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Apply color class
|
|
162
|
-
const color = config.color || SLIDER_COLORS.PRIMARY;
|
|
163
|
-
if (color !== SLIDER_COLORS.PRIMARY) {
|
|
164
|
-
component.element.classList.add(`${baseClass}--${color}`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Apply discrete class if step is specified
|
|
168
|
-
if (config.step !== undefined && config.step > 0) {
|
|
169
|
-
component.element.classList.add(`${baseClass}--discrete`);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Apply disabled class if needed
|
|
173
|
-
if (isDisabled) {
|
|
174
|
-
component.element.classList.add(`${baseClass}--disabled`);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Apply range class if needed
|
|
178
|
-
if (isRangeSlider) {
|
|
179
|
-
component.element.classList.add(`${baseClass}--range`);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
@@ -1,388 +0,0 @@
|
|
|
1
|
-
// src/components/slider/features/ui.ts
|
|
2
|
-
import { SliderConfig } from '../types';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Create optimized UI update helpers for slider component
|
|
6
|
-
*
|
|
7
|
-
* @param config Slider configuration
|
|
8
|
-
* @param state Slider state object
|
|
9
|
-
* @returns UI update helper methods
|
|
10
|
-
*/
|
|
11
|
-
export const createUiHelpers = (config: SliderConfig, state) => {
|
|
12
|
-
// Return empty implementations if component structure is missing
|
|
13
|
-
if (!state.component?.structure) {
|
|
14
|
-
console.error('Cannot create UI helpers: component structure is missing');
|
|
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
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const {
|
|
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
|
|
38
|
-
} = state.component.structure;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Calculates percentage position for a value
|
|
42
|
-
*/
|
|
43
|
-
const getPercentage = (value) => {
|
|
44
|
-
const range = state.max - state.min;
|
|
45
|
-
return range === 0 ? 0 : ((value - state.min) / range) * 100;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Gets track dimensions and constraints for positioning calculations
|
|
50
|
-
*/
|
|
51
|
-
const getTrackDimensions = () => {
|
|
52
|
-
if (!track || !handle || !container) return null;
|
|
53
|
-
|
|
54
|
-
const handleRect = handle.getBoundingClientRect();
|
|
55
|
-
const trackRect = container.getBoundingClientRect();
|
|
56
|
-
const handleSize = handleRect.width || 20;
|
|
57
|
-
const trackSize = trackRect.width;
|
|
58
|
-
|
|
59
|
-
const edgeConstraint = (handleSize / 2) / trackSize * 100;
|
|
60
|
-
const paddingPixels = state.activeHandle ? 6 : 8;
|
|
61
|
-
const paddingPercent = (paddingPixels / trackSize) * 100;
|
|
62
|
-
|
|
63
|
-
return { handleSize, trackSize, edgeConstraint, paddingPercent };
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Maps value percentage to visual position with edge constraints
|
|
68
|
-
*/
|
|
69
|
-
const mapValueToVisualPercent = (valuePercent, edgeConstraint) => {
|
|
70
|
-
const minEdge = edgeConstraint;
|
|
71
|
-
const maxEdge = 100 - edgeConstraint;
|
|
72
|
-
const visualRange = maxEdge - minEdge;
|
|
73
|
-
|
|
74
|
-
if (valuePercent <= 0) return minEdge;
|
|
75
|
-
if (valuePercent >= 100) return maxEdge;
|
|
76
|
-
return minEdge + (valuePercent / 100) * visualRange;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Gets slider value from a position on the track
|
|
81
|
-
*/
|
|
82
|
-
const getValueFromPosition = (position) => {
|
|
83
|
-
if (!track || !container) return state.min;
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
const containerRect = container.getBoundingClientRect();
|
|
87
|
-
const range = state.max - state.min;
|
|
88
|
-
const handleWidth = handle.getBoundingClientRect().width || 20;
|
|
89
|
-
|
|
90
|
-
const leftEdge = containerRect.left + (handleWidth / 2);
|
|
91
|
-
const rightEdge = containerRect.right - (handleWidth / 2);
|
|
92
|
-
const effectiveWidth = rightEdge - leftEdge;
|
|
93
|
-
|
|
94
|
-
const adjustedPosition = Math.max(leftEdge, Math.min(rightEdge, position));
|
|
95
|
-
const percentageFromLeft = (adjustedPosition - leftEdge) / effectiveWidth;
|
|
96
|
-
|
|
97
|
-
return state.min + percentageFromLeft * range;
|
|
98
|
-
} catch (error) {
|
|
99
|
-
console.warn('Error calculating value from position:', error);
|
|
100
|
-
return state.min;
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Rounds a value to the nearest step
|
|
106
|
-
*/
|
|
107
|
-
const roundToStep = (value) => {
|
|
108
|
-
const step = state.step;
|
|
109
|
-
if (step <= 0) return value;
|
|
110
|
-
|
|
111
|
-
const steps = Math.round((value - state.min) / step);
|
|
112
|
-
return state.min + (steps * step);
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Clamps a value between min and max
|
|
117
|
-
*/
|
|
118
|
-
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Updates handle and bubble positions and transforms
|
|
122
|
-
*/
|
|
123
|
-
const updateHandlePositions = () => {
|
|
124
|
-
if (!handle || !container) return;
|
|
125
|
-
|
|
126
|
-
const dims = getTrackDimensions();
|
|
127
|
-
if (!dims) return;
|
|
128
|
-
|
|
129
|
-
const { edgeConstraint } = dims;
|
|
130
|
-
|
|
131
|
-
// Update main handle position
|
|
132
|
-
const percent = getPercentage(state.value);
|
|
133
|
-
const adjustedPercent = mapValueToVisualPercent(percent, edgeConstraint);
|
|
134
|
-
|
|
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%)';
|
|
140
|
-
}
|
|
141
|
-
|
|
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);
|
|
146
|
-
|
|
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));
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Updates all track segments at once with optimized positioning
|
|
164
|
-
*/
|
|
165
|
-
const updateTrackSegments = () => {
|
|
166
|
-
if (!track || !container || !handle) return;
|
|
167
|
-
|
|
168
|
-
const dims = getTrackDimensions();
|
|
169
|
-
if (!dims) return;
|
|
170
|
-
|
|
171
|
-
const { handleSize, trackSize, paddingPercent } = dims;
|
|
172
|
-
const edgeConstraint = (handleSize / 2) / trackSize * 100;
|
|
173
|
-
|
|
174
|
-
if (config.range && state.secondValue !== null) {
|
|
175
|
-
// Range slider setup
|
|
176
|
-
const lowerValue = Math.min(state.value, state.secondValue);
|
|
177
|
-
const higherValue = Math.max(state.value, state.secondValue);
|
|
178
|
-
const lowerPercent = getPercentage(lowerValue);
|
|
179
|
-
const higherPercent = getPercentage(higherValue);
|
|
180
|
-
|
|
181
|
-
const adjustedLower = mapValueToVisualPercent(lowerPercent, edgeConstraint);
|
|
182
|
-
const adjustedHigher = mapValueToVisualPercent(higherPercent, edgeConstraint);
|
|
183
|
-
|
|
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
|
-
}
|
|
193
|
-
|
|
194
|
-
// Active track (between handles)
|
|
195
|
-
const valueDiffPercent = Math.abs(higherPercent - lowerPercent);
|
|
196
|
-
const hideThreshold = (handleSize / trackSize) * 100;
|
|
197
|
-
|
|
198
|
-
if (valueDiffPercent <= hideThreshold) {
|
|
199
|
-
activeTrack.style.display = 'none';
|
|
200
|
-
} else {
|
|
201
|
-
activeTrack.style.display = 'block';
|
|
202
|
-
activeTrack.style.left = `${adjustedLower + paddingPercent}%`;
|
|
203
|
-
activeTrack.style.right = `${100 - (adjustedHigher - paddingPercent)}%`;
|
|
204
|
-
activeTrack.style.width = 'auto';
|
|
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';
|
|
212
|
-
} else {
|
|
213
|
-
// Single handle slider
|
|
214
|
-
const valuePercent = getPercentage(state.value);
|
|
215
|
-
const adjustedPercent = mapValueToVisualPercent(valuePercent, edgeConstraint);
|
|
216
|
-
|
|
217
|
-
// Hide start track for single slider
|
|
218
|
-
startTrack.style.display = 'none';
|
|
219
|
-
|
|
220
|
-
// Active track (filled part)
|
|
221
|
-
activeTrack.style.display = 'block';
|
|
222
|
-
activeTrack.style.left = '0';
|
|
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';
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Updates value bubble content
|
|
236
|
-
*/
|
|
237
|
-
const updateValueBubbles = () => {
|
|
238
|
-
if (!valueBubble) return;
|
|
239
|
-
|
|
240
|
-
const formatter = config.valueFormatter || (value => value.toString());
|
|
241
|
-
valueBubble.textContent = formatter(state.value);
|
|
242
|
-
|
|
243
|
-
if (config.range && secondValueBubble && state.secondValue !== null) {
|
|
244
|
-
secondValueBubble.textContent = formatter(state.secondValue);
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Shows or hides value bubble
|
|
250
|
-
*/
|
|
251
|
-
const showValueBubble = (bubbleElement, show) => {
|
|
252
|
-
if (!bubbleElement || !config.showValue) return;
|
|
253
|
-
|
|
254
|
-
bubbleElement.classList[show ? 'add' : 'remove'](`${state.component.getClass('slider-value')}--visible`);
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Generates tick marks
|
|
259
|
-
*/
|
|
260
|
-
const generateTicks = () => {
|
|
261
|
-
if (!ticksContainer || !container) return;
|
|
262
|
-
|
|
263
|
-
// Clear existing ticks
|
|
264
|
-
while (ticksContainer.firstChild) {
|
|
265
|
-
ticksContainer.removeChild(ticksContainer.firstChild);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
state.ticks = [];
|
|
269
|
-
if (!config.ticks) return;
|
|
270
|
-
|
|
271
|
-
const numSteps = Math.floor((state.max - state.min) / state.step);
|
|
272
|
-
const tickValues = [];
|
|
273
|
-
|
|
274
|
-
// Generate tick values
|
|
275
|
-
for (let i = 0; i <= numSteps; i++) {
|
|
276
|
-
const value = state.min + (i * state.step);
|
|
277
|
-
if (value <= state.max) tickValues.push(value);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Ensure max value is included
|
|
281
|
-
if (tickValues[tickValues.length - 1] !== state.max) {
|
|
282
|
-
tickValues.push(state.max);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// CSS classes
|
|
286
|
-
const tickClass = state.component.getClass('slider-tick');
|
|
287
|
-
const activeClass = `${tickClass}--active`;
|
|
288
|
-
const inactiveClass = `${tickClass}--inactive`;
|
|
289
|
-
const hiddenClass = `${tickClass}--hidden`;
|
|
290
|
-
|
|
291
|
-
// Create tick elements
|
|
292
|
-
tickValues.forEach(value => {
|
|
293
|
-
const percent = getPercentage(value);
|
|
294
|
-
const tick = document.createElement('div');
|
|
295
|
-
tick.classList.add(tickClass);
|
|
296
|
-
tick.style.left = `${percent}%`;
|
|
297
|
-
|
|
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);
|
|
307
|
-
|
|
308
|
-
tick.classList.add(value >= lowerValue && value <= higherValue ? activeClass : inactiveClass);
|
|
309
|
-
} else {
|
|
310
|
-
tick.classList.add(value <= state.value ? activeClass : inactiveClass);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
ticksContainer.appendChild(tick);
|
|
314
|
-
state.ticks.push(tick);
|
|
315
|
-
});
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Updates active state of tick marks
|
|
320
|
-
*/
|
|
321
|
-
const updateTicks = () => {
|
|
322
|
-
if (!state.ticks || state.ticks.length === 0) return;
|
|
323
|
-
|
|
324
|
-
const tickClass = state.component.getClass('slider-tick');
|
|
325
|
-
const activeClass = `${tickClass}--active`;
|
|
326
|
-
const inactiveClass = `${tickClass}--inactive`;
|
|
327
|
-
const hiddenClass = `${tickClass}--hidden`;
|
|
328
|
-
|
|
329
|
-
state.ticks.forEach((tick, index) => {
|
|
330
|
-
// Calculate the value for this tick
|
|
331
|
-
const tickValue = state.min + (index * state.step);
|
|
332
|
-
|
|
333
|
-
// Reset visibility first
|
|
334
|
-
tick.classList.remove(hiddenClass);
|
|
335
|
-
|
|
336
|
-
// Check if this tick should be hidden (matches exactly a selected value)
|
|
337
|
-
const isExactlySelected = (tickValue === state.value ||
|
|
338
|
-
(config.range && state.secondValue !== null && tickValue === state.secondValue));
|
|
339
|
-
|
|
340
|
-
if (isExactlySelected) {
|
|
341
|
-
tick.classList.add(hiddenClass);
|
|
342
|
-
} else if (config.range && state.secondValue !== null) {
|
|
343
|
-
// Range slider - ticks between values should be active
|
|
344
|
-
const lowerValue = Math.min(state.value, state.secondValue);
|
|
345
|
-
const higherValue = Math.max(state.value, state.secondValue);
|
|
346
|
-
|
|
347
|
-
if (tickValue >= lowerValue && tickValue <= higherValue) {
|
|
348
|
-
tick.classList.add(activeClass);
|
|
349
|
-
tick.classList.remove(inactiveClass);
|
|
350
|
-
} else {
|
|
351
|
-
tick.classList.remove(activeClass);
|
|
352
|
-
tick.classList.add(inactiveClass);
|
|
353
|
-
}
|
|
354
|
-
} else {
|
|
355
|
-
// Single slider - ticks below value should be active
|
|
356
|
-
if (tickValue <= state.value) {
|
|
357
|
-
tick.classList.add(activeClass);
|
|
358
|
-
tick.classList.remove(inactiveClass);
|
|
359
|
-
} else {
|
|
360
|
-
tick.classList.remove(activeClass);
|
|
361
|
-
tick.classList.add(inactiveClass);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Updates all UI elements
|
|
369
|
-
*/
|
|
370
|
-
const updateUi = () => {
|
|
371
|
-
updateHandlePositions();
|
|
372
|
-
updateTrackSegments();
|
|
373
|
-
updateValueBubbles();
|
|
374
|
-
updateTicks();
|
|
375
|
-
};
|
|
376
|
-
|
|
377
|
-
// Return consolidated helper methods
|
|
378
|
-
return {
|
|
379
|
-
getPercentage,
|
|
380
|
-
getValueFromPosition,
|
|
381
|
-
roundToStep,
|
|
382
|
-
clamp,
|
|
383
|
-
showValueBubble,
|
|
384
|
-
generateTicks,
|
|
385
|
-
updateTicks,
|
|
386
|
-
updateUi
|
|
387
|
-
};
|
|
388
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
// src/components/switch/constants.ts
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Label position options
|
|
5
|
-
*/
|
|
6
|
-
export const SWITCH_LABEL_POSITION = {
|
|
7
|
-
START: 'start',
|
|
8
|
-
END: 'end'
|
|
9
|
-
} as const;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Validation schema for switch configuration
|
|
13
|
-
*/
|
|
14
|
-
export const SWITCH_SCHEMA = {
|
|
15
|
-
type: 'object',
|
|
16
|
-
properties: {
|
|
17
|
-
name: {
|
|
18
|
-
type: 'string',
|
|
19
|
-
optional: true
|
|
20
|
-
},
|
|
21
|
-
checked: {
|
|
22
|
-
type: 'boolean',
|
|
23
|
-
optional: true
|
|
24
|
-
},
|
|
25
|
-
required: {
|
|
26
|
-
type: 'boolean',
|
|
27
|
-
optional: true
|
|
28
|
-
},
|
|
29
|
-
disabled: {
|
|
30
|
-
type: 'boolean',
|
|
31
|
-
optional: true
|
|
32
|
-
},
|
|
33
|
-
value: {
|
|
34
|
-
type: 'string',
|
|
35
|
-
optional: true
|
|
36
|
-
},
|
|
37
|
-
label: {
|
|
38
|
-
type: 'string',
|
|
39
|
-
optional: true
|
|
40
|
-
},
|
|
41
|
-
labelPosition: {
|
|
42
|
-
type: 'string',
|
|
43
|
-
enum: Object.values(SWITCH_LABEL_POSITION),
|
|
44
|
-
optional: true
|
|
45
|
-
},
|
|
46
|
-
icon: {
|
|
47
|
-
type: 'string',
|
|
48
|
-
optional: true
|
|
49
|
-
},
|
|
50
|
-
ariaLabel: {
|
|
51
|
-
type: 'string',
|
|
52
|
-
optional: true
|
|
53
|
-
},
|
|
54
|
-
class: {
|
|
55
|
-
type: 'string',
|
|
56
|
-
optional: true
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
} as const;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Switch state classes
|
|
63
|
-
*/
|
|
64
|
-
export const SWITCH_STATES = {
|
|
65
|
-
CHECKED: 'checked',
|
|
66
|
-
DISABLED: 'disabled',
|
|
67
|
-
FOCUSED: 'focused'
|
|
68
|
-
} as const;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Switch element classes
|
|
72
|
-
*/
|
|
73
|
-
export const SWITCH_CLASSES = {
|
|
74
|
-
ROOT: 'switch',
|
|
75
|
-
INPUT: 'switch-input',
|
|
76
|
-
TRACK: 'switch-track',
|
|
77
|
-
THUMB: 'thumb',
|
|
78
|
-
THUMB_ICON: 'thumb-icon',
|
|
79
|
-
LABEL: 'switch-label'
|
|
80
|
-
} as const;
|