mtrl 0.8.0-next.36 → 0.8.0-next.37
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/README.md +11 -0
- package/dist/README.md +11 -0
- package/dist/components/slider/features/controller.js +10 -49
- package/dist/components/slider/features/index.d.ts +1 -1
- package/dist/components/slider/features/index.js +1 -1
- package/dist/components/slider/features/states.js +19 -19
- package/dist/components/slider/features/tracks.d.ts +13 -0
- package/dist/components/slider/features/tracks.js +174 -0
- package/dist/components/slider/slider.d.ts +1 -1
- package/dist/components/slider/slider.js +2 -2
- package/dist/index.cjs +12 -12
- package/dist/package.json +1 -1
- package/dist/styles/badge.css +1 -1
- package/dist/styles/base.css +1 -1
- package/dist/styles/bottom-app-bar.css +1 -1
- package/dist/styles/bottom-sheet.css +1 -1
- package/dist/styles/button-group.css +1 -1
- package/dist/styles/button.css +1 -1
- package/dist/styles/card.css +1 -1
- package/dist/styles/carousel.css +1 -1
- package/dist/styles/checkbox.css +1 -1
- package/dist/styles/chips.css +1 -1
- package/dist/styles/dialog.css +1 -1
- package/dist/styles/divider.css +1 -1
- package/dist/styles/drawer.css +1 -1
- package/dist/styles/extended-fab.css +1 -1
- package/dist/styles/fab.css +1 -1
- package/dist/styles/icon-button.css +1 -1
- package/dist/styles/list.css +1 -1
- package/dist/styles/loading-indicator.css +1 -1
- package/dist/styles/menu.css +1 -1
- package/dist/styles/navigation.css +1 -1
- package/dist/styles/progress.css +1 -1
- package/dist/styles/radios.css +1 -1
- package/dist/styles/search.css +1 -1
- package/dist/styles/segmented-button.css +1 -1
- package/dist/styles/select.css +1 -1
- package/dist/styles/side-sheet.css +1 -1
- package/dist/styles/slider.css +2 -2
- package/dist/styles/snackbar.css +1 -1
- package/dist/styles/split-button.css +1 -1
- package/dist/styles/switch.css +1 -1
- package/dist/styles/tabs.css +1 -1
- package/dist/styles/textfield.css +1 -1
- package/dist/styles/timepicker.css +1 -1
- package/dist/styles/top-app-bar.css +1 -1
- package/dist/styles/utilities.css +1 -1
- package/dist/styles.css +2 -2
- package/dist/themes/autumn.css +1 -1
- package/dist/themes/baseline.css +1 -1
- package/dist/themes/brownbeige.css +1 -1
- package/dist/themes/browngreen.css +1 -1
- package/dist/themes/desert.css +1 -1
- package/dist/themes/forest.css +1 -1
- package/dist/themes/highcontrast.css +1 -1
- package/dist/themes/legacy.css +1 -1
- package/dist/themes/material.css +1 -1
- package/dist/themes/ocean.css +1 -1
- package/dist/themes/sageivory.css +1 -1
- package/dist/themes/spring.css +1 -1
- package/dist/themes/summer.css +1 -1
- package/dist/themes/sunset.css +1 -1
- package/dist/themes/tealcaramel.css +1 -1
- package/dist/themes/winter.css +1 -1
- package/package.json +2 -1
- package/src/styles/components/_slider.scss +39 -10
- package/dist/components/slider/features/canvas.d.ts +0 -47
- package/dist/components/slider/features/canvas.js +0 -570
package/README.md
CHANGED
|
@@ -137,6 +137,17 @@ Button progress and card action buttons retain their dynamic imports in ESM.
|
|
|
137
137
|
Enable code splitting in your application build to load those features on demand.
|
|
138
138
|
The CommonJS compatibility bundle includes them eagerly.
|
|
139
139
|
|
|
140
|
+
Slider decoration uses DOM tracks and CSS backgrounds for ticks; it no longer
|
|
141
|
+
creates a canvas or subscribes to theme changes. Its public slider API and
|
|
142
|
+
accessible handles remain. Themes and per-slider colour overrides apply through
|
|
143
|
+
CSS. Custom styling that targets the former `.mtrl-slider-canvas` element must be
|
|
144
|
+
updated. Run `bun run slider:check` after building for slider geometry, keyboard,
|
|
145
|
+
pointer, resize, and lifecycle checks. For comparison with a previous canvas
|
|
146
|
+
build, pass `--reference=/path/to/reference` to `scripts/check-slider.ts`; that
|
|
147
|
+
directory must contain an ESM `slider.js` exporting `createSlider` and its full
|
|
148
|
+
`styles.css`. Comparison screenshots and measurements go to `analysis/slider-dom`.
|
|
149
|
+
CSS rounded corners differ slightly from the previous canvas curves.
|
|
150
|
+
|
|
140
151
|
### Checking distribution size
|
|
141
152
|
|
|
142
153
|
```bash
|
package/dist/README.md
CHANGED
|
@@ -137,6 +137,17 @@ Button progress and card action buttons retain their dynamic imports in ESM.
|
|
|
137
137
|
Enable code splitting in your application build to load those features on demand.
|
|
138
138
|
The CommonJS compatibility bundle includes them eagerly.
|
|
139
139
|
|
|
140
|
+
Slider decoration uses DOM tracks and CSS backgrounds for ticks; it no longer
|
|
141
|
+
creates a canvas or subscribes to theme changes. Its public slider API and
|
|
142
|
+
accessible handles remain. Themes and per-slider colour overrides apply through
|
|
143
|
+
CSS. Custom styling that targets the former `.mtrl-slider-canvas` element must be
|
|
144
|
+
updated. Run `bun run slider:check` after building for slider geometry, keyboard,
|
|
145
|
+
pointer, resize, and lifecycle checks. For comparison with a previous canvas
|
|
146
|
+
build, pass `--reference=/path/to/reference` to `scripts/check-slider.ts`; that
|
|
147
|
+
directory must contain an ESM `slider.js` exporting `createSlider` and its full
|
|
148
|
+
`styles.css`. Comparison screenshots and measurements go to `analysis/slider-dom`.
|
|
149
|
+
CSS rounded corners differ slightly from the previous canvas curves.
|
|
150
|
+
|
|
140
151
|
### Checking distribution size
|
|
141
152
|
|
|
142
153
|
```bash
|
|
@@ -54,41 +54,9 @@ export const withController = (config) => (component) => {
|
|
|
54
54
|
const range = state.max - state.min;
|
|
55
55
|
return range === 0 ? 0 : ((value - state.min) / range) * 100;
|
|
56
56
|
};
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
if (!handle || !container)
|
|
61
|
-
return null;
|
|
62
|
-
try {
|
|
63
|
-
const handleRect = handle.getBoundingClientRect();
|
|
64
|
-
const containerRect = container.getBoundingClientRect();
|
|
65
|
-
const handleSize = handleRect.width || SLIDER_MEASUREMENTS.HANDLE_SIZE;
|
|
66
|
-
const trackSize = containerRect.width;
|
|
67
|
-
const edgeConstraint = (SLIDER_MEASUREMENTS.EDGE_PADDING / trackSize) * 100;
|
|
68
|
-
const paddingPixels = state.activeHandle ? 6 : 8;
|
|
69
|
-
const paddingPercent = (paddingPixels / trackSize) * 100;
|
|
70
|
-
return { handleSize, trackSize, edgeConstraint, paddingPercent };
|
|
71
|
-
}
|
|
72
|
-
catch (error) {
|
|
73
|
-
console.warn("Error calculating track dimensions:", error);
|
|
74
|
-
return {
|
|
75
|
-
handleSize: SLIDER_MEASUREMENTS.HANDLE_SIZE,
|
|
76
|
-
trackSize: 200,
|
|
77
|
-
edgeConstraint: 3,
|
|
78
|
-
paddingPercent: 4,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
const mapValueToVisualPercent = (valuePercent, trackSize = 200) => {
|
|
83
|
-
const edgeConstraint = (SLIDER_MEASUREMENTS.EDGE_PADDING / trackSize) * 100;
|
|
84
|
-
const minEdge = edgeConstraint;
|
|
85
|
-
const maxEdge = 100 - edgeConstraint;
|
|
86
|
-
const visualRange = maxEdge - minEdge;
|
|
87
|
-
if (valuePercent <= 0)
|
|
88
|
-
return minEdge;
|
|
89
|
-
if (valuePercent >= 100)
|
|
90
|
-
return maxEdge;
|
|
91
|
-
return minEdge + (valuePercent / 100) * visualRange;
|
|
57
|
+
const visualPosition = (percent) => {
|
|
58
|
+
const ratio = Math.min(1, Math.max(0, percent / 100));
|
|
59
|
+
return `calc(${ratio * 100}% + ${SLIDER_MEASUREMENTS.EDGE_PADDING * (1 - 2 * ratio)}px)`;
|
|
92
60
|
};
|
|
93
61
|
const getValueFromPosition = (position) => {
|
|
94
62
|
const components = getComponents();
|
|
@@ -123,25 +91,17 @@ export const withController = (config) => (component) => {
|
|
|
123
91
|
const { handle, container, valueBubble, secondHandle, secondValueBubble } = components;
|
|
124
92
|
if (!handle || !container)
|
|
125
93
|
return;
|
|
126
|
-
|
|
127
|
-
if (!dims)
|
|
128
|
-
return;
|
|
129
|
-
const { trackSize } = dims;
|
|
130
|
-
const percent = getPercentage(state.value);
|
|
131
|
-
const adjustedPercent = mapValueToVisualPercent(percent, trackSize);
|
|
132
|
-
handle.style.left = `${adjustedPercent}%`;
|
|
94
|
+
handle.style.left = visualPosition(getPercentage(state.value));
|
|
133
95
|
handle.style.transform = "translate(-50%, -50%)";
|
|
134
96
|
if (valueBubble) {
|
|
135
|
-
valueBubble.style.left =
|
|
97
|
+
valueBubble.style.left = visualPosition(getPercentage(state.value));
|
|
136
98
|
valueBubble.style.transform = "translateX(-50%)";
|
|
137
99
|
}
|
|
138
100
|
if (config.range && secondHandle && state.secondValue !== null) {
|
|
139
|
-
|
|
140
|
-
const adjustedSecondPercent = mapValueToVisualPercent(secondPercent, trackSize);
|
|
141
|
-
secondHandle.style.left = `${adjustedSecondPercent}%`;
|
|
101
|
+
secondHandle.style.left = visualPosition(getPercentage(state.secondValue));
|
|
142
102
|
secondHandle.style.transform = "translate(-50%, -50%)";
|
|
143
103
|
if (secondValueBubble) {
|
|
144
|
-
secondValueBubble.style.left =
|
|
104
|
+
secondValueBubble.style.left = visualPosition(getPercentage(state.secondValue));
|
|
145
105
|
secondValueBubble.style.transform = "translateX(-50%)";
|
|
146
106
|
}
|
|
147
107
|
}
|
|
@@ -174,8 +134,8 @@ export const withController = (config) => (component) => {
|
|
|
174
134
|
try {
|
|
175
135
|
updateHandlePositions();
|
|
176
136
|
updateValueBubbles();
|
|
177
|
-
if (component.
|
|
178
|
-
component.
|
|
137
|
+
if (component.renderTracks) {
|
|
138
|
+
component.renderTracks(state);
|
|
179
139
|
}
|
|
180
140
|
}
|
|
181
141
|
catch (error) {
|
|
@@ -307,6 +267,7 @@ export const withController = (config) => (component) => {
|
|
|
307
267
|
},
|
|
308
268
|
setStep(step) {
|
|
309
269
|
state.step = step;
|
|
270
|
+
render();
|
|
310
271
|
component.element.classList[step > 0 ? "add" : "remove"](`${component.getClass("slider")}--discrete`);
|
|
311
272
|
if (config.ticks || config.tickLabels) {
|
|
312
273
|
generateTicks();
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { SLIDER_COLORS } from "../types.js";
|
|
2
2
|
import { SLIDER_SIZES } from "../constants.js";
|
|
3
3
|
export const withStates = (config) => (component) => {
|
|
4
|
+
const handle = component.handle ?? component.structure?.handle;
|
|
5
|
+
const secondHandle = component.secondHandle ?? component.structure?.secondHandle;
|
|
4
6
|
const isDisabled = config.disabled === true;
|
|
5
7
|
let initialization = null;
|
|
6
8
|
if (isDisabled) {
|
|
@@ -11,31 +13,31 @@ export const withStates = (config) => (component) => {
|
|
|
11
13
|
function disableComponent() {
|
|
12
14
|
component.element.classList.add(`${component.getClass("slider")}--disabled`);
|
|
13
15
|
component.element.setAttribute("aria-disabled", "true");
|
|
14
|
-
if (
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
if (handle) {
|
|
17
|
+
handle.tabIndex = -1;
|
|
18
|
+
handle.setAttribute("aria-disabled", "true");
|
|
17
19
|
}
|
|
18
|
-
if (config.range &&
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
if (config.range && secondHandle) {
|
|
21
|
+
secondHandle.tabIndex = -1;
|
|
22
|
+
secondHandle.setAttribute("aria-disabled", "true");
|
|
21
23
|
}
|
|
22
|
-
if (component.
|
|
23
|
-
component.
|
|
24
|
+
if (component.renderTracks) {
|
|
25
|
+
component.renderTracks();
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
function enableComponent() {
|
|
27
29
|
component.element.classList.remove(`${component.getClass("slider")}--disabled`);
|
|
28
30
|
component.element.setAttribute("aria-disabled", "false");
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
if (handle) {
|
|
32
|
+
handle.tabIndex = 0;
|
|
33
|
+
handle.setAttribute("aria-disabled", "false");
|
|
32
34
|
}
|
|
33
|
-
if (config.range &&
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
if (config.range && secondHandle) {
|
|
36
|
+
secondHandle.tabIndex = 0;
|
|
37
|
+
secondHandle.setAttribute("aria-disabled", "false");
|
|
36
38
|
}
|
|
37
|
-
if (component.
|
|
38
|
-
component.
|
|
39
|
+
if (component.renderTracks) {
|
|
40
|
+
component.renderTracks();
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
function getActiveColor() {
|
|
@@ -105,9 +107,7 @@ export const withStates = (config) => (component) => {
|
|
|
105
107
|
},
|
|
106
108
|
showTicks(show) {
|
|
107
109
|
config.ticks = show;
|
|
108
|
-
|
|
109
|
-
component.slider.regenerateTicks();
|
|
110
|
-
}
|
|
110
|
+
component.renderTracks?.();
|
|
111
111
|
return this;
|
|
112
112
|
},
|
|
113
113
|
showCurrentValue(show) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SliderConfig } from "../types.js";
|
|
2
|
+
import { SliderSize } from "../constants.js";
|
|
3
|
+
export declare const getTrackHeight: (size?: SliderSize) => number;
|
|
4
|
+
/**
|
|
5
|
+
* Gets the handle height based on the slider size
|
|
6
|
+
*/
|
|
7
|
+
export declare const getHandleHeight: (size?: SliderSize) => number;
|
|
8
|
+
/**
|
|
9
|
+
* Gets the external track radius based on the slider size
|
|
10
|
+
*/
|
|
11
|
+
export declare const getExternalTrackRadius: (size?: SliderSize) => number;
|
|
12
|
+
/** Decorative tracks and ticks. Handles and controller retain interaction ownership. */
|
|
13
|
+
export declare const withTracks: (config: SliderConfig) => (component: any) => any;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { SLIDER_SIZES, SLIDER_MEASUREMENTS } from "../constants.js";
|
|
2
|
+
export const getTrackHeight = (size) => {
|
|
3
|
+
if (typeof size === "number") {
|
|
4
|
+
return Math.max(size, SLIDER_SIZES.XS);
|
|
5
|
+
}
|
|
6
|
+
if (typeof size === "string" && size in SLIDER_SIZES) {
|
|
7
|
+
return SLIDER_SIZES[size];
|
|
8
|
+
}
|
|
9
|
+
return SLIDER_SIZES.XS;
|
|
10
|
+
};
|
|
11
|
+
export const getHandleHeight = (size) => {
|
|
12
|
+
const trackHeight = getTrackHeight(size);
|
|
13
|
+
if (trackHeight <= SLIDER_SIZES.S) {
|
|
14
|
+
return SLIDER_MEASUREMENTS.SMALL_HANDLE_HEIGHT;
|
|
15
|
+
}
|
|
16
|
+
return trackHeight + SLIDER_MEASUREMENTS.HANDLE_HEIGHT_OFFSET;
|
|
17
|
+
};
|
|
18
|
+
export const getExternalTrackRadius = (size) => {
|
|
19
|
+
const trackHeight = getTrackHeight(size);
|
|
20
|
+
if (trackHeight <= SLIDER_SIZES.S) {
|
|
21
|
+
return SLIDER_MEASUREMENTS.SMALL_TRACK_EXTERNAL_RADIUS;
|
|
22
|
+
}
|
|
23
|
+
return trackHeight * SLIDER_MEASUREMENTS.LARGE_TRACK_RADIUS_RATIO;
|
|
24
|
+
};
|
|
25
|
+
export const withTracks = (config) => (component) => {
|
|
26
|
+
const container = component.container;
|
|
27
|
+
const element = (name, parent) => {
|
|
28
|
+
const node = document.createElement("div");
|
|
29
|
+
node.className = component.getClass(`slider-${name}`);
|
|
30
|
+
parent.append(node);
|
|
31
|
+
return node;
|
|
32
|
+
};
|
|
33
|
+
const visual = element("visual", container);
|
|
34
|
+
visual.setAttribute("aria-hidden", "true");
|
|
35
|
+
const track = element("track", visual);
|
|
36
|
+
const segments = Array.from({ length: 3 }, () => element("segment", track));
|
|
37
|
+
const ticks = [element("ticks", visual), element("ticks", visual)];
|
|
38
|
+
ticks[1].classList.add(component.getClass("slider-ticks--active"));
|
|
39
|
+
const dot = element("dot", visual);
|
|
40
|
+
let destroyed = false;
|
|
41
|
+
let size = config.size ?? "XS";
|
|
42
|
+
let state = {
|
|
43
|
+
value: config.value ?? 0, secondValue: config.secondValue ?? null,
|
|
44
|
+
min: config.min ?? 0, max: config.max ?? 100, step: config.step ?? 1,
|
|
45
|
+
};
|
|
46
|
+
const render = (next) => {
|
|
47
|
+
if (destroyed)
|
|
48
|
+
return;
|
|
49
|
+
if (next)
|
|
50
|
+
state = { ...next };
|
|
51
|
+
const width = container.getBoundingClientRect().width || 200;
|
|
52
|
+
const height = getTrackHeight(size);
|
|
53
|
+
const edge = SLIDER_MEASUREMENTS.EDGE_PADDING;
|
|
54
|
+
const position = (value) => edge + Math.min(1, Math.max(0, state.max === state.min ? 0 : (value - state.min) / (state.max - state.min))) * Math.max(0, width - 2 * edge);
|
|
55
|
+
const first = position(state.value);
|
|
56
|
+
const second = position(state.secondValue ?? state.max);
|
|
57
|
+
const zero = position(0);
|
|
58
|
+
const gap = SLIDER_MEASUREMENTS.HANDLE_GAP;
|
|
59
|
+
const reduction = SLIDER_MEASUREMENTS.HANDLE_GAP_PRESSED_REDUCTION;
|
|
60
|
+
const firstGap = gap - (state.pressed && (!config.range || state.activeHandle === "first") ? reduction : 0);
|
|
61
|
+
const secondGap = gap - (state.pressed && state.activeHandle === "second" ? reduction : 0);
|
|
62
|
+
const parts = [];
|
|
63
|
+
let activeStart = 0, activeEnd = first;
|
|
64
|
+
if (config.centered) {
|
|
65
|
+
activeStart = Math.min(zero, first);
|
|
66
|
+
activeEnd = Math.max(zero, first);
|
|
67
|
+
const centerGap = SLIDER_MEASUREMENTS.CENTER_GAP / 2;
|
|
68
|
+
if (Math.abs(first - zero) < firstGap) {
|
|
69
|
+
parts.push([0, first - firstGap, false], [zero, zero, true], [first + firstGap, width, false]);
|
|
70
|
+
}
|
|
71
|
+
else if (state.value >= 0) {
|
|
72
|
+
parts.push([0, zero - centerGap, false], [zero + centerGap, first - firstGap, true], [first + firstGap, width, false]);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
parts.push([0, first - firstGap, false], [first + firstGap, zero - centerGap, true], [zero + centerGap, width, false]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (config.range && state.secondValue !== null) {
|
|
79
|
+
activeStart = Math.min(first, second);
|
|
80
|
+
activeEnd = Math.max(first, second);
|
|
81
|
+
const lowGap = first <= second ? firstGap : secondGap;
|
|
82
|
+
const highGap = first <= second ? secondGap : firstGap;
|
|
83
|
+
const start = activeStart + lowGap, end = activeEnd - highGap;
|
|
84
|
+
parts.push([0, activeStart - lowGap, false], [start, end - start > height ? end : start, true], [activeEnd + highGap, width, false]);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
parts.push([0, 0, false], [0, first - firstGap, true], [first + firstGap, width, false]);
|
|
88
|
+
}
|
|
89
|
+
segments.forEach((segment, index) => {
|
|
90
|
+
const [start, end, active] = parts[index] ?? [0, 0, false];
|
|
91
|
+
segment.style.left = `${start}px`;
|
|
92
|
+
segment.style.width = `${Math.max(0, end - start)}px`;
|
|
93
|
+
segment.classList.toggle(component.getClass("slider-segment--active"), active);
|
|
94
|
+
});
|
|
95
|
+
const discrete = !!config.ticks && state.step > 0 && state.max > state.min;
|
|
96
|
+
ticks.forEach(tick => { tick.hidden = !discrete; });
|
|
97
|
+
dot.hidden = !!config.ticks;
|
|
98
|
+
if (discrete) {
|
|
99
|
+
const spacing = (width - 2 * edge) * state.step / (state.max - state.min);
|
|
100
|
+
const last = position(state.min + Math.floor((state.max - state.min) / state.step) * state.step);
|
|
101
|
+
const holes = [state.value, ...(config.range && state.secondValue !== null ? [state.secondValue] : [])]
|
|
102
|
+
.filter(value => Math.abs((value - state.min) / state.step - Math.round((value - state.min) / state.step)) < 0.1)
|
|
103
|
+
.map(value => position(state.min + Math.round((value - state.min) / state.step) * state.step))
|
|
104
|
+
.sort((a, b) => a - b);
|
|
105
|
+
const mask = (intervals) => {
|
|
106
|
+
const merged = [];
|
|
107
|
+
for (const [start, end] of intervals.sort((a, b) => a[0] - b[0])) {
|
|
108
|
+
const previous = merged[merged.length - 1];
|
|
109
|
+
if (previous && start <= previous[1])
|
|
110
|
+
previous[1] = Math.max(previous[1], end);
|
|
111
|
+
else
|
|
112
|
+
merged.push([start, end]);
|
|
113
|
+
}
|
|
114
|
+
const stops = merged.flatMap(([start, end]) => [`black ${start}px`, `transparent ${start}px`, `transparent ${end}px`, `black ${end}px`]);
|
|
115
|
+
return stops.length ? `linear-gradient(to right, black 0px, ${stops.join(",")}, black 100%)` : "none";
|
|
116
|
+
};
|
|
117
|
+
ticks.forEach((tick, index) => {
|
|
118
|
+
tick.style.backgroundSize = `${Math.max(spacing, 0.01)}px 100%`;
|
|
119
|
+
tick.style.backgroundPosition = `${edge - spacing / 2}px center`;
|
|
120
|
+
tick.style.maskImage = mask([
|
|
121
|
+
...holes.map(x => [x - 2, x + 2]),
|
|
122
|
+
...(index === 0 ? [[activeStart - 2, activeEnd + 2]] : []),
|
|
123
|
+
]);
|
|
124
|
+
});
|
|
125
|
+
ticks[0].style.clipPath = `inset(0 ${Math.max(0, width - last - 2)}px 0 0)`;
|
|
126
|
+
ticks[1].style.clipPath = `inset(0 ${Math.max(0, width - Math.min(last + 2, activeEnd + 2))}px 0 ${Math.max(0, activeStart - 2)}px)`;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const setSize = (next) => {
|
|
130
|
+
if (destroyed)
|
|
131
|
+
return;
|
|
132
|
+
size = next;
|
|
133
|
+
const handleHeight = getHandleHeight(size);
|
|
134
|
+
container.style.height = `${Math.max(handleHeight, SLIDER_MEASUREMENTS.MIN_HEIGHT)}px`;
|
|
135
|
+
track.style.height = `${getTrackHeight(size)}px`;
|
|
136
|
+
track.style.borderRadius = `${getExternalTrackRadius(size)}px`;
|
|
137
|
+
for (const handle of [component.handle, component.secondHandle]) {
|
|
138
|
+
if (handle)
|
|
139
|
+
handle.style.height = `${handleHeight}px`;
|
|
140
|
+
}
|
|
141
|
+
render();
|
|
142
|
+
};
|
|
143
|
+
component.renderTracks = render;
|
|
144
|
+
component.setSize = setSize;
|
|
145
|
+
component.getSize = () => size;
|
|
146
|
+
const setColor = component.appearance.setColor;
|
|
147
|
+
component.appearance.setColor = (color) => {
|
|
148
|
+
if (destroyed)
|
|
149
|
+
return;
|
|
150
|
+
setColor.call(component.appearance, color);
|
|
151
|
+
config.color = color;
|
|
152
|
+
};
|
|
153
|
+
setSize(size);
|
|
154
|
+
const resize = () => {
|
|
155
|
+
if (!destroyed)
|
|
156
|
+
render();
|
|
157
|
+
};
|
|
158
|
+
const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(resize) : null;
|
|
159
|
+
if (observer)
|
|
160
|
+
observer.observe(container);
|
|
161
|
+
else
|
|
162
|
+
window.addEventListener("resize", resize);
|
|
163
|
+
const destroy = component.lifecycle.destroy;
|
|
164
|
+
component.lifecycle.destroy = () => {
|
|
165
|
+
if (destroyed)
|
|
166
|
+
return;
|
|
167
|
+
destroyed = true;
|
|
168
|
+
observer?.disconnect();
|
|
169
|
+
if (!observer)
|
|
170
|
+
window.removeEventListener("resize", resize);
|
|
171
|
+
destroy.call(component.lifecycle);
|
|
172
|
+
};
|
|
173
|
+
return component;
|
|
174
|
+
};
|
|
@@ -6,7 +6,7 @@ import { SliderConfig, SliderComponent } from "./types.js";
|
|
|
6
6
|
* 1. Base component - Creates the foundation with event system
|
|
7
7
|
* 2. Feature enhancement - Adds specific capabilities (range, icons)
|
|
8
8
|
* 3. DOM creation - Creates DOM elements directly using optimized createElement
|
|
9
|
-
* 4.
|
|
9
|
+
* 4. Track rendering - Uses decorative DOM and CSS alongside accessible handles
|
|
10
10
|
* 5. State management - Handles visual states and appearance
|
|
11
11
|
* 6. Controller - Manages behavior, events, and UI rendering
|
|
12
12
|
* 7. Lifecycle - Handles component lifecycle events
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { pipe } from "../../core/compose/pipe.js";
|
|
2
2
|
import { createBase, withElement } from "../../core/compose/component.js";
|
|
3
3
|
import { withEvents, withLifecycle, withIcon, withTextLabel, } from "../../core/compose/features/index.js";
|
|
4
|
-
import { withRange, withStates, withController,
|
|
4
|
+
import { withRange, withStates, withController, withTracks, withDom, } from "./features/index.js";
|
|
5
5
|
import { withAPI } from "./api.js";
|
|
6
6
|
import { createBaseConfig, getElementConfig, getApiConfig } from "./config.js";
|
|
7
7
|
const createSlider = (config = {}) => {
|
|
8
8
|
const baseConfig = createBaseConfig(config);
|
|
9
9
|
try {
|
|
10
|
-
const component = pipe(createBase, withEvents(), withElement(getElementConfig(baseConfig)), withRange(baseConfig), withDom(baseConfig), withTextLabel(baseConfig), withIcon(baseConfig), withLifecycle(), withStates(baseConfig),
|
|
10
|
+
const component = pipe(createBase, withEvents(), withElement(getElementConfig(baseConfig)), withRange(baseConfig), withDom(baseConfig), withTextLabel(baseConfig), withIcon(baseConfig), withLifecycle(), withStates(baseConfig), withTracks(baseConfig), withController(baseConfig))(baseConfig);
|
|
11
11
|
const apiOptions = getApiConfig(component);
|
|
12
12
|
const slider = withAPI(apiOptions)(component);
|
|
13
13
|
if (baseConfig.on && typeof slider.on === "function") {
|