motionrail 0.0.0 → 0.0.3

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.
@@ -0,0 +1,45 @@
1
+ import type { MotionRailOptions } from "./types";
2
+ export declare class MotionRail {
3
+ private rtl;
4
+ private rtlScrollType;
5
+ private autoplay;
6
+ private breakpoints;
7
+ private element;
8
+ private delay;
9
+ private resumeDelay;
10
+ private autoPlayIntervalId;
11
+ private autoPlayTimeoutId;
12
+ private currentIndex;
13
+ private isDragging;
14
+ private startX;
15
+ private startLogicalScroll;
16
+ private cancelScroll;
17
+ private lastPointerX;
18
+ private lastPointerTime;
19
+ private velocity;
20
+ private pointerId;
21
+ private snapPoints;
22
+ private resizeObserver;
23
+ constructor(element: HTMLElement, options: MotionRailOptions);
24
+ private detectRTLScrollType;
25
+ private getLogicalScroll;
26
+ private setLogicalScroll;
27
+ private scrollToLogical;
28
+ private observeResize;
29
+ private cacheSnapPoints;
30
+ private findNearestSnapPoint;
31
+ private init;
32
+ private attachPointerEvents;
33
+ private handlePointerDown;
34
+ private handlePointerMove;
35
+ private handlePointerUp;
36
+ private animateLogicalScroll;
37
+ private scrollByPage;
38
+ play(): void;
39
+ next(): void;
40
+ prev(): void;
41
+ pause(): void;
42
+ scrollToIndex(index: number): void;
43
+ getCurrentIndex(): number;
44
+ destroy(): void;
45
+ }
@@ -0,0 +1,12 @@
1
+ export type MotionRailBreakpoint = {
2
+ width?: number;
3
+ columns?: number;
4
+ gap?: string;
5
+ };
6
+ export type MotionRailOptions = {
7
+ autoplay?: boolean;
8
+ resumeDelay?: number;
9
+ delay?: number;
10
+ rtl?: boolean;
11
+ breakpoints: MotionRailBreakpoint[];
12
+ };
@@ -0,0 +1,7 @@
1
+ import type { MotionRailBreakpoint } from "./types";
2
+ export declare function randomContainerName(): string;
3
+ export declare function setBreakPoints(par: {
4
+ container: HTMLElement;
5
+ breakpoints: MotionRailBreakpoint[];
6
+ length: number;
7
+ }): HTMLStyleElement | undefined;
@@ -0,0 +1,3 @@
1
+ export { MotionRail } from "./lib/main";
2
+ export type { MotionRailBreakpoint, MotionRailOptions } from "./lib/types";
3
+ import "./style.css";
@@ -0,0 +1,196 @@
1
+ function randomContainerName() {
2
+ return `motion-rail-${Math.random().toString(36).substring(2, 11)}`;
3
+ }
4
+ function setBreakPoints(t) {
5
+ let { container: n, breakpoints: r, length: i } = t;
6
+ if (!n.querySelector(".motion-rail-container")) return;
7
+ let a = "";
8
+ n.style.containerName ? a = n.style.containerName : (a = randomContainerName(), n.style.containerName = a);
9
+ let o = document.createElement("style"), s = "", c = r.filter((e) => e.width), l = c.length > 0 ? Math.min(...c.map((e) => e.width)) : null;
10
+ return r.forEach((e) => {
11
+ let t = e.columns || 1, n = e.gap || "0px", r = `calc((100cqw - (${t - 1} * ${n})) / ${t})`, o = "";
12
+ o = e.width ? `(min-width: ${e.width}px)` : l ? `(max-width: ${l - 1}px)` : "(min-width: 0px)", s += `
13
+ @container ${a} ${o} {
14
+ .motion-rail-container {
15
+ grid-template-columns: repeat(${i}, ${r});
16
+ gap: ${n};
17
+ }
18
+ }
19
+ `;
20
+ }), o.textContent = s, document.head.appendChild(o), o;
21
+ }
22
+ var MotionRail = class {
23
+ rtl = !1;
24
+ rtlScrollType = "default";
25
+ autoplay = !1;
26
+ breakpoints = [];
27
+ element;
28
+ delay = 3e3;
29
+ resumeDelay = 4e3;
30
+ autoPlayIntervalId = null;
31
+ autoPlayTimeoutId = null;
32
+ currentIndex = 0;
33
+ isDragging = !1;
34
+ startX = 0;
35
+ startLogicalScroll = 0;
36
+ cancelScroll = null;
37
+ lastPointerX = 0;
38
+ lastPointerTime = 0;
39
+ velocity = 0;
40
+ pointerId = null;
41
+ snapPoints = [];
42
+ resizeObserver = null;
43
+ constructor(e, n) {
44
+ this.autoplay = n.autoplay || !1, this.rtl = n.rtl || !1, this.breakpoints = n.breakpoints, this.element = e, this.delay = n.delay || 3e3, this.resumeDelay = n.resumeDelay || 4e3, this.rtl && (this.rtlScrollType = this.detectRTLScrollType()), setBreakPoints({
45
+ container: this.element,
46
+ breakpoints: this.breakpoints,
47
+ length: this.element.querySelectorAll(".motion-rail-item").length
48
+ }), this.init(), this.attachPointerEvents(), this.cacheSnapPoints(), this.observeResize(), this.autoplay && this.play();
49
+ }
50
+ detectRTLScrollType() {
51
+ let e = document.createElement("div");
52
+ e.dir = "rtl", e.style.width = "1px", e.style.height = "1px", e.style.position = "absolute", e.style.top = "-1000px", e.style.overflow = "scroll";
53
+ let t = document.createElement("div");
54
+ t.style.width = "2px", t.style.height = "1px", e.appendChild(t), document.body.appendChild(e);
55
+ let n = "default";
56
+ return e.scrollLeft > 0 ? n = "reverse" : (e.scrollLeft = 1, e.scrollLeft < 0 && (n = "negative")), document.body.removeChild(e), n;
57
+ }
58
+ getLogicalScroll() {
59
+ if (!this.rtl) return this.element.scrollLeft;
60
+ let e = this.element.scrollLeft, t = this.element.scrollWidth - this.element.clientWidth;
61
+ switch (this.rtlScrollType) {
62
+ case "negative": return -e;
63
+ case "reverse": return t - e;
64
+ default: return e;
65
+ }
66
+ }
67
+ setLogicalScroll(e) {
68
+ if (!this.rtl) {
69
+ this.element.scrollLeft = e;
70
+ return;
71
+ }
72
+ let t = this.element.scrollWidth - this.element.clientWidth;
73
+ switch (this.rtlScrollType) {
74
+ case "negative":
75
+ this.element.scrollLeft = -e;
76
+ break;
77
+ case "reverse":
78
+ this.element.scrollLeft = t - e;
79
+ break;
80
+ default:
81
+ this.element.scrollLeft = e;
82
+ break;
83
+ }
84
+ }
85
+ scrollToLogical(e, t = "auto") {
86
+ if (!this.rtl) {
87
+ this.element.scrollTo({
88
+ left: e,
89
+ behavior: t
90
+ });
91
+ return;
92
+ }
93
+ let n = this.element.scrollWidth - this.element.clientWidth, r;
94
+ switch (this.rtlScrollType) {
95
+ case "negative":
96
+ r = -e;
97
+ break;
98
+ case "reverse":
99
+ r = n - e;
100
+ break;
101
+ default:
102
+ r = e;
103
+ break;
104
+ }
105
+ this.element.scrollTo({
106
+ left: r,
107
+ behavior: t
108
+ });
109
+ }
110
+ observeResize() {
111
+ typeof ResizeObserver > "u" || (this.resizeObserver = new ResizeObserver(() => {
112
+ this.cacheSnapPoints();
113
+ }), this.resizeObserver.observe(this.element));
114
+ }
115
+ cacheSnapPoints() {
116
+ let e = this.element.querySelectorAll(".motion-rail-item"), t = this.element.scrollWidth - this.element.clientWidth;
117
+ this.snapPoints = Array.from(e).map((e) => Math.min(e.offsetLeft, t));
118
+ }
119
+ findNearestSnapPoint(e) {
120
+ let t = 0, n = Infinity;
121
+ for (let r of this.snapPoints) {
122
+ let i = Math.abs(r - e);
123
+ i < n && (n = i, t = r);
124
+ }
125
+ return t;
126
+ }
127
+ init() {
128
+ this.setLogicalScroll(0), this.currentIndex = 0, this.element.style.cursor = "grab";
129
+ }
130
+ attachPointerEvents() {
131
+ this.element.addEventListener("pointerdown", this.handlePointerDown), this.element.addEventListener("pointermove", this.handlePointerMove), this.element.addEventListener("pointerup", this.handlePointerUp), this.element.addEventListener("pointerleave", this.handlePointerUp);
132
+ }
133
+ handlePointerDown = (e) => {
134
+ this.pointerId === null && (this.pointerId = e.pointerId, this.element.setPointerCapture(e.pointerId), this.isDragging = !0, this.startX = e.clientX, this.startLogicalScroll = this.getLogicalScroll(), this.lastPointerX = e.clientX, this.lastPointerTime = e.timeStamp, this.velocity = 0, this.element.style.cursor = "grabbing", this.element.style.userSelect = "none", this.element.style.scrollSnapType = "none", this.pause(), this.cancelScroll && this.cancelScroll(), this.autoPlayTimeoutId && clearTimeout(this.autoPlayTimeoutId));
135
+ };
136
+ handlePointerMove = (e) => {
137
+ if (!this.isDragging || e.pointerId !== this.pointerId) return;
138
+ e.preventDefault();
139
+ let t = e.clientX - this.startX, n = this.startLogicalScroll - t;
140
+ this.setLogicalScroll(n);
141
+ let r = e.timeStamp - this.lastPointerTime;
142
+ r > 0 && (this.velocity = (e.clientX - this.lastPointerX) / r, this.lastPointerX = e.clientX, this.lastPointerTime = e.timeStamp);
143
+ };
144
+ handlePointerUp = (e) => {
145
+ if (!this.isDragging || e.pointerId !== this.pointerId) return;
146
+ this.element.releasePointerCapture(e.pointerId), this.pointerId = null, this.isDragging = !1, this.element.style.cursor = "grab", this.element.style.userSelect = "";
147
+ let t = Math.abs(this.velocity), n = Math.min(100 + Math.sqrt(t) * 50, 200), r = -this.velocity * n, i = this.getLogicalScroll() + r;
148
+ this.cancelScroll && this.cancelScroll();
149
+ let a = this.findNearestSnapPoint(i);
150
+ this.currentIndex = this.snapPoints.indexOf(a), this.cancelScroll = this.animateLogicalScroll(a, n, () => {
151
+ this.element.style.scrollSnapType = "x mandatory", this.cancelScroll = null, this.autoplay && (this.autoPlayTimeoutId = window.setTimeout(() => {
152
+ this.play(), this.autoPlayTimeoutId = null;
153
+ }, this.resumeDelay));
154
+ }), this.velocity = 0;
155
+ };
156
+ animateLogicalScroll(e, t, n) {
157
+ let r = this.getLogicalScroll(), i = performance.now(), a = !1, o = (s) => {
158
+ if (a) return;
159
+ let c = s - i, l = Math.min(c / t, 1), u = 1 - (1 - l) ** 3, d = r + (e - r) * u;
160
+ this.setLogicalScroll(d), l < 1 ? requestAnimationFrame(o) : n();
161
+ };
162
+ return requestAnimationFrame(o), () => {
163
+ a = !0;
164
+ };
165
+ }
166
+ scrollByPage(e) {
167
+ let t = this.currentIndex + e;
168
+ t >= this.snapPoints.length ? (this.currentIndex = 0, this.scrollToLogical(this.snapPoints[0], "smooth")) : t < 0 ? (this.currentIndex = this.snapPoints.length - 1, this.scrollToLogical(this.snapPoints[this.currentIndex], "smooth")) : (this.currentIndex = t, this.scrollToLogical(this.snapPoints[t], "smooth"));
169
+ }
170
+ play() {
171
+ this.autoPlayIntervalId = window.setInterval(() => {
172
+ this.scrollByPage(1);
173
+ }, this.delay);
174
+ }
175
+ next() {
176
+ this.pause(), this.scrollByPage(1);
177
+ }
178
+ prev() {
179
+ this.pause(), this.scrollByPage(-1);
180
+ }
181
+ pause() {
182
+ this.autoplay && (this.autoPlayIntervalId &&= (clearInterval(this.autoPlayIntervalId), null), this.autoPlayTimeoutId &&= (clearTimeout(this.autoPlayTimeoutId), null), !this.isDragging && (this.autoPlayTimeoutId = window.setTimeout(() => {
183
+ this.play(), this.autoPlayTimeoutId = null;
184
+ }, this.resumeDelay)));
185
+ }
186
+ scrollToIndex(e) {
187
+ this.pause(), e >= 0 && e < this.snapPoints.length && (this.scrollToLogical(this.snapPoints[e], "smooth"), this.currentIndex = e);
188
+ }
189
+ getCurrentIndex() {
190
+ return this.currentIndex;
191
+ }
192
+ destroy() {
193
+ this.autoPlayIntervalId &&= (clearInterval(this.autoPlayIntervalId), null), this.cancelScroll &&= (this.cancelScroll(), null), this.autoPlayTimeoutId &&= (clearTimeout(this.autoPlayTimeoutId), null), this.resizeObserver &&= (this.resizeObserver.disconnect(), null), this.element.removeEventListener("pointerdown", this.handlePointerDown), this.element.removeEventListener("pointermove", this.handlePointerMove), this.element.removeEventListener("pointerup", this.handlePointerUp), this.element.removeEventListener("pointerleave", this.handlePointerUp);
194
+ }
195
+ };
196
+ export { MotionRail };
@@ -0,0 +1,8 @@
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports):typeof define==`function`&&define.amd?define([`exports`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.MotionRail={}))})(this,function(e){function t(){return`motion-rail-${Math.random().toString(36).substring(2,11)}`}function n(e){let{container:n,breakpoints:r,length:i}=e;if(!n.querySelector(`.motion-rail-container`))return;let a=``;n.style.containerName?a=n.style.containerName:(a=t(),n.style.containerName=a);let o=document.createElement(`style`),s=``,c=r.filter(e=>e.width),l=c.length>0?Math.min(...c.map(e=>e.width)):null;return r.forEach(e=>{let t=e.columns||1,n=e.gap||`0px`,r=`calc((100cqw - (${t-1} * ${n})) / ${t})`,o=``;o=e.width?`(min-width: ${e.width}px)`:l?`(max-width: ${l-1}px)`:`(min-width: 0px)`,s+=`
2
+ @container ${a} ${o} {
3
+ .motion-rail-container {
4
+ grid-template-columns: repeat(${i}, ${r});
5
+ gap: ${n};
6
+ }
7
+ }
8
+ `}),o.textContent=s,document.head.appendChild(o),o}e.MotionRail=class{rtl=!1;rtlScrollType=`default`;autoplay=!1;breakpoints=[];element;delay=3e3;resumeDelay=4e3;autoPlayIntervalId=null;autoPlayTimeoutId=null;currentIndex=0;isDragging=!1;startX=0;startLogicalScroll=0;cancelScroll=null;lastPointerX=0;lastPointerTime=0;velocity=0;pointerId=null;snapPoints=[];resizeObserver=null;constructor(e,t){this.autoplay=t.autoplay||!1,this.rtl=t.rtl||!1,this.breakpoints=t.breakpoints,this.element=e,this.delay=t.delay||3e3,this.resumeDelay=t.resumeDelay||4e3,this.rtl&&(this.rtlScrollType=this.detectRTLScrollType()),n({container:this.element,breakpoints:this.breakpoints,length:this.element.querySelectorAll(`.motion-rail-item`).length}),this.init(),this.attachPointerEvents(),this.cacheSnapPoints(),this.observeResize(),this.autoplay&&this.play()}detectRTLScrollType(){let e=document.createElement(`div`);e.dir=`rtl`,e.style.width=`1px`,e.style.height=`1px`,e.style.position=`absolute`,e.style.top=`-1000px`,e.style.overflow=`scroll`;let t=document.createElement(`div`);t.style.width=`2px`,t.style.height=`1px`,e.appendChild(t),document.body.appendChild(e);let n=`default`;return e.scrollLeft>0?n=`reverse`:(e.scrollLeft=1,e.scrollLeft<0&&(n=`negative`)),document.body.removeChild(e),n}getLogicalScroll(){if(!this.rtl)return this.element.scrollLeft;let e=this.element.scrollLeft,t=this.element.scrollWidth-this.element.clientWidth;switch(this.rtlScrollType){case`negative`:return-e;case`reverse`:return t-e;default:return e}}setLogicalScroll(e){if(!this.rtl){this.element.scrollLeft=e;return}let t=this.element.scrollWidth-this.element.clientWidth;switch(this.rtlScrollType){case`negative`:this.element.scrollLeft=-e;break;case`reverse`:this.element.scrollLeft=t-e;break;default:this.element.scrollLeft=e;break}}scrollToLogical(e,t=`auto`){if(!this.rtl){this.element.scrollTo({left:e,behavior:t});return}let n=this.element.scrollWidth-this.element.clientWidth,r;switch(this.rtlScrollType){case`negative`:r=-e;break;case`reverse`:r=n-e;break;default:r=e;break}this.element.scrollTo({left:r,behavior:t})}observeResize(){typeof ResizeObserver>`u`||(this.resizeObserver=new ResizeObserver(()=>{this.cacheSnapPoints()}),this.resizeObserver.observe(this.element))}cacheSnapPoints(){let e=this.element.querySelectorAll(`.motion-rail-item`),t=this.element.scrollWidth-this.element.clientWidth;this.snapPoints=Array.from(e).map(e=>Math.min(e.offsetLeft,t))}findNearestSnapPoint(e){let t=0,n=1/0;for(let r of this.snapPoints){let i=Math.abs(r-e);i<n&&(n=i,t=r)}return t}init(){this.setLogicalScroll(0),this.currentIndex=0,this.element.style.cursor=`grab`}attachPointerEvents(){this.element.addEventListener(`pointerdown`,this.handlePointerDown),this.element.addEventListener(`pointermove`,this.handlePointerMove),this.element.addEventListener(`pointerup`,this.handlePointerUp),this.element.addEventListener(`pointerleave`,this.handlePointerUp)}handlePointerDown=e=>{this.pointerId===null&&(this.pointerId=e.pointerId,this.element.setPointerCapture(e.pointerId),this.isDragging=!0,this.startX=e.clientX,this.startLogicalScroll=this.getLogicalScroll(),this.lastPointerX=e.clientX,this.lastPointerTime=e.timeStamp,this.velocity=0,this.element.style.cursor=`grabbing`,this.element.style.userSelect=`none`,this.element.style.scrollSnapType=`none`,this.pause(),this.cancelScroll&&this.cancelScroll(),this.autoPlayTimeoutId&&clearTimeout(this.autoPlayTimeoutId))};handlePointerMove=e=>{if(!this.isDragging||e.pointerId!==this.pointerId)return;e.preventDefault();let t=e.clientX-this.startX,n=this.startLogicalScroll-t;this.setLogicalScroll(n);let r=e.timeStamp-this.lastPointerTime;r>0&&(this.velocity=(e.clientX-this.lastPointerX)/r,this.lastPointerX=e.clientX,this.lastPointerTime=e.timeStamp)};handlePointerUp=e=>{if(!this.isDragging||e.pointerId!==this.pointerId)return;this.element.releasePointerCapture(e.pointerId),this.pointerId=null,this.isDragging=!1,this.element.style.cursor=`grab`,this.element.style.userSelect=``;let t=Math.abs(this.velocity),n=Math.min(100+Math.sqrt(t)*50,200),r=-this.velocity*n,i=this.getLogicalScroll()+r;this.cancelScroll&&this.cancelScroll();let a=this.findNearestSnapPoint(i);this.currentIndex=this.snapPoints.indexOf(a),this.cancelScroll=this.animateLogicalScroll(a,n,()=>{this.element.style.scrollSnapType=`x mandatory`,this.cancelScroll=null,this.autoplay&&(this.autoPlayTimeoutId=window.setTimeout(()=>{this.play(),this.autoPlayTimeoutId=null},this.resumeDelay))}),this.velocity=0};animateLogicalScroll(e,t,n){let r=this.getLogicalScroll(),i=performance.now(),a=!1,o=s=>{if(a)return;let c=s-i,l=Math.min(c/t,1),u=1-(1-l)**3,d=r+(e-r)*u;this.setLogicalScroll(d),l<1?requestAnimationFrame(o):n()};return requestAnimationFrame(o),()=>{a=!0}}scrollByPage(e){let t=this.currentIndex+e;t>=this.snapPoints.length?(this.currentIndex=0,this.scrollToLogical(this.snapPoints[0],`smooth`)):t<0?(this.currentIndex=this.snapPoints.length-1,this.scrollToLogical(this.snapPoints[this.currentIndex],`smooth`)):(this.currentIndex=t,this.scrollToLogical(this.snapPoints[t],`smooth`))}play(){this.autoPlayIntervalId=window.setInterval(()=>{this.scrollByPage(1)},this.delay)}next(){this.pause(),this.scrollByPage(1)}prev(){this.pause(),this.scrollByPage(-1)}pause(){this.autoplay&&(this.autoPlayIntervalId&&=(clearInterval(this.autoPlayIntervalId),null),this.autoPlayTimeoutId&&=(clearTimeout(this.autoPlayTimeoutId),null),!this.isDragging&&(this.autoPlayTimeoutId=window.setTimeout(()=>{this.play(),this.autoPlayTimeoutId=null},this.resumeDelay)))}scrollToIndex(e){this.pause(),e>=0&&e<this.snapPoints.length&&(this.scrollToLogical(this.snapPoints[e],`smooth`),this.currentIndex=e)}getCurrentIndex(){return this.currentIndex}destroy(){this.autoPlayIntervalId&&=(clearInterval(this.autoPlayIntervalId),null),this.cancelScroll&&=(this.cancelScroll(),null),this.autoPlayTimeoutId&&=(clearTimeout(this.autoPlayTimeoutId),null),this.resizeObserver&&=(this.resizeObserver.disconnect(),null),this.element.removeEventListener(`pointerdown`,this.handlePointerDown),this.element.removeEventListener(`pointermove`,this.handlePointerMove),this.element.removeEventListener(`pointerup`,this.handlePointerUp),this.element.removeEventListener(`pointerleave`,this.handlePointerUp)}}});
package/dist/style.css ADDED
@@ -0,0 +1,2 @@
1
+ .motion-rail::-webkit-scrollbar{display:none}.motion-rail{scroll-snap-type:x mandatory;scrollbar-width:none;-ms-overflow-style:none;position:relative;overflow:scroll hidden;container-type:inline-size}.motion-rail .motion-rail-container{grid-auto-flow:column;height:100%;display:grid}
2
+ /*$vite$:1*/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "motionrail",
3
- "version": "0.0.0",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "main": "./dist/motionrail.umd.cjs",
6
6
  "module": "./dist/motionrail.js",
@@ -16,6 +16,10 @@
16
16
  "files": [
17
17
  "dist"
18
18
  ],
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/juji/motionrail"
22
+ },
19
23
  "scripts": {
20
24
  "dev": "vite",
21
25
  "build": "vite build && tsc",
@@ -32,12 +36,11 @@
32
36
  "lint-staged": "^16.2.7",
33
37
  "prettier": "^3.7.4",
34
38
  "typescript": "~5.9.3",
35
- "vite": "npm:rolldown-vite@7.2.5"
39
+ "vite": "npm:rolldown-vite@7.2.5",
40
+ "husky": "^9.1.7"
36
41
  },
37
42
  "overrides": {
38
43
  "vite": "npm:rolldown-vite@7.2.5"
39
44
  },
40
- "dependencies": {
41
- "husky": "^9.1.7"
42
- }
45
+ "dependencies": {}
43
46
  }