@data-slot/slider 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/README.md ADDED
@@ -0,0 +1,156 @@
1
+ # @data-slot/slider
2
+
3
+ Headless slider component for vanilla JavaScript. Supports single value and range sliders with full keyboard navigation and ARIA compliance.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @data-slot/slider
9
+ # or
10
+ npm install @data-slot/slider
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### HTML Structure
16
+
17
+ ```html
18
+ <!-- Single value slider -->
19
+ <div data-slot="slider" data-default-value="50">
20
+ <div class="slider-control">
21
+ <div data-slot="slider-track">
22
+ <div data-slot="slider-range"></div>
23
+ </div>
24
+ <div data-slot="slider-thumb"></div>
25
+ </div>
26
+ </div>
27
+
28
+ <!-- Range slider (two thumbs) -->
29
+ <div data-slot="slider" data-default-value="25,75">
30
+ <div class="slider-control">
31
+ <div data-slot="slider-track">
32
+ <div data-slot="slider-range"></div>
33
+ </div>
34
+ <div data-slot="slider-thumb"></div>
35
+ <div data-slot="slider-thumb"></div>
36
+ </div>
37
+ </div>
38
+ ```
39
+
40
+ ### JavaScript
41
+
42
+ ```javascript
43
+ import { create, createSlider } from "@data-slot/slider";
44
+
45
+ // Auto-discover and bind all [data-slot="slider"] elements
46
+ const controllers = create();
47
+
48
+ // Or target a specific element
49
+ const slider = createSlider(element, {
50
+ defaultValue: 50,
51
+ min: 0,
52
+ max: 100,
53
+ step: 1,
54
+ onValueChange: (value) => console.log("Changed:", value),
55
+ onValueCommit: (value) => console.log("Committed:", value),
56
+ });
57
+
58
+ // Programmatic control
59
+ slider.setValue(75);
60
+ console.log(slider.value); // 75
61
+
62
+ // Cleanup
63
+ slider.destroy();
64
+ ```
65
+
66
+ ## Data Attributes
67
+
68
+ | Attribute | Description | Default |
69
+ |-----------|-------------|---------|
70
+ | `data-default-value` | Initial value (`50` or `25,75` for range) | `min` |
71
+ | `data-min` | Minimum value | `0` |
72
+ | `data-max` | Maximum value | `100` |
73
+ | `data-step` | Step increment | `1` |
74
+ | `data-large-step` | Large step for PageUp/PageDown | `step * 10` |
75
+ | `data-orientation` | `horizontal` or `vertical` | `horizontal` |
76
+ | `data-disabled` | Disable the slider | - |
77
+
78
+ ## Events
79
+
80
+ ### Outbound Events (on root)
81
+
82
+ | Event | Detail | Description |
83
+ |-------|--------|-------------|
84
+ | `slider:change` | `{ value: number \| [number, number] }` | Fires during value changes |
85
+ | `slider:commit` | `{ value: number \| [number, number] }` | Fires when interaction ends |
86
+
87
+ ### Inbound Events (on root)
88
+
89
+ | Event | Detail | Description |
90
+ |-------|--------|-------------|
91
+ | `slider:set` | `{ value: number \| [number, number] }` | Set value programmatically |
92
+
93
+ ```javascript
94
+ // Listen for changes
95
+ root.addEventListener("slider:change", (e) => {
96
+ console.log("Value:", e.detail.value);
97
+ });
98
+
99
+ // Set value from outside
100
+ root.dispatchEvent(new CustomEvent("slider:set", {
101
+ detail: { value: 50 }
102
+ }));
103
+ ```
104
+
105
+ ## Keyboard Navigation
106
+
107
+ | Key | Action |
108
+ |-----|--------|
109
+ | `ArrowRight` / `ArrowUp` | Increase by step |
110
+ | `ArrowLeft` / `ArrowDown` | Decrease by step |
111
+ | `PageUp` | Increase by largeStep |
112
+ | `PageDown` | Decrease by largeStep |
113
+ | `Home` | Set to min |
114
+ | `End` | Set to max |
115
+ | `Shift+Arrow` | Move by largeStep |
116
+
117
+ ## Styling
118
+
119
+ The component sets data attributes and inline styles for CSS hooks:
120
+
121
+ ```css
122
+ /* Root state */
123
+ [data-slot="slider"][data-orientation="horizontal"] { ... }
124
+ [data-slot="slider"][data-orientation="vertical"] { ... }
125
+ [data-slot="slider"][data-disabled] { ... }
126
+ [data-slot="slider"][data-dragging] { ... }
127
+
128
+ /* Thumb positioning (set automatically) */
129
+ [data-slot="slider-thumb"] {
130
+ position: absolute;
131
+ /* left: X% (horizontal) or bottom: X% (vertical) */
132
+ }
133
+
134
+ /* Range positioning (set automatically) */
135
+ [data-slot="slider-range"] {
136
+ position: absolute;
137
+ /* left + width (horizontal) or bottom + height (vertical) */
138
+ }
139
+
140
+ /* Thumb dragging state */
141
+ [data-slot="slider-thumb"][data-dragging] { ... }
142
+ ```
143
+
144
+ ## Accessibility
145
+
146
+ Each thumb element receives:
147
+ - `role="slider"`
148
+ - `tabindex="0"`
149
+ - `aria-valuemin` / `aria-valuemax` / `aria-valuenow`
150
+ - `aria-orientation`
151
+ - `aria-disabled` (when disabled)
152
+ - `aria-label` (from `data-label` or auto-generated for range)
153
+
154
+ ## License
155
+
156
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ const e=(e,t)=>e.querySelector(`[data-slot="${t}"]`),t=(e,t)=>[...e.querySelectorAll(`[data-slot="${t}"]`)],n=(e,t)=>[...e.querySelectorAll(`[data-slot="${t}"]`)],r=new WeakMap;function i(e,t,n){if(typeof process<`u`&&process.env?.NODE_ENV===`production`)return;let i=r.get(e);i||(i=new Set,r.set(e,i)),!i.has(t)&&(i.add(t),console.warn(`[@data-slot] ${n}`))}function a(e){let t=`data-${e.replace(/([A-Z])/g,`-$1`).toLowerCase()}`,n=`data-${e}`;return t===n?[t]:[t,n]}function o(e,t){for(let n of a(t))if(e.hasAttribute(n))return e.getAttribute(n);return null}function s(e,t){return a(t).some(t=>e.hasAttribute(t))}const c=new Set([``,`true`,`1`,`yes`]),l=new Set([`false`,`0`,`no`]);function u(e,t){if(!s(e,t))return;let n=o(e,t);if(n===null)return;let r=n.toLowerCase();if(c.has(r))return!0;if(l.has(r))return!1;i(e,t,`Invalid boolean value "${n}" for data-${t}. Expected: true/false/1/0/yes/no or empty.`)}function d(e,t){let n=o(e,t);if(n===null||n===``)return;let r=Number(n);if(Number.isNaN(r)||!Number.isFinite(r)){i(e,t,`Invalid number value "${n}" for data-${t}.`);return}return r}function f(e,t){if(s(e,t))return o(e,t)??void 0}function p(e,t,n){let r=o(e,t);if(r!==null){if(n.includes(r))return r;i(e,t,`Invalid value "${r}" for data-${t}. Expected one of: ${n.join(`, `)}.`)}}let m=0;const h=(e,t)=>e.id||=`${t}-${++m}`,g=(e,t,n)=>{n===null?e.removeAttribute(`aria-${t}`):e.setAttribute(`aria-${t}`,String(n))};function _(e,t,n,r){return e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)}const v=(e,t,n)=>e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:n})),y=[`horizontal`,`vertical`];function b(e){if(!e)return;let t=e.split(`,`).map(e=>parseFloat(e.trim()));if(!t.some(e=>isNaN(e))){if(t.length===2)return[t[0],t[1]];if(t.length===1)return t[0]}}function x(e){return Array.isArray(e)}function S(e,t,n,r){let i=Math.round((e-t)/r)*r+t,a=r.toString().split(`.`)[1]?.length??0,o=parseFloat(i.toFixed(a));return Math.min(n,Math.max(t,o))}function C(e,t,n){return n===t?0:(e-t)/(n-t)*100}function w(e,t,n){return e/100*(n-t)+t}function T(n,r={}){let i=e(n,`slider-track`),a=t(n,`slider-thumb`),o=e(n,`slider-range`);if(!i||a.length===0)throw Error(`Slider requires slider-track and at least one slider-thumb`);let s=i.parentElement;if(!s)throw Error(`Slider track must have a parent element (control)`);let c=r.min??d(n,`min`)??0,l=r.max??d(n,`max`)??100;c>l&&([c,l]=[l,c]);let m=r.step??d(n,`step`)??1;m<=0&&(m=1);let T=r.largeStep??d(n,`largeStep`)??m*10,E=r.orientation??p(n,`orientation`,y)??`horizontal`,D=r.disabled??u(n,`disabled`)??!1,O=r.onValueChange,k=r.onValueCommit,A=b(f(n,`defaultValue`)),j=r.defaultValue??A??c,M=a.length>=2;M&&!x(j)?j=[c,j]:!M&&x(j)&&(j=j[1]);let N=x(j)?[S(j[0],c,l,m),S(j[1],c,l,m)]:S(j,c,l,m),P=[],F=null,I=0,L=null,R=null;n.setAttribute(`data-orientation`,E);let z=e=>{e?n.setAttribute(`data-disabled`,``):n.removeAttribute(`data-disabled`);for(let t of a)g(t,`disabled`,e),t.tabIndex=e?-1:0};z(D);let B=(e,t)=>{e.setAttribute(`role`,`slider`),e.tabIndex=D?-1:0,h(e,`slider-thumb`),g(e,`orientation`,E);let n=e.hasAttribute(`aria-label`)||e.hasAttribute(`aria-labelledby`),r=e.dataset.label;r?g(e,`label`,r):!n&&M&&g(e,`label`,t===0?`Minimum`:`Maximum`)};for(let e=0;e<a.length;e++)B(a[e],e);let V=E===`horizontal`,H=()=>{if(x(N)){let[e,t]=N,n=C(e,c,l),r=C(t,c,l);a[0]&&(g(a[0],`valuenow`,String(e)),g(a[0],`valuemin`,String(c)),g(a[0],`valuemax`,String(t)),V?(a[0].style.left=`${n}%`,a[0].style.bottom=``):(a[0].style.bottom=`${n}%`,a[0].style.left=``)),a[1]&&(g(a[1],`valuenow`,String(t)),g(a[1],`valuemin`,String(e)),g(a[1],`valuemax`,String(l)),V?(a[1].style.left=`${r}%`,a[1].style.bottom=``):(a[1].style.bottom=`${r}%`,a[1].style.left=``)),o&&(V?(o.style.left=`${n}%`,o.style.width=`${r-n}%`,o.style.bottom=``,o.style.height=``):(o.style.bottom=`${n}%`,o.style.height=`${r-n}%`,o.style.left=``,o.style.width=``))}else{let e=C(N,c,l);a[0]&&(g(a[0],`valuenow`,String(N)),g(a[0],`valuemin`,String(c)),g(a[0],`valuemax`,String(l)),V?(a[0].style.left=`${e}%`,a[0].style.bottom=``):(a[0].style.bottom=`${e}%`,a[0].style.left=``)),o&&(V?(o.style.left=`0%`,o.style.width=`${e}%`,o.style.bottom=``,o.style.height=``):(o.style.bottom=`0%`,o.style.height=`${e}%`,o.style.left=``,o.style.width=``))}x(N)?n.setAttribute(`data-value`,`${N[0]},${N[1]}`):n.setAttribute(`data-value`,String(N))},U=(e,t)=>x(e)&&x(t)?e[0]===t[0]&&e[1]===t[1]:e===t,W=(e,t=!0)=>{let r;if(x(e)){let[t,n]=e;t=S(t,c,l,m),n=S(n,c,l,m),t>n&&([t,n]=[n,t]),r=[t,n]}else r=S(e,c,l,m);let i=!U(r,N);return i?(N=r,H(),t&&(v(n,`slider:change`,{value:N}),O?.(N)),!0):!1};H();let G=e=>{let t=i.getBoundingClientRect();if(V&&t.width===0||!V&&t.height===0)return null;let n;return n=V?(e.clientX-t.left)/t.width*100:(t.bottom-e.clientY)/t.height*100,n=Math.max(0,Math.min(100,n)),w(n,c,l)},K=e=>{if(!e||!(e instanceof HTMLElement))return null;let t=a.indexOf(e);if(t!==-1)return t;for(let t=0;t<a.length;t++)if(a[t].contains(e))return t;return null},q=e=>{if(!x(N))return 0;let[t,n]=N,r=Math.abs(e-t),i=Math.abs(e-n);return r===i?I:r<i?0:1},J=(e,t)=>{if(x(N)){let[n,r]=N;if(e===0){let e=S(t,c,r,m);return W([e,r])}else{let e=S(t,n,l,m);return W([n,e])}}else return W(t)},Y=e=>{if(D)return;e.preventDefault();let t=G(e);if(t===null)return;let r=K(e.target);r===null&&(r=q(t)),F=r,I=r,n.setAttribute(`data-dragging`,``),a[r]?.setAttribute(`data-dragging`,``),a[r]?.focus(),R=s.style.touchAction,s.style.touchAction=`none`,J(r,t),s.setPointerCapture(e.pointerId)},X=e=>{if(F===null||D)return;e.preventDefault();let t=G(e);t!==null&&J(F,t)},Z=e=>{if(F!==null){n.removeAttribute(`data-dragging`);for(let e of a)e.removeAttribute(`data-dragging`);s.style.touchAction=R??``,R=null,v(n,`slider:commit`,{value:N}),k?.(N),F=null;try{s.releasePointerCapture(e.pointerId)}catch{}}};P.push(_(s,`pointerdown`,Y)),P.push(_(s,`pointermove`,X)),P.push(_(s,`pointerup`,Z)),P.push(_(s,`pointercancel`,Z));let Q=e=>{if(D)return;let t=e.target,n=a.indexOf(t);if(n===-1)return;let r=0,i=null;switch(e.key){case`ArrowRight`:if(!V)return;r=m;break;case`ArrowLeft`:if(!V)return;r=-m;break;case`ArrowUp`:if(V)return;r=m;break;case`ArrowDown`:if(V)return;r=-m;break;case`PageUp`:r=T;break;case`PageDown`:r=-T;break;case`Home`:i=c;break;case`End`:i=l;break;default:return}e.shiftKey&&e.key.startsWith(`Arrow`)&&(r=r>0?T:r<0?-T:0),e.preventDefault(),I=n,L===null&&(L=x(N)?[N[0],N[1]]:N);let o=x(N)?N[n]??N[0]:N,s=i===null?o+r:i;J(n,s)},$=()=>{L!==null&&(U(L,N)||(v(n,`slider:commit`,{value:N}),k?.(N)),L=null)};for(let e of a)P.push(_(e,`keydown`,Q)),P.push(_(e,`blur`,$));let ee=e=>{let t=e,r=t.detail,i;if(typeof r==`number`||Array.isArray(r)?i=r:r&&typeof r==`object`&&`value`in r&&(i=r.value),i!==void 0){let e=W(i);e&&(v(n,`slider:commit`,{value:N}),k?.(N))}};P.push(_(n,`slider:set`,ee));let te={setValue:e=>{W(e)},get value(){return N},get min(){return c},get max(){return l},get disabled(){return D},destroy:()=>{P.forEach(e=>e()),P.length=0}};return te}const E=new WeakSet;function D(e=document){let t=[];for(let r of n(e,`slider`)){if(E.has(r))continue;E.add(r),t.push(T(r))}return t}exports.create=D,exports.createSlider=T;
@@ -0,0 +1,74 @@
1
+ //#region src/index.d.ts
2
+ interface SliderOptions {
3
+ /** Initial value(s) - number or [min, max] for range */
4
+ defaultValue?: number | [number, number];
5
+ /** Minimum value */
6
+ min?: number;
7
+ /** Maximum value */
8
+ max?: number;
9
+ /** Step increment */
10
+ step?: number;
11
+ /** Larger step for PageUp/PageDown/Shift+Arrow */
12
+ largeStep?: number;
13
+ /** Slider orientation */
14
+ orientation?: "horizontal" | "vertical";
15
+ /** Disable the slider */
16
+ disabled?: boolean;
17
+ /** Callback when value changes during interaction */
18
+ onValueChange?: (value: number | [number, number]) => void;
19
+ /** Callback when interaction ends (pointer release, blur) */
20
+ onValueCommit?: (value: number | [number, number]) => void;
21
+ }
22
+ interface SliderController {
23
+ /** Set value programmatically */
24
+ setValue(value: number | [number, number]): void;
25
+ /** Current value(s) */
26
+ readonly value: number | [number, number];
27
+ /** Min value */
28
+ readonly min: number;
29
+ /** Max value */
30
+ readonly max: number;
31
+ /** Whether slider is disabled */
32
+ readonly disabled: boolean;
33
+ /** Cleanup all event listeners */
34
+ destroy(): void;
35
+ }
36
+ /**
37
+ * Create a slider controller for a root element
38
+ *
39
+ * ## Events
40
+ * - **Outbound** `slider:change` (on root): Fires during value changes.
41
+ * `event.detail: { value: number | [number, number] }`
42
+ * - **Outbound** `slider:commit` (on root): Fires when interaction ends (pointer release, blur).
43
+ * `event.detail: { value: number | [number, number] }`
44
+ * - **Inbound** `slider:set` (on root): Set value programmatically.
45
+ * `event.detail: { value: number | [number, number] }`
46
+ *
47
+ * @example
48
+ * ```js
49
+ * // Listen for value changes
50
+ * root.addEventListener("slider:change", (e) => console.log(e.detail.value));
51
+ * // Set value from outside
52
+ * root.dispatchEvent(new CustomEvent("slider:set", { detail: { value: 50 } }));
53
+ * ```
54
+ *
55
+ * Expected markup:
56
+ * ```html
57
+ * <div data-slot="slider" data-default-value="50">
58
+ * <div class="slider-control">
59
+ * <div data-slot="slider-track">
60
+ * <div data-slot="slider-range"></div>
61
+ * </div>
62
+ * <div data-slot="slider-thumb"></div>
63
+ * </div>
64
+ * </div>
65
+ * ```
66
+ */
67
+ declare function createSlider(root: Element, options?: SliderOptions): SliderController;
68
+ /**
69
+ * Find and bind all slider components in a scope
70
+ * Returns array of controllers for programmatic access
71
+ */
72
+ declare function create(scope?: ParentNode): SliderController[];
73
+ //#endregion
74
+ export { SliderController, SliderOptions, create, createSlider };
@@ -0,0 +1,74 @@
1
+ //#region src/index.d.ts
2
+ interface SliderOptions {
3
+ /** Initial value(s) - number or [min, max] for range */
4
+ defaultValue?: number | [number, number];
5
+ /** Minimum value */
6
+ min?: number;
7
+ /** Maximum value */
8
+ max?: number;
9
+ /** Step increment */
10
+ step?: number;
11
+ /** Larger step for PageUp/PageDown/Shift+Arrow */
12
+ largeStep?: number;
13
+ /** Slider orientation */
14
+ orientation?: "horizontal" | "vertical";
15
+ /** Disable the slider */
16
+ disabled?: boolean;
17
+ /** Callback when value changes during interaction */
18
+ onValueChange?: (value: number | [number, number]) => void;
19
+ /** Callback when interaction ends (pointer release, blur) */
20
+ onValueCommit?: (value: number | [number, number]) => void;
21
+ }
22
+ interface SliderController {
23
+ /** Set value programmatically */
24
+ setValue(value: number | [number, number]): void;
25
+ /** Current value(s) */
26
+ readonly value: number | [number, number];
27
+ /** Min value */
28
+ readonly min: number;
29
+ /** Max value */
30
+ readonly max: number;
31
+ /** Whether slider is disabled */
32
+ readonly disabled: boolean;
33
+ /** Cleanup all event listeners */
34
+ destroy(): void;
35
+ }
36
+ /**
37
+ * Create a slider controller for a root element
38
+ *
39
+ * ## Events
40
+ * - **Outbound** `slider:change` (on root): Fires during value changes.
41
+ * `event.detail: { value: number | [number, number] }`
42
+ * - **Outbound** `slider:commit` (on root): Fires when interaction ends (pointer release, blur).
43
+ * `event.detail: { value: number | [number, number] }`
44
+ * - **Inbound** `slider:set` (on root): Set value programmatically.
45
+ * `event.detail: { value: number | [number, number] }`
46
+ *
47
+ * @example
48
+ * ```js
49
+ * // Listen for value changes
50
+ * root.addEventListener("slider:change", (e) => console.log(e.detail.value));
51
+ * // Set value from outside
52
+ * root.dispatchEvent(new CustomEvent("slider:set", { detail: { value: 50 } }));
53
+ * ```
54
+ *
55
+ * Expected markup:
56
+ * ```html
57
+ * <div data-slot="slider" data-default-value="50">
58
+ * <div class="slider-control">
59
+ * <div data-slot="slider-track">
60
+ * <div data-slot="slider-range"></div>
61
+ * </div>
62
+ * <div data-slot="slider-thumb"></div>
63
+ * </div>
64
+ * </div>
65
+ * ```
66
+ */
67
+ declare function createSlider(root: Element, options?: SliderOptions): SliderController;
68
+ /**
69
+ * Find and bind all slider components in a scope
70
+ * Returns array of controllers for programmatic access
71
+ */
72
+ declare function create(scope?: ParentNode): SliderController[];
73
+ //#endregion
74
+ export { SliderController, SliderOptions, create, createSlider };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ const e=(e,t)=>e.querySelector(`[data-slot="${t}"]`),t=(e,t)=>[...e.querySelectorAll(`[data-slot="${t}"]`)],n=(e,t)=>[...e.querySelectorAll(`[data-slot="${t}"]`)],r=new WeakMap;function i(e,t,n){if(typeof process<`u`&&process.env?.NODE_ENV===`production`)return;let i=r.get(e);i||(i=new Set,r.set(e,i)),!i.has(t)&&(i.add(t),console.warn(`[@data-slot] ${n}`))}function a(e){let t=`data-${e.replace(/([A-Z])/g,`-$1`).toLowerCase()}`,n=`data-${e}`;return t===n?[t]:[t,n]}function o(e,t){for(let n of a(t))if(e.hasAttribute(n))return e.getAttribute(n);return null}function s(e,t){return a(t).some(t=>e.hasAttribute(t))}const c=new Set([``,`true`,`1`,`yes`]),l=new Set([`false`,`0`,`no`]);function u(e,t){if(!s(e,t))return;let n=o(e,t);if(n===null)return;let r=n.toLowerCase();if(c.has(r))return!0;if(l.has(r))return!1;i(e,t,`Invalid boolean value "${n}" for data-${t}. Expected: true/false/1/0/yes/no or empty.`)}function d(e,t){let n=o(e,t);if(n===null||n===``)return;let r=Number(n);if(Number.isNaN(r)||!Number.isFinite(r)){i(e,t,`Invalid number value "${n}" for data-${t}.`);return}return r}function f(e,t){if(s(e,t))return o(e,t)??void 0}function p(e,t,n){let r=o(e,t);if(r!==null){if(n.includes(r))return r;i(e,t,`Invalid value "${r}" for data-${t}. Expected one of: ${n.join(`, `)}.`)}}let m=0;const h=(e,t)=>e.id||=`${t}-${++m}`,g=(e,t,n)=>{n===null?e.removeAttribute(`aria-${t}`):e.setAttribute(`aria-${t}`,String(n))};function _(e,t,n,r){return e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)}const v=(e,t,n)=>e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:n})),y=[`horizontal`,`vertical`];function b(e){if(!e)return;let t=e.split(`,`).map(e=>parseFloat(e.trim()));if(!t.some(e=>isNaN(e))){if(t.length===2)return[t[0],t[1]];if(t.length===1)return t[0]}}function x(e){return Array.isArray(e)}function S(e,t,n,r){let i=Math.round((e-t)/r)*r+t,a=r.toString().split(`.`)[1]?.length??0,o=parseFloat(i.toFixed(a));return Math.min(n,Math.max(t,o))}function C(e,t,n){return n===t?0:(e-t)/(n-t)*100}function w(e,t,n){return e/100*(n-t)+t}function T(n,r={}){let i=e(n,`slider-track`),a=t(n,`slider-thumb`),o=e(n,`slider-range`);if(!i||a.length===0)throw Error(`Slider requires slider-track and at least one slider-thumb`);let s=i.parentElement;if(!s)throw Error(`Slider track must have a parent element (control)`);let c=r.min??d(n,`min`)??0,l=r.max??d(n,`max`)??100;c>l&&([c,l]=[l,c]);let m=r.step??d(n,`step`)??1;m<=0&&(m=1);let T=r.largeStep??d(n,`largeStep`)??m*10,E=r.orientation??p(n,`orientation`,y)??`horizontal`,D=r.disabled??u(n,`disabled`)??!1,O=r.onValueChange,k=r.onValueCommit,A=b(f(n,`defaultValue`)),j=r.defaultValue??A??c,M=a.length>=2;M&&!x(j)?j=[c,j]:!M&&x(j)&&(j=j[1]);let N=x(j)?[S(j[0],c,l,m),S(j[1],c,l,m)]:S(j,c,l,m),P=[],F=null,I=0,L=null,R=null;n.setAttribute(`data-orientation`,E);let z=e=>{e?n.setAttribute(`data-disabled`,``):n.removeAttribute(`data-disabled`);for(let t of a)g(t,`disabled`,e),t.tabIndex=e?-1:0};z(D);let B=(e,t)=>{e.setAttribute(`role`,`slider`),e.tabIndex=D?-1:0,h(e,`slider-thumb`),g(e,`orientation`,E);let n=e.hasAttribute(`aria-label`)||e.hasAttribute(`aria-labelledby`),r=e.dataset.label;r?g(e,`label`,r):!n&&M&&g(e,`label`,t===0?`Minimum`:`Maximum`)};for(let e=0;e<a.length;e++)B(a[e],e);let V=E===`horizontal`,H=()=>{if(x(N)){let[e,t]=N,n=C(e,c,l),r=C(t,c,l);a[0]&&(g(a[0],`valuenow`,String(e)),g(a[0],`valuemin`,String(c)),g(a[0],`valuemax`,String(t)),V?(a[0].style.left=`${n}%`,a[0].style.bottom=``):(a[0].style.bottom=`${n}%`,a[0].style.left=``)),a[1]&&(g(a[1],`valuenow`,String(t)),g(a[1],`valuemin`,String(e)),g(a[1],`valuemax`,String(l)),V?(a[1].style.left=`${r}%`,a[1].style.bottom=``):(a[1].style.bottom=`${r}%`,a[1].style.left=``)),o&&(V?(o.style.left=`${n}%`,o.style.width=`${r-n}%`,o.style.bottom=``,o.style.height=``):(o.style.bottom=`${n}%`,o.style.height=`${r-n}%`,o.style.left=``,o.style.width=``))}else{let e=C(N,c,l);a[0]&&(g(a[0],`valuenow`,String(N)),g(a[0],`valuemin`,String(c)),g(a[0],`valuemax`,String(l)),V?(a[0].style.left=`${e}%`,a[0].style.bottom=``):(a[0].style.bottom=`${e}%`,a[0].style.left=``)),o&&(V?(o.style.left=`0%`,o.style.width=`${e}%`,o.style.bottom=``,o.style.height=``):(o.style.bottom=`0%`,o.style.height=`${e}%`,o.style.left=``,o.style.width=``))}x(N)?n.setAttribute(`data-value`,`${N[0]},${N[1]}`):n.setAttribute(`data-value`,String(N))},U=(e,t)=>x(e)&&x(t)?e[0]===t[0]&&e[1]===t[1]:e===t,W=(e,t=!0)=>{let r;if(x(e)){let[t,n]=e;t=S(t,c,l,m),n=S(n,c,l,m),t>n&&([t,n]=[n,t]),r=[t,n]}else r=S(e,c,l,m);let i=!U(r,N);return i?(N=r,H(),t&&(v(n,`slider:change`,{value:N}),O?.(N)),!0):!1};H();let G=e=>{let t=i.getBoundingClientRect();if(V&&t.width===0||!V&&t.height===0)return null;let n;return n=V?(e.clientX-t.left)/t.width*100:(t.bottom-e.clientY)/t.height*100,n=Math.max(0,Math.min(100,n)),w(n,c,l)},K=e=>{if(!e||!(e instanceof HTMLElement))return null;let t=a.indexOf(e);if(t!==-1)return t;for(let t=0;t<a.length;t++)if(a[t].contains(e))return t;return null},q=e=>{if(!x(N))return 0;let[t,n]=N,r=Math.abs(e-t),i=Math.abs(e-n);return r===i?I:r<i?0:1},J=(e,t)=>{if(x(N)){let[n,r]=N;if(e===0){let e=S(t,c,r,m);return W([e,r])}else{let e=S(t,n,l,m);return W([n,e])}}else return W(t)},Y=e=>{if(D)return;e.preventDefault();let t=G(e);if(t===null)return;let r=K(e.target);r===null&&(r=q(t)),F=r,I=r,n.setAttribute(`data-dragging`,``),a[r]?.setAttribute(`data-dragging`,``),a[r]?.focus(),R=s.style.touchAction,s.style.touchAction=`none`,J(r,t),s.setPointerCapture(e.pointerId)},X=e=>{if(F===null||D)return;e.preventDefault();let t=G(e);t!==null&&J(F,t)},Z=e=>{if(F!==null){n.removeAttribute(`data-dragging`);for(let e of a)e.removeAttribute(`data-dragging`);s.style.touchAction=R??``,R=null,v(n,`slider:commit`,{value:N}),k?.(N),F=null;try{s.releasePointerCapture(e.pointerId)}catch{}}};P.push(_(s,`pointerdown`,Y)),P.push(_(s,`pointermove`,X)),P.push(_(s,`pointerup`,Z)),P.push(_(s,`pointercancel`,Z));let Q=e=>{if(D)return;let t=e.target,n=a.indexOf(t);if(n===-1)return;let r=0,i=null;switch(e.key){case`ArrowRight`:if(!V)return;r=m;break;case`ArrowLeft`:if(!V)return;r=-m;break;case`ArrowUp`:if(V)return;r=m;break;case`ArrowDown`:if(V)return;r=-m;break;case`PageUp`:r=T;break;case`PageDown`:r=-T;break;case`Home`:i=c;break;case`End`:i=l;break;default:return}e.shiftKey&&e.key.startsWith(`Arrow`)&&(r=r>0?T:r<0?-T:0),e.preventDefault(),I=n,L===null&&(L=x(N)?[N[0],N[1]]:N);let o=x(N)?N[n]??N[0]:N,s=i===null?o+r:i;J(n,s)},$=()=>{L!==null&&(U(L,N)||(v(n,`slider:commit`,{value:N}),k?.(N)),L=null)};for(let e of a)P.push(_(e,`keydown`,Q)),P.push(_(e,`blur`,$));let ee=e=>{let t=e,r=t.detail,i;if(typeof r==`number`||Array.isArray(r)?i=r:r&&typeof r==`object`&&`value`in r&&(i=r.value),i!==void 0){let e=W(i);e&&(v(n,`slider:commit`,{value:N}),k?.(N))}};P.push(_(n,`slider:set`,ee));let te={setValue:e=>{W(e)},get value(){return N},get min(){return c},get max(){return l},get disabled(){return D},destroy:()=>{P.forEach(e=>e()),P.length=0}};return te}const E=new WeakSet;function D(e=document){let t=[];for(let r of n(e,`slider`)){if(E.has(r))continue;E.add(r),t.push(T(r))}return t}export{D as create,T as createSlider};
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@data-slot/slider",
3
+ "version": "0.2.9",
4
+ "type": "module",
5
+ "sideEffects": false,
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsdown"
26
+ },
27
+ "devDependencies": {
28
+ "@data-slot/core": "workspace:*"
29
+ },
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/bejamas/data-slot",
33
+ "directory": "packages/slider"
34
+ },
35
+ "keywords": [
36
+ "headless",
37
+ "ui",
38
+ "slider",
39
+ "range",
40
+ "vanilla",
41
+ "data-slot"
42
+ ],
43
+ "license": "MIT"
44
+ }