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.
Files changed (190) hide show
  1. package/index.ts +2 -0
  2. package/package.json +14 -3
  3. package/src/components/badge/api.ts +23 -14
  4. package/src/components/badge/badge.ts +2 -2
  5. package/src/components/badge/config.ts +10 -11
  6. package/src/components/badge/features.ts +15 -10
  7. package/src/components/badge/index.ts +27 -2
  8. package/src/components/badge/types.ts +28 -8
  9. package/src/components/bottom-app-bar/bottom-app-bar.ts +2 -44
  10. package/src/components/bottom-app-bar/config.ts +1 -45
  11. package/src/components/bottom-app-bar/index.ts +7 -1
  12. package/src/components/bottom-app-bar/types.ts +7 -1
  13. package/src/components/button/button.ts +0 -1
  14. package/src/components/button/config.ts +1 -2
  15. package/src/components/button/index.ts +10 -2
  16. package/src/components/button/types.ts +14 -2
  17. package/src/components/card/config.ts +17 -9
  18. package/src/components/card/content.ts +8 -10
  19. package/src/components/card/features.ts +4 -6
  20. package/src/components/card/index.ts +29 -2
  21. package/src/components/card/types.ts +6 -23
  22. package/src/components/checkbox/config.ts +3 -4
  23. package/src/components/checkbox/index.ts +1 -2
  24. package/src/components/checkbox/types.ts +12 -3
  25. package/src/components/chip/api.ts +170 -221
  26. package/src/components/chip/chip.ts +34 -302
  27. package/src/components/chip/config.ts +1 -2
  28. package/src/components/chip/index.ts +10 -2
  29. package/src/components/chip/types.ts +224 -35
  30. package/src/components/datepicker/api.ts +18 -25
  31. package/src/components/datepicker/config.ts +9 -12
  32. package/src/components/datepicker/datepicker.ts +7 -12
  33. package/src/components/datepicker/index.ts +10 -7
  34. package/src/components/datepicker/render.ts +16 -18
  35. package/src/components/datepicker/types.ts +164 -35
  36. package/src/components/datepicker/utils.ts +1 -2
  37. package/src/components/dialog/api.ts +7 -8
  38. package/src/components/dialog/config.ts +3 -4
  39. package/src/components/dialog/features.ts +56 -22
  40. package/src/components/dialog/index.ts +38 -8
  41. package/src/components/dialog/types.ts +33 -10
  42. package/src/components/divider/index.ts +5 -1
  43. package/src/components/extended-fab/config.ts +6 -2
  44. package/src/components/extended-fab/index.ts +7 -2
  45. package/src/components/extended-fab/types.ts +21 -4
  46. package/src/components/fab/config.ts +3 -4
  47. package/src/components/fab/fab.ts +1 -1
  48. package/src/components/fab/index.ts +7 -2
  49. package/src/components/fab/types.ts +21 -4
  50. package/src/components/list/config.ts +4 -5
  51. package/src/components/list/features.ts +6 -7
  52. package/src/components/list/index.ts +7 -9
  53. package/src/components/list/list-item.ts +12 -13
  54. package/src/components/list/types.ts +50 -5
  55. package/src/components/list/utils.ts +30 -3
  56. package/src/components/menu/features/items-manager.ts +9 -9
  57. package/src/components/menu/features/positioning.ts +7 -7
  58. package/src/components/menu/features/visibility.ts +7 -7
  59. package/src/components/menu/index.ts +7 -9
  60. package/src/components/menu/menu-item.ts +6 -6
  61. package/src/components/menu/menu.ts +22 -0
  62. package/src/components/menu/types.ts +29 -10
  63. package/src/components/menu/utils.ts +67 -0
  64. package/src/components/navigation/api.ts +131 -96
  65. package/src/components/navigation/config.ts +22 -10
  66. package/src/components/navigation/features/controller.ts +273 -0
  67. package/src/components/navigation/features/items.ts +160 -87
  68. package/src/components/navigation/index.ts +0 -6
  69. package/src/components/navigation/nav-item.ts +12 -24
  70. package/src/components/navigation/navigation.ts +21 -8
  71. package/src/components/navigation/system-types.ts +124 -0
  72. package/src/components/navigation/system.ts +776 -0
  73. package/src/components/navigation/types.ts +228 -203
  74. package/src/components/progress/api.ts +2 -3
  75. package/src/components/progress/config.ts +2 -3
  76. package/src/components/progress/index.ts +0 -1
  77. package/src/components/progress/progress.ts +1 -2
  78. package/src/components/progress/types.ts +186 -33
  79. package/src/components/radios/config.ts +1 -1
  80. package/src/components/radios/index.ts +0 -1
  81. package/src/components/radios/types.ts +0 -7
  82. package/src/components/search/config.ts +1 -2
  83. package/src/components/search/features/search.ts +14 -15
  84. package/src/components/search/features/states.ts +5 -1
  85. package/src/components/search/features/structure.ts +3 -4
  86. package/src/components/search/index.ts +0 -3
  87. package/src/components/search/types.ts +18 -6
  88. package/src/components/segmented-button/config.ts +20 -7
  89. package/src/components/segmented-button/segment.ts +6 -7
  90. package/src/components/segmented-button/segmented-button.ts +4 -5
  91. package/src/components/segmented-button/types.ts +37 -2
  92. package/src/components/slider/config.ts +20 -2
  93. package/src/components/slider/features/controller.ts +761 -0
  94. package/src/components/slider/features/handlers.ts +18 -15
  95. package/src/components/slider/features/index.ts +3 -2
  96. package/src/components/slider/features/range.ts +104 -0
  97. package/src/components/slider/slider.ts +34 -14
  98. package/src/components/slider/structure.ts +152 -0
  99. package/src/components/slider/types.ts +34 -8
  100. package/src/components/snackbar/config.ts +2 -3
  101. package/src/components/snackbar/constants.ts +0 -32
  102. package/src/components/snackbar/index.ts +0 -1
  103. package/src/components/snackbar/position.ts +9 -1
  104. package/src/components/snackbar/types.ts +122 -46
  105. package/src/components/switch/config.ts +2 -3
  106. package/src/components/switch/index.ts +0 -1
  107. package/src/components/switch/types.ts +3 -2
  108. package/src/components/tabs/config.ts +3 -4
  109. package/src/components/tabs/index.ts +0 -15
  110. package/src/components/tabs/tab-api.ts +12 -4
  111. package/src/components/tabs/tab.ts +18 -6
  112. package/src/components/tabs/types.ts +13 -3
  113. package/src/components/textfield/api.ts +53 -0
  114. package/src/components/textfield/config.ts +2 -3
  115. package/src/components/textfield/features.ts +322 -0
  116. package/src/components/textfield/index.ts +0 -1
  117. package/src/components/textfield/textfield.ts +8 -0
  118. package/src/components/textfield/types.ts +29 -6
  119. package/src/components/timepicker/api.ts +1 -1
  120. package/src/components/timepicker/clockdial.ts +2 -5
  121. package/src/components/timepicker/config.ts +102 -4
  122. package/src/components/timepicker/index.ts +1 -6
  123. package/src/components/timepicker/render.ts +1 -1
  124. package/src/components/timepicker/timepicker.ts +1 -1
  125. package/src/components/tooltip/api.ts +1 -1
  126. package/src/components/tooltip/config.ts +27 -6
  127. package/src/components/tooltip/index.ts +0 -1
  128. package/src/components/tooltip/types.ts +13 -3
  129. package/src/core/compose/features/textinput.ts +15 -2
  130. package/src/core/compose/features/textlabel.ts +0 -3
  131. package/src/core/composition/features/dom.ts +33 -0
  132. package/src/core/composition/features/icon.ts +131 -0
  133. package/src/core/composition/features/index.ts +11 -0
  134. package/src/core/composition/features/label.ts +156 -0
  135. package/src/core/composition/features/structure.ts +22 -0
  136. package/src/core/composition/index.ts +26 -0
  137. package/src/core/index.ts +1 -1
  138. package/src/core/structure.ts +288 -0
  139. package/src/index.ts +1 -0
  140. package/src/styles/components/_navigation-mobile.scss +244 -0
  141. package/src/styles/components/_navigation-system.scss +151 -0
  142. package/src/{components/tabs/_styles.scss → styles/components/_tabs.scss} +1 -1
  143. package/src/{components/textfield/_styles.scss → styles/components/_textfield.scss} +314 -72
  144. package/src/styles/main.scss +98 -49
  145. package/src/components/badge/constants.ts +0 -40
  146. package/src/components/button/constants.ts +0 -11
  147. package/src/components/card/constants.ts +0 -84
  148. package/src/components/datepicker/constants.ts +0 -98
  149. package/src/components/dialog/constants.ts +0 -32
  150. package/src/components/extended-fab/constants.ts +0 -36
  151. package/src/components/fab/constants.ts +0 -41
  152. package/src/components/menu/constants.ts +0 -154
  153. package/src/components/navigation/constants.ts +0 -200
  154. package/src/components/progress/constants.ts +0 -29
  155. package/src/components/search/constants.ts +0 -21
  156. package/src/components/segmented-button/constants.ts +0 -42
  157. package/src/components/slider/features/slider.ts +0 -318
  158. package/src/components/slider/features/structure.ts +0 -181
  159. package/src/components/slider/features/ui.ts +0 -388
  160. package/src/components/switch/constants.ts +0 -80
  161. package/src/components/tabs/constants.ts +0 -89
  162. package/src/components/textfield/constants.ts +0 -100
  163. package/src/components/timepicker/constants.ts +0 -138
  164. /package/src/{components/badge/_styles.scss → styles/components/_badge.scss} +0 -0
  165. /package/src/{components/bottom-app-bar/_styles.scss → styles/components/_bottom-app-bar.scss} +0 -0
  166. /package/src/{components/button/_styles.scss → styles/components/_button.scss} +0 -0
  167. /package/src/{components/card/_styles.scss → styles/components/_card.scss} +0 -0
  168. /package/src/{components/carousel/_styles.scss → styles/components/_carousel.scss} +0 -0
  169. /package/src/{components/checkbox/_styles.scss → styles/components/_checkbox.scss} +0 -0
  170. /package/src/{components/chip/_styles.scss → styles/components/_chip.scss} +0 -0
  171. /package/src/{components/datepicker/_styles.scss → styles/components/_datepicker.scss} +0 -0
  172. /package/src/{components/dialog/_styles.scss → styles/components/_dialog.scss} +0 -0
  173. /package/src/{components/divider/_styles.scss → styles/components/_divider.scss} +0 -0
  174. /package/src/{components/extended-fab/_styles.scss → styles/components/_extended-fab.scss} +0 -0
  175. /package/src/{components/fab/_styles.scss → styles/components/_fab.scss} +0 -0
  176. /package/src/{components/list/_styles.scss → styles/components/_list.scss} +0 -0
  177. /package/src/{components/menu/_styles.scss → styles/components/_menu.scss} +0 -0
  178. /package/src/{components/navigation/_styles.scss → styles/components/_navigation.scss} +0 -0
  179. /package/src/{components/progress/_styles.scss → styles/components/_progress.scss} +0 -0
  180. /package/src/{components/radios/_styles.scss → styles/components/_radios.scss} +0 -0
  181. /package/src/{components/search/_styles.scss → styles/components/_search.scss} +0 -0
  182. /package/src/{components/segmented-button/_styles.scss → styles/components/_segmented-button.scss} +0 -0
  183. /package/src/{components/sheet/_styles.scss → styles/components/_sheet.scss} +0 -0
  184. /package/src/{components/slider/_styles.scss → styles/components/_slider.scss} +0 -0
  185. /package/src/{components/snackbar/_styles.scss → styles/components/_snackbar.scss} +0 -0
  186. /package/src/{components/switch/_styles.scss → styles/components/_switch.scss} +0 -0
  187. /package/src/{components/timepicker/_styles.scss → styles/components/_timepicker.scss} +0 -0
  188. /package/src/{components/tooltip/_styles.scss → styles/components/_tooltip.scss} +0 -0
  189. /package/src/{components/top-app-bar/_styles.scss → styles/components/_top-app-bar.scss} +0 -0
  190. /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;