motionrail 0.5.2 → 0.5.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.
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MotionRail
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -59,9 +59,10 @@ var MotionRail = class {
|
|
|
59
59
|
let n = e.columns || 1, i = e.gap || "0px", o = `calc((100cqw - (${n - 1} * ${i})) / ${n})`, s = "";
|
|
60
60
|
s = e.width ? `(min-width: ${e.width}px)` : a ? `(max-width: ${a - 1}px)` : "(min-width: 0px)", r += `
|
|
61
61
|
@container ${t} ${s} {
|
|
62
|
-
[data-motionrail-grid] {
|
|
63
|
-
grid-template-columns: repeat(${this.state.totalItems}, ${o});
|
|
64
|
-
gap: ${i};
|
|
62
|
+
[data-motionrail] [data-motionrail-scrollable] [data-motionrail-grid] {
|
|
63
|
+
grid-template-columns: repeat(${this.state.totalItems}, ${o}) !important;
|
|
64
|
+
gap: ${i} !important;
|
|
65
|
+
opacity: 1 !important;
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"motionrail.es.js","names":["targetIndex: number"],"sources":["../src/lib/main.ts"],"sourcesContent":["import type {\n MotionRailBreakpoint,\n MotionRailExtension,\n MotionRailOptions,\n MotionRailState,\n} from \"./types\";\n\nexport class MotionRail {\n readonly element: HTMLElement;\n readonly scrollable: HTMLElement;\n\n private rtl: boolean = false;\n private autoplay: boolean = false;\n private delay: number = 3000;\n private resumeDelay: number = 4000;\n private onChange?: (state: MotionRailState) => void;\n\n private handleStateChange = () => {\n const state = this.getState();\n this.extensions.forEach((ext) => {\n if (ext.onUpdate) {\n ext.onUpdate(this, state);\n }\n });\n\n if (this.onChange) {\n this.onChange(state);\n }\n };\n\n private extensions: MotionRailExtension[] = [];\n\n private breakpoints: MotionRailBreakpoint[] = [];\n private autoPlayIntervalId: number | null = null;\n private autoPlayTimeoutId: number | null = null;\n private isDragging: boolean = false;\n private startX: number = 0;\n private startLogicalScroll: number = 0;\n private cancelScroll: (() => void) | null = null;\n private lastPointerX: number = 0;\n private lastPointerTime: number = 0;\n private velocity: number = 0;\n private pointerId: number | null = null;\n private snapPoints: number[] = [];\n private resizeObserver: ResizeObserver | null = null;\n private intersectionObserver: IntersectionObserver | null = null;\n\n private state: MotionRailState = {\n totalItems: 0,\n visibleItemIndexes: [],\n isFirstItemVisible: false,\n isLastItemVisible: false,\n };\n\n constructor(element: HTMLElement, options: MotionRailOptions) {\n this.autoplay = options.autoplay || false;\n this.rtl = options.rtl || false;\n this.breakpoints = options.breakpoints || [{ columns: 1, gap: \"0px\" }];\n this.element = element;\n this.extensions = options.extensions || [];\n\n const container = this.element.querySelector(\n \"[data-motionrail-scrollable]\",\n ) as HTMLElement;\n if (!container) {\n throw new Error(\n \"MotionRail: [data-motionrail-scrollable] element not found\",\n );\n }\n this.scrollable = container;\n\n this.delay = options.delay || 3000;\n this.resumeDelay = options.resumeDelay || 4000;\n this.onChange = options.onChange;\n this.state.totalItems = this.element.querySelectorAll(\n \"[data-motionrail-grid] > *\",\n ).length;\n\n this.setBreakPoints();\n\n this.setLogicalScroll(0);\n\n this.attachPointerEvents();\n this.cacheSnapPoints();\n this.observeResize();\n this.observeEdgeItems();\n if (this.autoplay) this.play();\n\n const state = this.getState();\n this.extensions.forEach((ext) => {\n if (ext.onInit) {\n ext.onInit(this, state);\n }\n });\n }\n\n // ============================================================================\n // UTILITY METHODS\n // ============================================================================\n\n private randomContainerName(): string {\n return `motion-rail-${Math.random().toString(36).substring(2, 11)}`;\n }\n\n private setBreakPoints() {\n const motionRailContainer = this.element.querySelector(\n \"[data-motionrail-scrollable]\",\n ) as HTMLElement;\n if (!motionRailContainer) return;\n\n // give random css-safe container-name\n let randomName = \"\";\n if (!motionRailContainer.style.containerName) {\n randomName = this.randomContainerName();\n motionRailContainer.style.containerName = randomName;\n } else {\n randomName = motionRailContainer.style.containerName;\n }\n\n // setup container query\n const styleElement = document.createElement(\"style\");\n let containerQueries = \"\";\n\n // Find the smallest width for base case max-width\n const withWidth = this.breakpoints.filter((bp) => bp.width);\n const smallestWidth =\n withWidth.length > 0\n ? Math.min(...withWidth.map((bp) => bp.width!))\n : null;\n\n this.breakpoints.forEach((bp) => {\n const columns = bp.columns || 1;\n const gapValue = bp.gap || \"0px\";\n const itemWidth = `calc((100cqw - (${columns - 1} * ${gapValue})) / ${columns})`;\n\n let condition = \"\";\n if (bp.width) {\n condition = `(min-width: ${bp.width}px)`;\n } else if (smallestWidth) {\n condition = `(max-width: ${smallestWidth - 1}px)`;\n } else {\n // Single breakpoint with no width\n condition = `(min-width: 0px)`;\n }\n\n containerQueries += `\n @container ${randomName} ${condition} {\n [data-motionrail-grid] {\n grid-template-columns: repeat(${this.state.totalItems}, ${itemWidth});\n gap: ${gapValue};\n }\n }\n `;\n });\n styleElement.textContent = containerQueries;\n document.head.appendChild(styleElement);\n\n return styleElement;\n }\n\n // ============================================================================\n // CORE LOGIC: All operations in logical space\n // ============================================================================\n\n private getLogicalScroll(): number {\n return this.scrollable.scrollLeft;\n }\n\n private setLogicalScroll(logicalScroll: number): void {\n this.scrollable.scrollLeft = logicalScroll;\n }\n\n private scrollToLogical(\n logicalScroll: number,\n behavior: ScrollBehavior = \"auto\",\n ): void {\n this.scrollable.scrollTo({ left: logicalScroll, behavior });\n }\n\n private observeResize() {\n if (typeof ResizeObserver === \"undefined\") return;\n\n this.resizeObserver = new ResizeObserver(() => {\n this.cacheSnapPoints();\n });\n\n this.resizeObserver.observe(this.scrollable);\n }\n\n private observeEdgeItems() {\n if (typeof IntersectionObserver === \"undefined\") return;\n\n const items = this.element.querySelectorAll(\"[data-motionrail-grid] > *\");\n if (items.length === 0) return;\n\n const firstItem = items[0];\n const lastItem = items[items.length - 1];\n\n this.intersectionObserver = new IntersectionObserver(\n (entries) => {\n let stateChanged = false;\n entries.forEach((entry) => {\n const index = Array.from(items).indexOf(entry.target as Element);\n if (index === -1) return;\n\n if (entry.isIntersecting) {\n if (!this.state.visibleItemIndexes.includes(index)) {\n this.state.visibleItemIndexes.push(index);\n this.state.visibleItemIndexes.sort((a, b) => a - b);\n stateChanged = true;\n }\n } else {\n const prevLength = this.state.visibleItemIndexes.length;\n this.state.visibleItemIndexes =\n this.state.visibleItemIndexes.filter((i) => i !== index);\n if (this.state.visibleItemIndexes.length !== prevLength) {\n stateChanged = true;\n }\n }\n\n // Update edge visibility flags\n if (entry.target === firstItem) {\n const newValue = entry.isIntersecting;\n if (this.state.isFirstItemVisible !== newValue) {\n this.state.isFirstItemVisible = newValue;\n stateChanged = true;\n }\n } else if (entry.target === lastItem) {\n const newValue = entry.isIntersecting;\n if (this.state.isLastItemVisible !== newValue) {\n this.state.isLastItemVisible = newValue;\n stateChanged = true;\n }\n }\n });\n\n if (stateChanged) {\n this.handleStateChange();\n }\n },\n {\n root: this.scrollable,\n threshold: 0.5,\n },\n );\n\n items.forEach((item) => this.intersectionObserver!.observe(item));\n }\n\n private cacheSnapPoints() {\n const items = this.element.querySelectorAll(\n \"[data-motionrail-grid] > *\",\n ) as NodeListOf<HTMLElement>;\n const maxScroll = this.scrollable.scrollWidth - this.scrollable.clientWidth;\n\n this.snapPoints = Array.from(items).map((item) => {\n // Snap points are stored in logical space (0-based, increasing forward)\n return Math.min(item.offsetLeft, maxScroll);\n });\n }\n\n private findNearestSnapPoint(logicalScroll: number): number {\n let nearestPoint = 0;\n let minDistance = Infinity;\n\n for (const point of this.snapPoints) {\n const distance = Math.abs(point - logicalScroll);\n if (distance < minDistance) {\n minDistance = distance;\n nearestPoint = point;\n }\n }\n\n return nearestPoint;\n }\n\n private attachPointerEvents() {\n // only on pointer: fine\n if (!window.matchMedia(\"(pointer: fine)\").matches) return;\n this.scrollable.addEventListener(\"pointerdown\", this.handlePointerDown);\n this.scrollable.addEventListener(\"pointermove\", this.handlePointerMove);\n this.scrollable.addEventListener(\"pointerup\", this.handlePointerUp);\n this.scrollable.addEventListener(\"pointerleave\", this.handlePointerUp);\n }\n\n private handlePointerDown = (e: PointerEvent) => {\n if (this.pointerId !== null) return;\n\n this.pointerId = e.pointerId;\n this.scrollable.setPointerCapture(e.pointerId);\n this.isDragging = true;\n this.startX = e.clientX;\n this.startLogicalScroll = this.getLogicalScroll();\n this.lastPointerX = e.clientX;\n this.lastPointerTime = e.timeStamp;\n this.velocity = 0;\n\n this.scrollable.style.userSelect = \"none\";\n this.scrollable.style.scrollSnapType = \"none\";\n this.pause();\n if (this.cancelScroll) {\n this.cancelScroll();\n }\n if (this.autoPlayTimeoutId) clearTimeout(this.autoPlayTimeoutId);\n };\n\n private handlePointerMove = (e: PointerEvent) => {\n if (!this.isDragging || e.pointerId !== this.pointerId) return;\n e.preventDefault();\n\n const deltaX = e.clientX - this.startX;\n const logicalScroll = this.startLogicalScroll - deltaX;\n this.setLogicalScroll(logicalScroll);\n\n const deltaTime = e.timeStamp - this.lastPointerTime;\n if (deltaTime > 0) {\n const pointerDelta = e.clientX - this.lastPointerX;\n this.velocity = pointerDelta / deltaTime;\n this.lastPointerX = e.clientX;\n this.lastPointerTime = e.timeStamp;\n }\n };\n\n private handlePointerUp = (e: PointerEvent) => {\n if (!this.isDragging || e.pointerId !== this.pointerId) return;\n\n this.scrollable.releasePointerCapture(e.pointerId);\n this.pointerId = null;\n this.isDragging = false;\n\n this.scrollable.style.userSelect = \"\";\n\n const velocityMagnitude = Math.abs(this.velocity);\n const baseTime = 100;\n const maxTime = 200;\n const momentumTime = Math.min(\n baseTime + Math.sqrt(velocityMagnitude) * 50,\n maxTime,\n );\n const momentum = -this.velocity * momentumTime;\n\n const currentLogicalScroll = this.getLogicalScroll();\n const targetLogicalScroll = currentLogicalScroll + momentum;\n\n if (this.cancelScroll) {\n this.cancelScroll();\n }\n\n const snapPoint = this.findNearestSnapPoint(targetLogicalScroll);\n\n const onScrollEnd = () => {\n this.scrollable.style.scrollSnapType = \"x mandatory\";\n this.cancelScroll = null;\n if (this.autoplay) {\n this.autoPlayTimeoutId = window.setTimeout(() => {\n this.play();\n this.autoPlayTimeoutId = null;\n }, this.resumeDelay);\n }\n };\n\n // Create a wrapper that animates in logical space\n this.cancelScroll = this.animateLogicalScroll(\n snapPoint,\n momentumTime,\n onScrollEnd,\n );\n this.velocity = 0;\n };\n\n private animateLogicalScroll(\n targetLogical: number,\n duration: number,\n onComplete: () => void,\n ): () => void {\n const startLogical = this.getLogicalScroll();\n const startTime = performance.now();\n let cancelled = false;\n\n const animate = (currentTime: number) => {\n if (cancelled) return;\n\n const elapsed = currentTime - startTime;\n const progress = Math.min(elapsed / duration, 1);\n const eased = 1 - Math.pow(1 - progress, 3);\n const currentLogical =\n startLogical + (targetLogical - startLogical) * eased;\n\n this.setLogicalScroll(currentLogical);\n\n if (progress < 1) {\n requestAnimationFrame(animate);\n } else {\n onComplete();\n }\n };\n\n requestAnimationFrame(animate);\n return () => {\n cancelled = true;\n };\n }\n\n private scrollByPage(direction: 1 | -1) {\n if (this.state.visibleItemIndexes.length === 0) return;\n\n let targetIndex: number;\n const firstVisibleIndex = this.state.visibleItemIndexes[0];\n const lastVisibleIndex =\n this.state.visibleItemIndexes[this.state.visibleItemIndexes.length - 1];\n const visibleCount = this.state.visibleItemIndexes.length;\n\n if (direction === 1) {\n // Going forward: skip all visible items, go to the next one\n targetIndex = lastVisibleIndex + 1;\n if (this.rtl) {\n targetIndex = lastVisibleIndex + visibleCount;\n }\n\n if (\n targetIndex >= this.snapPoints.length - 1 &&\n this.state.isLastItemVisible\n ) {\n targetIndex = 0;\n } else if (\n targetIndex >= this.snapPoints.length - 1 &&\n !this.state.isLastItemVisible\n ) {\n targetIndex = this.snapPoints.length - 1;\n }\n } else {\n // Going backward: skip all visible items, go to the previous one\n targetIndex = firstVisibleIndex - visibleCount;\n if (this.rtl) {\n targetIndex = lastVisibleIndex - visibleCount;\n }\n\n if (targetIndex <= 0 && this.state.isFirstItemVisible) {\n targetIndex = this.snapPoints.length - 1;\n } else if (targetIndex <= 0 && !this.state.isFirstItemVisible) {\n targetIndex = 0;\n }\n }\n\n this.scrollToLogical(this.snapPoints[targetIndex], \"smooth\");\n }\n\n play() {\n this.autoPlayIntervalId = window.setInterval(() => {\n this.scrollByPage(1);\n }, this.delay);\n }\n\n next() {\n this.pause();\n this.scrollByPage(1);\n }\n\n prev() {\n this.pause();\n this.scrollByPage(-1);\n }\n\n pause() {\n if (this.autoPlayIntervalId) {\n clearInterval(this.autoPlayIntervalId);\n this.autoPlayIntervalId = null;\n }\n\n if (this.autoPlayTimeoutId) {\n clearTimeout(this.autoPlayTimeoutId);\n this.autoPlayTimeoutId = null;\n }\n\n if (this.isDragging) return;\n if (!this.autoplay) return;\n this.autoPlayTimeoutId = window.setTimeout(() => {\n this.play();\n this.autoPlayTimeoutId = null;\n }, this.resumeDelay);\n }\n\n scrollToIndex(index: number) {\n this.pause();\n if (index >= 0 && index < this.snapPoints.length) {\n this.scrollToLogical(this.snapPoints[index], \"smooth\");\n }\n }\n\n getState() {\n return {\n ...this.state,\n visibleItemIndexes: [...this.state.visibleItemIndexes],\n };\n }\n\n getOptions() {\n return {\n autoplay: this.autoplay,\n rtl: this.rtl,\n delay: this.delay,\n resumeDelay: this.resumeDelay,\n breakpoints: this.breakpoints.map((bp) => ({ ...bp })),\n };\n }\n\n update() {\n // Disconnect existing intersection observer\n if (this.intersectionObserver) {\n this.intersectionObserver.disconnect();\n this.intersectionObserver = null;\n }\n\n // Update total items count\n this.state.totalItems = this.element.querySelectorAll(\n \"[data-motionrail-grid] > *\",\n ).length;\n\n // Reset visible items state\n this.state.visibleItemIndexes = [];\n this.state.isFirstItemVisible = false;\n this.state.isLastItemVisible = false;\n\n // Re-apply breakpoints with new item count\n this.setBreakPoints();\n\n // Recache snap points with new items\n this.cacheSnapPoints();\n\n // Re-observe edge items\n this.observeEdgeItems();\n\n // Notify state change\n this.handleStateChange();\n }\n\n destroy() {\n const state = this.getState();\n this.extensions.forEach((ext) => {\n if (ext.onDestroy) {\n ext.onDestroy(this, state);\n }\n });\n\n if (this.autoPlayIntervalId) {\n clearInterval(this.autoPlayIntervalId);\n this.autoPlayIntervalId = null;\n }\n\n if (this.cancelScroll) {\n this.cancelScroll();\n this.cancelScroll = null;\n }\n\n if (this.autoPlayTimeoutId) {\n clearTimeout(this.autoPlayTimeoutId);\n this.autoPlayTimeoutId = null;\n }\n\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n\n if (this.intersectionObserver) {\n this.intersectionObserver.disconnect();\n this.intersectionObserver = null;\n }\n\n this.scrollable.removeEventListener(\"pointerdown\", this.handlePointerDown);\n this.scrollable.removeEventListener(\"pointermove\", this.handlePointerMove);\n this.scrollable.removeEventListener(\"pointerup\", this.handlePointerUp);\n this.scrollable.removeEventListener(\"pointerleave\", this.handlePointerUp);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"motionrail.es.js","names":["targetIndex: number"],"sources":["../src/lib/main.ts"],"sourcesContent":["import type {\n MotionRailBreakpoint,\n MotionRailExtension,\n MotionRailOptions,\n MotionRailState,\n} from \"./types\";\n\nexport class MotionRail {\n readonly element: HTMLElement;\n readonly scrollable: HTMLElement;\n\n private rtl: boolean = false;\n private autoplay: boolean = false;\n private delay: number = 3000;\n private resumeDelay: number = 4000;\n private onChange?: (state: MotionRailState) => void;\n\n private handleStateChange = () => {\n const state = this.getState();\n this.extensions.forEach((ext) => {\n if (ext.onUpdate) {\n ext.onUpdate(this, state);\n }\n });\n\n if (this.onChange) {\n this.onChange(state);\n }\n };\n\n private extensions: MotionRailExtension[] = [];\n\n private breakpoints: MotionRailBreakpoint[] = [];\n private autoPlayIntervalId: number | null = null;\n private autoPlayTimeoutId: number | null = null;\n private isDragging: boolean = false;\n private startX: number = 0;\n private startLogicalScroll: number = 0;\n private cancelScroll: (() => void) | null = null;\n private lastPointerX: number = 0;\n private lastPointerTime: number = 0;\n private velocity: number = 0;\n private pointerId: number | null = null;\n private snapPoints: number[] = [];\n private resizeObserver: ResizeObserver | null = null;\n private intersectionObserver: IntersectionObserver | null = null;\n\n private state: MotionRailState = {\n totalItems: 0,\n visibleItemIndexes: [],\n isFirstItemVisible: false,\n isLastItemVisible: false,\n };\n\n constructor(element: HTMLElement, options: MotionRailOptions) {\n this.autoplay = options.autoplay || false;\n this.rtl = options.rtl || false;\n this.breakpoints = options.breakpoints || [{ columns: 1, gap: \"0px\" }];\n this.element = element;\n this.extensions = options.extensions || [];\n\n const container = this.element.querySelector(\n \"[data-motionrail-scrollable]\",\n ) as HTMLElement;\n if (!container) {\n throw new Error(\n \"MotionRail: [data-motionrail-scrollable] element not found\",\n );\n }\n this.scrollable = container;\n\n this.delay = options.delay || 3000;\n this.resumeDelay = options.resumeDelay || 4000;\n this.onChange = options.onChange;\n this.state.totalItems = this.element.querySelectorAll(\n \"[data-motionrail-grid] > *\",\n ).length;\n\n this.setBreakPoints();\n\n this.setLogicalScroll(0);\n\n this.attachPointerEvents();\n this.cacheSnapPoints();\n this.observeResize();\n this.observeEdgeItems();\n if (this.autoplay) this.play();\n\n const state = this.getState();\n this.extensions.forEach((ext) => {\n if (ext.onInit) {\n ext.onInit(this, state);\n }\n });\n }\n\n // ============================================================================\n // UTILITY METHODS\n // ============================================================================\n\n private randomContainerName(): string {\n return `motion-rail-${Math.random().toString(36).substring(2, 11)}`;\n }\n\n private setBreakPoints() {\n const motionRailContainer = this.element.querySelector(\n \"[data-motionrail-scrollable]\",\n ) as HTMLElement;\n if (!motionRailContainer) return;\n\n // give random css-safe container-name\n let randomName = \"\";\n if (!motionRailContainer.style.containerName) {\n randomName = this.randomContainerName();\n motionRailContainer.style.containerName = randomName;\n } else {\n randomName = motionRailContainer.style.containerName;\n }\n\n // setup container query\n const styleElement = document.createElement(\"style\");\n let containerQueries = \"\";\n\n // Find the smallest width for base case max-width\n const withWidth = this.breakpoints.filter((bp) => bp.width);\n const smallestWidth =\n withWidth.length > 0\n ? Math.min(...withWidth.map((bp) => bp.width!))\n : null;\n\n this.breakpoints.forEach((bp) => {\n const columns = bp.columns || 1;\n const gapValue = bp.gap || \"0px\";\n const itemWidth = `calc((100cqw - (${columns - 1} * ${gapValue})) / ${columns})`;\n\n let condition = \"\";\n if (bp.width) {\n condition = `(min-width: ${bp.width}px)`;\n } else if (smallestWidth) {\n condition = `(max-width: ${smallestWidth - 1}px)`;\n } else {\n // Single breakpoint with no width\n condition = `(min-width: 0px)`;\n }\n\n containerQueries += `\n @container ${randomName} ${condition} {\n [data-motionrail] [data-motionrail-scrollable] [data-motionrail-grid] {\n grid-template-columns: repeat(${this.state.totalItems}, ${itemWidth}) !important;\n gap: ${gapValue} !important;\n opacity: 1 !important;\n }\n }\n `;\n });\n styleElement.textContent = containerQueries;\n document.head.appendChild(styleElement);\n\n return styleElement;\n }\n\n // ============================================================================\n // CORE LOGIC: All operations in logical space\n // ============================================================================\n\n private getLogicalScroll(): number {\n return this.scrollable.scrollLeft;\n }\n\n private setLogicalScroll(logicalScroll: number): void {\n this.scrollable.scrollLeft = logicalScroll;\n }\n\n private scrollToLogical(\n logicalScroll: number,\n behavior: ScrollBehavior = \"auto\",\n ): void {\n this.scrollable.scrollTo({ left: logicalScroll, behavior });\n }\n\n private observeResize() {\n if (typeof ResizeObserver === \"undefined\") return;\n\n this.resizeObserver = new ResizeObserver(() => {\n this.cacheSnapPoints();\n });\n\n this.resizeObserver.observe(this.scrollable);\n }\n\n private observeEdgeItems() {\n if (typeof IntersectionObserver === \"undefined\") return;\n\n const items = this.element.querySelectorAll(\"[data-motionrail-grid] > *\");\n if (items.length === 0) return;\n\n const firstItem = items[0];\n const lastItem = items[items.length - 1];\n\n this.intersectionObserver = new IntersectionObserver(\n (entries) => {\n let stateChanged = false;\n entries.forEach((entry) => {\n const index = Array.from(items).indexOf(entry.target as Element);\n if (index === -1) return;\n\n if (entry.isIntersecting) {\n if (!this.state.visibleItemIndexes.includes(index)) {\n this.state.visibleItemIndexes.push(index);\n this.state.visibleItemIndexes.sort((a, b) => a - b);\n stateChanged = true;\n }\n } else {\n const prevLength = this.state.visibleItemIndexes.length;\n this.state.visibleItemIndexes =\n this.state.visibleItemIndexes.filter((i) => i !== index);\n if (this.state.visibleItemIndexes.length !== prevLength) {\n stateChanged = true;\n }\n }\n\n // Update edge visibility flags\n if (entry.target === firstItem) {\n const newValue = entry.isIntersecting;\n if (this.state.isFirstItemVisible !== newValue) {\n this.state.isFirstItemVisible = newValue;\n stateChanged = true;\n }\n } else if (entry.target === lastItem) {\n const newValue = entry.isIntersecting;\n if (this.state.isLastItemVisible !== newValue) {\n this.state.isLastItemVisible = newValue;\n stateChanged = true;\n }\n }\n });\n\n if (stateChanged) {\n this.handleStateChange();\n }\n },\n {\n root: this.scrollable,\n threshold: 0.5,\n },\n );\n\n items.forEach((item) => this.intersectionObserver!.observe(item));\n }\n\n private cacheSnapPoints() {\n const items = this.element.querySelectorAll(\n \"[data-motionrail-grid] > *\",\n ) as NodeListOf<HTMLElement>;\n const maxScroll = this.scrollable.scrollWidth - this.scrollable.clientWidth;\n\n this.snapPoints = Array.from(items).map((item) => {\n // Snap points are stored in logical space (0-based, increasing forward)\n return Math.min(item.offsetLeft, maxScroll);\n });\n }\n\n private findNearestSnapPoint(logicalScroll: number): number {\n let nearestPoint = 0;\n let minDistance = Infinity;\n\n for (const point of this.snapPoints) {\n const distance = Math.abs(point - logicalScroll);\n if (distance < minDistance) {\n minDistance = distance;\n nearestPoint = point;\n }\n }\n\n return nearestPoint;\n }\n\n private attachPointerEvents() {\n // only on pointer: fine\n if (!window.matchMedia(\"(pointer: fine)\").matches) return;\n this.scrollable.addEventListener(\"pointerdown\", this.handlePointerDown);\n this.scrollable.addEventListener(\"pointermove\", this.handlePointerMove);\n this.scrollable.addEventListener(\"pointerup\", this.handlePointerUp);\n this.scrollable.addEventListener(\"pointerleave\", this.handlePointerUp);\n }\n\n private handlePointerDown = (e: PointerEvent) => {\n if (this.pointerId !== null) return;\n\n this.pointerId = e.pointerId;\n this.scrollable.setPointerCapture(e.pointerId);\n this.isDragging = true;\n this.startX = e.clientX;\n this.startLogicalScroll = this.getLogicalScroll();\n this.lastPointerX = e.clientX;\n this.lastPointerTime = e.timeStamp;\n this.velocity = 0;\n\n this.scrollable.style.userSelect = \"none\";\n this.scrollable.style.scrollSnapType = \"none\";\n this.pause();\n if (this.cancelScroll) {\n this.cancelScroll();\n }\n if (this.autoPlayTimeoutId) clearTimeout(this.autoPlayTimeoutId);\n };\n\n private handlePointerMove = (e: PointerEvent) => {\n if (!this.isDragging || e.pointerId !== this.pointerId) return;\n e.preventDefault();\n\n const deltaX = e.clientX - this.startX;\n const logicalScroll = this.startLogicalScroll - deltaX;\n this.setLogicalScroll(logicalScroll);\n\n const deltaTime = e.timeStamp - this.lastPointerTime;\n if (deltaTime > 0) {\n const pointerDelta = e.clientX - this.lastPointerX;\n this.velocity = pointerDelta / deltaTime;\n this.lastPointerX = e.clientX;\n this.lastPointerTime = e.timeStamp;\n }\n };\n\n private handlePointerUp = (e: PointerEvent) => {\n if (!this.isDragging || e.pointerId !== this.pointerId) return;\n\n this.scrollable.releasePointerCapture(e.pointerId);\n this.pointerId = null;\n this.isDragging = false;\n\n this.scrollable.style.userSelect = \"\";\n\n const velocityMagnitude = Math.abs(this.velocity);\n const baseTime = 100;\n const maxTime = 200;\n const momentumTime = Math.min(\n baseTime + Math.sqrt(velocityMagnitude) * 50,\n maxTime,\n );\n const momentum = -this.velocity * momentumTime;\n\n const currentLogicalScroll = this.getLogicalScroll();\n const targetLogicalScroll = currentLogicalScroll + momentum;\n\n if (this.cancelScroll) {\n this.cancelScroll();\n }\n\n const snapPoint = this.findNearestSnapPoint(targetLogicalScroll);\n\n const onScrollEnd = () => {\n this.scrollable.style.scrollSnapType = \"x mandatory\";\n this.cancelScroll = null;\n if (this.autoplay) {\n this.autoPlayTimeoutId = window.setTimeout(() => {\n this.play();\n this.autoPlayTimeoutId = null;\n }, this.resumeDelay);\n }\n };\n\n // Create a wrapper that animates in logical space\n this.cancelScroll = this.animateLogicalScroll(\n snapPoint,\n momentumTime,\n onScrollEnd,\n );\n this.velocity = 0;\n };\n\n private animateLogicalScroll(\n targetLogical: number,\n duration: number,\n onComplete: () => void,\n ): () => void {\n const startLogical = this.getLogicalScroll();\n const startTime = performance.now();\n let cancelled = false;\n\n const animate = (currentTime: number) => {\n if (cancelled) return;\n\n const elapsed = currentTime - startTime;\n const progress = Math.min(elapsed / duration, 1);\n const eased = 1 - Math.pow(1 - progress, 3);\n const currentLogical =\n startLogical + (targetLogical - startLogical) * eased;\n\n this.setLogicalScroll(currentLogical);\n\n if (progress < 1) {\n requestAnimationFrame(animate);\n } else {\n onComplete();\n }\n };\n\n requestAnimationFrame(animate);\n return () => {\n cancelled = true;\n };\n }\n\n private scrollByPage(direction: 1 | -1) {\n if (this.state.visibleItemIndexes.length === 0) return;\n\n let targetIndex: number;\n const firstVisibleIndex = this.state.visibleItemIndexes[0];\n const lastVisibleIndex =\n this.state.visibleItemIndexes[this.state.visibleItemIndexes.length - 1];\n const visibleCount = this.state.visibleItemIndexes.length;\n\n if (direction === 1) {\n // Going forward: skip all visible items, go to the next one\n targetIndex = lastVisibleIndex + 1;\n if (this.rtl) {\n targetIndex = lastVisibleIndex + visibleCount;\n }\n\n if (\n targetIndex >= this.snapPoints.length - 1 &&\n this.state.isLastItemVisible\n ) {\n targetIndex = 0;\n } else if (\n targetIndex >= this.snapPoints.length - 1 &&\n !this.state.isLastItemVisible\n ) {\n targetIndex = this.snapPoints.length - 1;\n }\n } else {\n // Going backward: skip all visible items, go to the previous one\n targetIndex = firstVisibleIndex - visibleCount;\n if (this.rtl) {\n targetIndex = lastVisibleIndex - visibleCount;\n }\n\n if (targetIndex <= 0 && this.state.isFirstItemVisible) {\n targetIndex = this.snapPoints.length - 1;\n } else if (targetIndex <= 0 && !this.state.isFirstItemVisible) {\n targetIndex = 0;\n }\n }\n\n this.scrollToLogical(this.snapPoints[targetIndex], \"smooth\");\n }\n\n play() {\n this.autoPlayIntervalId = window.setInterval(() => {\n this.scrollByPage(1);\n }, this.delay);\n }\n\n next() {\n this.pause();\n this.scrollByPage(1);\n }\n\n prev() {\n this.pause();\n this.scrollByPage(-1);\n }\n\n pause() {\n if (this.autoPlayIntervalId) {\n clearInterval(this.autoPlayIntervalId);\n this.autoPlayIntervalId = null;\n }\n\n if (this.autoPlayTimeoutId) {\n clearTimeout(this.autoPlayTimeoutId);\n this.autoPlayTimeoutId = null;\n }\n\n if (this.isDragging) return;\n if (!this.autoplay) return;\n this.autoPlayTimeoutId = window.setTimeout(() => {\n this.play();\n this.autoPlayTimeoutId = null;\n }, this.resumeDelay);\n }\n\n scrollToIndex(index: number) {\n this.pause();\n if (index >= 0 && index < this.snapPoints.length) {\n this.scrollToLogical(this.snapPoints[index], \"smooth\");\n }\n }\n\n getState() {\n return {\n ...this.state,\n visibleItemIndexes: [...this.state.visibleItemIndexes],\n };\n }\n\n getOptions() {\n return {\n autoplay: this.autoplay,\n rtl: this.rtl,\n delay: this.delay,\n resumeDelay: this.resumeDelay,\n breakpoints: this.breakpoints.map((bp) => ({ ...bp })),\n };\n }\n\n update() {\n // Disconnect existing intersection observer\n if (this.intersectionObserver) {\n this.intersectionObserver.disconnect();\n this.intersectionObserver = null;\n }\n\n // Update total items count\n this.state.totalItems = this.element.querySelectorAll(\n \"[data-motionrail-grid] > *\",\n ).length;\n\n // Reset visible items state\n this.state.visibleItemIndexes = [];\n this.state.isFirstItemVisible = false;\n this.state.isLastItemVisible = false;\n\n // Re-apply breakpoints with new item count\n this.setBreakPoints();\n\n // Recache snap points with new items\n this.cacheSnapPoints();\n\n // Re-observe edge items\n this.observeEdgeItems();\n\n // Notify state change\n this.handleStateChange();\n }\n\n destroy() {\n const state = this.getState();\n this.extensions.forEach((ext) => {\n if (ext.onDestroy) {\n ext.onDestroy(this, state);\n }\n });\n\n if (this.autoPlayIntervalId) {\n clearInterval(this.autoPlayIntervalId);\n this.autoPlayIntervalId = null;\n }\n\n if (this.cancelScroll) {\n this.cancelScroll();\n this.cancelScroll = null;\n }\n\n if (this.autoPlayTimeoutId) {\n clearTimeout(this.autoPlayTimeoutId);\n this.autoPlayTimeoutId = null;\n }\n\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n\n if (this.intersectionObserver) {\n this.intersectionObserver.disconnect();\n this.intersectionObserver = null;\n }\n\n this.scrollable.removeEventListener(\"pointerdown\", this.handlePointerDown);\n this.scrollable.removeEventListener(\"pointermove\", this.handlePointerMove);\n this.scrollable.removeEventListener(\"pointerup\", this.handlePointerUp);\n this.scrollable.removeEventListener(\"pointerleave\", this.handlePointerUp);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
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){e.MotionRail=class{element;scrollable;rtl=!1;autoplay=!1;delay=3e3;resumeDelay=4e3;onChange;handleStateChange=()=>{let e=this.getState();this.extensions.forEach(t=>{t.onUpdate&&t.onUpdate(this,e)}),this.onChange&&this.onChange(e)};extensions=[];breakpoints=[];autoPlayIntervalId=null;autoPlayTimeoutId=null;isDragging=!1;startX=0;startLogicalScroll=0;cancelScroll=null;lastPointerX=0;lastPointerTime=0;velocity=0;pointerId=null;snapPoints=[];resizeObserver=null;intersectionObserver=null;state={totalItems:0,visibleItemIndexes:[],isFirstItemVisible:!1,isLastItemVisible:!1};constructor(e,t){this.autoplay=t.autoplay||!1,this.rtl=t.rtl||!1,this.breakpoints=t.breakpoints||[{columns:1,gap:`0px`}],this.element=e,this.extensions=t.extensions||[];let n=this.element.querySelector(`[data-motionrail-scrollable]`);if(!n)throw Error(`MotionRail: [data-motionrail-scrollable] element not found`);this.scrollable=n,this.delay=t.delay||3e3,this.resumeDelay=t.resumeDelay||4e3,this.onChange=t.onChange,this.state.totalItems=this.element.querySelectorAll(`[data-motionrail-grid] > *`).length,this.setBreakPoints(),this.setLogicalScroll(0),this.attachPointerEvents(),this.cacheSnapPoints(),this.observeResize(),this.observeEdgeItems(),this.autoplay&&this.play();let r=this.getState();this.extensions.forEach(e=>{e.onInit&&e.onInit(this,r)})}randomContainerName(){return`motion-rail-${Math.random().toString(36).substring(2,11)}`}setBreakPoints(){let e=this.element.querySelector(`[data-motionrail-scrollable]`);if(!e)return;let t=``;e.style.containerName?t=e.style.containerName:(t=this.randomContainerName(),e.style.containerName=t);let n=document.createElement(`style`),r=``,i=this.breakpoints.filter(e=>e.width),a=i.length>0?Math.min(...i.map(e=>e.width)):null;return this.breakpoints.forEach(e=>{let n=e.columns||1,i=e.gap||`0px`,o=`calc((100cqw - (${n-1} * ${i})) / ${n})`,s=``;s=e.width?`(min-width: ${e.width}px)`:a?`(max-width: ${a-1}px)`:`(min-width: 0px)`,r+=`
|
|
2
2
|
@container ${t} ${s} {
|
|
3
|
-
[data-motionrail-grid] {
|
|
4
|
-
grid-template-columns: repeat(${this.state.totalItems}, ${o});
|
|
5
|
-
gap: ${i};
|
|
3
|
+
[data-motionrail] [data-motionrail-scrollable] [data-motionrail-grid] {
|
|
4
|
+
grid-template-columns: repeat(${this.state.totalItems}, ${o}) !important;
|
|
5
|
+
gap: ${i} !important;
|
|
6
|
+
opacity: 1 !important;
|
|
6
7
|
}
|
|
7
8
|
}
|
|
8
9
|
`}),n.textContent=r,document.head.appendChild(n),n}getLogicalScroll(){return this.scrollable.scrollLeft}setLogicalScroll(e){this.scrollable.scrollLeft=e}scrollToLogical(e,t=`auto`){this.scrollable.scrollTo({left:e,behavior:t})}observeResize(){typeof ResizeObserver>`u`||(this.resizeObserver=new ResizeObserver(()=>{this.cacheSnapPoints()}),this.resizeObserver.observe(this.scrollable))}observeEdgeItems(){if(typeof IntersectionObserver>`u`)return;let e=this.element.querySelectorAll(`[data-motionrail-grid] > *`);if(e.length===0)return;let t=e[0],n=e[e.length-1];this.intersectionObserver=new IntersectionObserver(r=>{let i=!1;r.forEach(r=>{let a=Array.from(e).indexOf(r.target);if(a!==-1){if(r.isIntersecting)this.state.visibleItemIndexes.includes(a)||(this.state.visibleItemIndexes.push(a),this.state.visibleItemIndexes.sort((e,t)=>e-t),i=!0);else{let e=this.state.visibleItemIndexes.length;this.state.visibleItemIndexes=this.state.visibleItemIndexes.filter(e=>e!==a),this.state.visibleItemIndexes.length!==e&&(i=!0)}if(r.target===t){let e=r.isIntersecting;this.state.isFirstItemVisible!==e&&(this.state.isFirstItemVisible=e,i=!0)}else if(r.target===n){let e=r.isIntersecting;this.state.isLastItemVisible!==e&&(this.state.isLastItemVisible=e,i=!0)}}}),i&&this.handleStateChange()},{root:this.scrollable,threshold:.5}),e.forEach(e=>this.intersectionObserver.observe(e))}cacheSnapPoints(){let e=this.element.querySelectorAll(`[data-motionrail-grid] > *`),t=this.scrollable.scrollWidth-this.scrollable.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}attachPointerEvents(){window.matchMedia(`(pointer: fine)`).matches&&(this.scrollable.addEventListener(`pointerdown`,this.handlePointerDown),this.scrollable.addEventListener(`pointermove`,this.handlePointerMove),this.scrollable.addEventListener(`pointerup`,this.handlePointerUp),this.scrollable.addEventListener(`pointerleave`,this.handlePointerUp))}handlePointerDown=e=>{this.pointerId===null&&(this.pointerId=e.pointerId,this.scrollable.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.scrollable.style.userSelect=`none`,this.scrollable.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.scrollable.releasePointerCapture(e.pointerId),this.pointerId=null,this.isDragging=!1,this.scrollable.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.cancelScroll=this.animateLogicalScroll(a,n,()=>{this.scrollable.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){if(this.state.visibleItemIndexes.length===0)return;let t,n=this.state.visibleItemIndexes[0],r=this.state.visibleItemIndexes[this.state.visibleItemIndexes.length-1],i=this.state.visibleItemIndexes.length;e===1?(t=r+1,this.rtl&&(t=r+i),t>=this.snapPoints.length-1&&this.state.isLastItemVisible?t=0:t>=this.snapPoints.length-1&&!this.state.isLastItemVisible&&(t=this.snapPoints.length-1)):(t=n-i,this.rtl&&(t=r-i),t<=0&&this.state.isFirstItemVisible?t=this.snapPoints.length-1:t<=0&&!this.state.isFirstItemVisible&&(t=0)),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.autoPlayIntervalId&&=(clearInterval(this.autoPlayIntervalId),null),this.autoPlayTimeoutId&&=(clearTimeout(this.autoPlayTimeoutId),null),!this.isDragging&&this.autoplay&&(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`)}getState(){return{...this.state,visibleItemIndexes:[...this.state.visibleItemIndexes]}}getOptions(){return{autoplay:this.autoplay,rtl:this.rtl,delay:this.delay,resumeDelay:this.resumeDelay,breakpoints:this.breakpoints.map(e=>({...e}))}}update(){this.intersectionObserver&&=(this.intersectionObserver.disconnect(),null),this.state.totalItems=this.element.querySelectorAll(`[data-motionrail-grid] > *`).length,this.state.visibleItemIndexes=[],this.state.isFirstItemVisible=!1,this.state.isLastItemVisible=!1,this.setBreakPoints(),this.cacheSnapPoints(),this.observeEdgeItems(),this.handleStateChange()}destroy(){let e=this.getState();this.extensions.forEach(t=>{t.onDestroy&&t.onDestroy(this,e)}),this.autoPlayIntervalId&&=(clearInterval(this.autoPlayIntervalId),null),this.cancelScroll&&=(this.cancelScroll(),null),this.autoPlayTimeoutId&&=(clearTimeout(this.autoPlayTimeoutId),null),this.resizeObserver&&=(this.resizeObserver.disconnect(),null),this.intersectionObserver&&=(this.intersectionObserver.disconnect(),null),this.scrollable.removeEventListener(`pointerdown`,this.handlePointerDown),this.scrollable.removeEventListener(`pointermove`,this.handlePointerMove),this.scrollable.removeEventListener(`pointerup`,this.handlePointerUp),this.scrollable.removeEventListener(`pointerleave`,this.handlePointerUp)}}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"motionrail.umd.js","names":["targetIndex: number"],"sources":["../src/lib/main.ts"],"sourcesContent":["import type {\n MotionRailBreakpoint,\n MotionRailExtension,\n MotionRailOptions,\n MotionRailState,\n} from \"./types\";\n\nexport class MotionRail {\n readonly element: HTMLElement;\n readonly scrollable: HTMLElement;\n\n private rtl: boolean = false;\n private autoplay: boolean = false;\n private delay: number = 3000;\n private resumeDelay: number = 4000;\n private onChange?: (state: MotionRailState) => void;\n\n private handleStateChange = () => {\n const state = this.getState();\n this.extensions.forEach((ext) => {\n if (ext.onUpdate) {\n ext.onUpdate(this, state);\n }\n });\n\n if (this.onChange) {\n this.onChange(state);\n }\n };\n\n private extensions: MotionRailExtension[] = [];\n\n private breakpoints: MotionRailBreakpoint[] = [];\n private autoPlayIntervalId: number | null = null;\n private autoPlayTimeoutId: number | null = null;\n private isDragging: boolean = false;\n private startX: number = 0;\n private startLogicalScroll: number = 0;\n private cancelScroll: (() => void) | null = null;\n private lastPointerX: number = 0;\n private lastPointerTime: number = 0;\n private velocity: number = 0;\n private pointerId: number | null = null;\n private snapPoints: number[] = [];\n private resizeObserver: ResizeObserver | null = null;\n private intersectionObserver: IntersectionObserver | null = null;\n\n private state: MotionRailState = {\n totalItems: 0,\n visibleItemIndexes: [],\n isFirstItemVisible: false,\n isLastItemVisible: false,\n };\n\n constructor(element: HTMLElement, options: MotionRailOptions) {\n this.autoplay = options.autoplay || false;\n this.rtl = options.rtl || false;\n this.breakpoints = options.breakpoints || [{ columns: 1, gap: \"0px\" }];\n this.element = element;\n this.extensions = options.extensions || [];\n\n const container = this.element.querySelector(\n \"[data-motionrail-scrollable]\",\n ) as HTMLElement;\n if (!container) {\n throw new Error(\n \"MotionRail: [data-motionrail-scrollable] element not found\",\n );\n }\n this.scrollable = container;\n\n this.delay = options.delay || 3000;\n this.resumeDelay = options.resumeDelay || 4000;\n this.onChange = options.onChange;\n this.state.totalItems = this.element.querySelectorAll(\n \"[data-motionrail-grid] > *\",\n ).length;\n\n this.setBreakPoints();\n\n this.setLogicalScroll(0);\n\n this.attachPointerEvents();\n this.cacheSnapPoints();\n this.observeResize();\n this.observeEdgeItems();\n if (this.autoplay) this.play();\n\n const state = this.getState();\n this.extensions.forEach((ext) => {\n if (ext.onInit) {\n ext.onInit(this, state);\n }\n });\n }\n\n // ============================================================================\n // UTILITY METHODS\n // ============================================================================\n\n private randomContainerName(): string {\n return `motion-rail-${Math.random().toString(36).substring(2, 11)}`;\n }\n\n private setBreakPoints() {\n const motionRailContainer = this.element.querySelector(\n \"[data-motionrail-scrollable]\",\n ) as HTMLElement;\n if (!motionRailContainer) return;\n\n // give random css-safe container-name\n let randomName = \"\";\n if (!motionRailContainer.style.containerName) {\n randomName = this.randomContainerName();\n motionRailContainer.style.containerName = randomName;\n } else {\n randomName = motionRailContainer.style.containerName;\n }\n\n // setup container query\n const styleElement = document.createElement(\"style\");\n let containerQueries = \"\";\n\n // Find the smallest width for base case max-width\n const withWidth = this.breakpoints.filter((bp) => bp.width);\n const smallestWidth =\n withWidth.length > 0\n ? Math.min(...withWidth.map((bp) => bp.width!))\n : null;\n\n this.breakpoints.forEach((bp) => {\n const columns = bp.columns || 1;\n const gapValue = bp.gap || \"0px\";\n const itemWidth = `calc((100cqw - (${columns - 1} * ${gapValue})) / ${columns})`;\n\n let condition = \"\";\n if (bp.width) {\n condition = `(min-width: ${bp.width}px)`;\n } else if (smallestWidth) {\n condition = `(max-width: ${smallestWidth - 1}px)`;\n } else {\n // Single breakpoint with no width\n condition = `(min-width: 0px)`;\n }\n\n containerQueries += `\n @container ${randomName} ${condition} {\n [data-motionrail-grid] {\n grid-template-columns: repeat(${this.state.totalItems}, ${itemWidth});\n gap: ${gapValue};\n }\n }\n `;\n });\n styleElement.textContent = containerQueries;\n document.head.appendChild(styleElement);\n\n return styleElement;\n }\n\n // ============================================================================\n // CORE LOGIC: All operations in logical space\n // ============================================================================\n\n private getLogicalScroll(): number {\n return this.scrollable.scrollLeft;\n }\n\n private setLogicalScroll(logicalScroll: number): void {\n this.scrollable.scrollLeft = logicalScroll;\n }\n\n private scrollToLogical(\n logicalScroll: number,\n behavior: ScrollBehavior = \"auto\",\n ): void {\n this.scrollable.scrollTo({ left: logicalScroll, behavior });\n }\n\n private observeResize() {\n if (typeof ResizeObserver === \"undefined\") return;\n\n this.resizeObserver = new ResizeObserver(() => {\n this.cacheSnapPoints();\n });\n\n this.resizeObserver.observe(this.scrollable);\n }\n\n private observeEdgeItems() {\n if (typeof IntersectionObserver === \"undefined\") return;\n\n const items = this.element.querySelectorAll(\"[data-motionrail-grid] > *\");\n if (items.length === 0) return;\n\n const firstItem = items[0];\n const lastItem = items[items.length - 1];\n\n this.intersectionObserver = new IntersectionObserver(\n (entries) => {\n let stateChanged = false;\n entries.forEach((entry) => {\n const index = Array.from(items).indexOf(entry.target as Element);\n if (index === -1) return;\n\n if (entry.isIntersecting) {\n if (!this.state.visibleItemIndexes.includes(index)) {\n this.state.visibleItemIndexes.push(index);\n this.state.visibleItemIndexes.sort((a, b) => a - b);\n stateChanged = true;\n }\n } else {\n const prevLength = this.state.visibleItemIndexes.length;\n this.state.visibleItemIndexes =\n this.state.visibleItemIndexes.filter((i) => i !== index);\n if (this.state.visibleItemIndexes.length !== prevLength) {\n stateChanged = true;\n }\n }\n\n // Update edge visibility flags\n if (entry.target === firstItem) {\n const newValue = entry.isIntersecting;\n if (this.state.isFirstItemVisible !== newValue) {\n this.state.isFirstItemVisible = newValue;\n stateChanged = true;\n }\n } else if (entry.target === lastItem) {\n const newValue = entry.isIntersecting;\n if (this.state.isLastItemVisible !== newValue) {\n this.state.isLastItemVisible = newValue;\n stateChanged = true;\n }\n }\n });\n\n if (stateChanged) {\n this.handleStateChange();\n }\n },\n {\n root: this.scrollable,\n threshold: 0.5,\n },\n );\n\n items.forEach((item) => this.intersectionObserver!.observe(item));\n }\n\n private cacheSnapPoints() {\n const items = this.element.querySelectorAll(\n \"[data-motionrail-grid] > *\",\n ) as NodeListOf<HTMLElement>;\n const maxScroll = this.scrollable.scrollWidth - this.scrollable.clientWidth;\n\n this.snapPoints = Array.from(items).map((item) => {\n // Snap points are stored in logical space (0-based, increasing forward)\n return Math.min(item.offsetLeft, maxScroll);\n });\n }\n\n private findNearestSnapPoint(logicalScroll: number): number {\n let nearestPoint = 0;\n let minDistance = Infinity;\n\n for (const point of this.snapPoints) {\n const distance = Math.abs(point - logicalScroll);\n if (distance < minDistance) {\n minDistance = distance;\n nearestPoint = point;\n }\n }\n\n return nearestPoint;\n }\n\n private attachPointerEvents() {\n // only on pointer: fine\n if (!window.matchMedia(\"(pointer: fine)\").matches) return;\n this.scrollable.addEventListener(\"pointerdown\", this.handlePointerDown);\n this.scrollable.addEventListener(\"pointermove\", this.handlePointerMove);\n this.scrollable.addEventListener(\"pointerup\", this.handlePointerUp);\n this.scrollable.addEventListener(\"pointerleave\", this.handlePointerUp);\n }\n\n private handlePointerDown = (e: PointerEvent) => {\n if (this.pointerId !== null) return;\n\n this.pointerId = e.pointerId;\n this.scrollable.setPointerCapture(e.pointerId);\n this.isDragging = true;\n this.startX = e.clientX;\n this.startLogicalScroll = this.getLogicalScroll();\n this.lastPointerX = e.clientX;\n this.lastPointerTime = e.timeStamp;\n this.velocity = 0;\n\n this.scrollable.style.userSelect = \"none\";\n this.scrollable.style.scrollSnapType = \"none\";\n this.pause();\n if (this.cancelScroll) {\n this.cancelScroll();\n }\n if (this.autoPlayTimeoutId) clearTimeout(this.autoPlayTimeoutId);\n };\n\n private handlePointerMove = (e: PointerEvent) => {\n if (!this.isDragging || e.pointerId !== this.pointerId) return;\n e.preventDefault();\n\n const deltaX = e.clientX - this.startX;\n const logicalScroll = this.startLogicalScroll - deltaX;\n this.setLogicalScroll(logicalScroll);\n\n const deltaTime = e.timeStamp - this.lastPointerTime;\n if (deltaTime > 0) {\n const pointerDelta = e.clientX - this.lastPointerX;\n this.velocity = pointerDelta / deltaTime;\n this.lastPointerX = e.clientX;\n this.lastPointerTime = e.timeStamp;\n }\n };\n\n private handlePointerUp = (e: PointerEvent) => {\n if (!this.isDragging || e.pointerId !== this.pointerId) return;\n\n this.scrollable.releasePointerCapture(e.pointerId);\n this.pointerId = null;\n this.isDragging = false;\n\n this.scrollable.style.userSelect = \"\";\n\n const velocityMagnitude = Math.abs(this.velocity);\n const baseTime = 100;\n const maxTime = 200;\n const momentumTime = Math.min(\n baseTime + Math.sqrt(velocityMagnitude) * 50,\n maxTime,\n );\n const momentum = -this.velocity * momentumTime;\n\n const currentLogicalScroll = this.getLogicalScroll();\n const targetLogicalScroll = currentLogicalScroll + momentum;\n\n if (this.cancelScroll) {\n this.cancelScroll();\n }\n\n const snapPoint = this.findNearestSnapPoint(targetLogicalScroll);\n\n const onScrollEnd = () => {\n this.scrollable.style.scrollSnapType = \"x mandatory\";\n this.cancelScroll = null;\n if (this.autoplay) {\n this.autoPlayTimeoutId = window.setTimeout(() => {\n this.play();\n this.autoPlayTimeoutId = null;\n }, this.resumeDelay);\n }\n };\n\n // Create a wrapper that animates in logical space\n this.cancelScroll = this.animateLogicalScroll(\n snapPoint,\n momentumTime,\n onScrollEnd,\n );\n this.velocity = 0;\n };\n\n private animateLogicalScroll(\n targetLogical: number,\n duration: number,\n onComplete: () => void,\n ): () => void {\n const startLogical = this.getLogicalScroll();\n const startTime = performance.now();\n let cancelled = false;\n\n const animate = (currentTime: number) => {\n if (cancelled) return;\n\n const elapsed = currentTime - startTime;\n const progress = Math.min(elapsed / duration, 1);\n const eased = 1 - Math.pow(1 - progress, 3);\n const currentLogical =\n startLogical + (targetLogical - startLogical) * eased;\n\n this.setLogicalScroll(currentLogical);\n\n if (progress < 1) {\n requestAnimationFrame(animate);\n } else {\n onComplete();\n }\n };\n\n requestAnimationFrame(animate);\n return () => {\n cancelled = true;\n };\n }\n\n private scrollByPage(direction: 1 | -1) {\n if (this.state.visibleItemIndexes.length === 0) return;\n\n let targetIndex: number;\n const firstVisibleIndex = this.state.visibleItemIndexes[0];\n const lastVisibleIndex =\n this.state.visibleItemIndexes[this.state.visibleItemIndexes.length - 1];\n const visibleCount = this.state.visibleItemIndexes.length;\n\n if (direction === 1) {\n // Going forward: skip all visible items, go to the next one\n targetIndex = lastVisibleIndex + 1;\n if (this.rtl) {\n targetIndex = lastVisibleIndex + visibleCount;\n }\n\n if (\n targetIndex >= this.snapPoints.length - 1 &&\n this.state.isLastItemVisible\n ) {\n targetIndex = 0;\n } else if (\n targetIndex >= this.snapPoints.length - 1 &&\n !this.state.isLastItemVisible\n ) {\n targetIndex = this.snapPoints.length - 1;\n }\n } else {\n // Going backward: skip all visible items, go to the previous one\n targetIndex = firstVisibleIndex - visibleCount;\n if (this.rtl) {\n targetIndex = lastVisibleIndex - visibleCount;\n }\n\n if (targetIndex <= 0 && this.state.isFirstItemVisible) {\n targetIndex = this.snapPoints.length - 1;\n } else if (targetIndex <= 0 && !this.state.isFirstItemVisible) {\n targetIndex = 0;\n }\n }\n\n this.scrollToLogical(this.snapPoints[targetIndex], \"smooth\");\n }\n\n play() {\n this.autoPlayIntervalId = window.setInterval(() => {\n this.scrollByPage(1);\n }, this.delay);\n }\n\n next() {\n this.pause();\n this.scrollByPage(1);\n }\n\n prev() {\n this.pause();\n this.scrollByPage(-1);\n }\n\n pause() {\n if (this.autoPlayIntervalId) {\n clearInterval(this.autoPlayIntervalId);\n this.autoPlayIntervalId = null;\n }\n\n if (this.autoPlayTimeoutId) {\n clearTimeout(this.autoPlayTimeoutId);\n this.autoPlayTimeoutId = null;\n }\n\n if (this.isDragging) return;\n if (!this.autoplay) return;\n this.autoPlayTimeoutId = window.setTimeout(() => {\n this.play();\n this.autoPlayTimeoutId = null;\n }, this.resumeDelay);\n }\n\n scrollToIndex(index: number) {\n this.pause();\n if (index >= 0 && index < this.snapPoints.length) {\n this.scrollToLogical(this.snapPoints[index], \"smooth\");\n }\n }\n\n getState() {\n return {\n ...this.state,\n visibleItemIndexes: [...this.state.visibleItemIndexes],\n };\n }\n\n getOptions() {\n return {\n autoplay: this.autoplay,\n rtl: this.rtl,\n delay: this.delay,\n resumeDelay: this.resumeDelay,\n breakpoints: this.breakpoints.map((bp) => ({ ...bp })),\n };\n }\n\n update() {\n // Disconnect existing intersection observer\n if (this.intersectionObserver) {\n this.intersectionObserver.disconnect();\n this.intersectionObserver = null;\n }\n\n // Update total items count\n this.state.totalItems = this.element.querySelectorAll(\n \"[data-motionrail-grid] > *\",\n ).length;\n\n // Reset visible items state\n this.state.visibleItemIndexes = [];\n this.state.isFirstItemVisible = false;\n this.state.isLastItemVisible = false;\n\n // Re-apply breakpoints with new item count\n this.setBreakPoints();\n\n // Recache snap points with new items\n this.cacheSnapPoints();\n\n // Re-observe edge items\n this.observeEdgeItems();\n\n // Notify state change\n this.handleStateChange();\n }\n\n destroy() {\n const state = this.getState();\n this.extensions.forEach((ext) => {\n if (ext.onDestroy) {\n ext.onDestroy(this, state);\n }\n });\n\n if (this.autoPlayIntervalId) {\n clearInterval(this.autoPlayIntervalId);\n this.autoPlayIntervalId = null;\n }\n\n if (this.cancelScroll) {\n this.cancelScroll();\n this.cancelScroll = null;\n }\n\n if (this.autoPlayTimeoutId) {\n clearTimeout(this.autoPlayTimeoutId);\n this.autoPlayTimeoutId = null;\n }\n\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n\n if (this.intersectionObserver) {\n this.intersectionObserver.disconnect();\n this.intersectionObserver = null;\n }\n\n this.scrollable.removeEventListener(\"pointerdown\", this.handlePointerDown);\n this.scrollable.removeEventListener(\"pointermove\", this.handlePointerMove);\n this.scrollable.removeEventListener(\"pointerup\", this.handlePointerUp);\n this.scrollable.removeEventListener(\"pointerleave\", this.handlePointerUp);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"motionrail.umd.js","names":["targetIndex: number"],"sources":["../src/lib/main.ts"],"sourcesContent":["import type {\n MotionRailBreakpoint,\n MotionRailExtension,\n MotionRailOptions,\n MotionRailState,\n} from \"./types\";\n\nexport class MotionRail {\n readonly element: HTMLElement;\n readonly scrollable: HTMLElement;\n\n private rtl: boolean = false;\n private autoplay: boolean = false;\n private delay: number = 3000;\n private resumeDelay: number = 4000;\n private onChange?: (state: MotionRailState) => void;\n\n private handleStateChange = () => {\n const state = this.getState();\n this.extensions.forEach((ext) => {\n if (ext.onUpdate) {\n ext.onUpdate(this, state);\n }\n });\n\n if (this.onChange) {\n this.onChange(state);\n }\n };\n\n private extensions: MotionRailExtension[] = [];\n\n private breakpoints: MotionRailBreakpoint[] = [];\n private autoPlayIntervalId: number | null = null;\n private autoPlayTimeoutId: number | null = null;\n private isDragging: boolean = false;\n private startX: number = 0;\n private startLogicalScroll: number = 0;\n private cancelScroll: (() => void) | null = null;\n private lastPointerX: number = 0;\n private lastPointerTime: number = 0;\n private velocity: number = 0;\n private pointerId: number | null = null;\n private snapPoints: number[] = [];\n private resizeObserver: ResizeObserver | null = null;\n private intersectionObserver: IntersectionObserver | null = null;\n\n private state: MotionRailState = {\n totalItems: 0,\n visibleItemIndexes: [],\n isFirstItemVisible: false,\n isLastItemVisible: false,\n };\n\n constructor(element: HTMLElement, options: MotionRailOptions) {\n this.autoplay = options.autoplay || false;\n this.rtl = options.rtl || false;\n this.breakpoints = options.breakpoints || [{ columns: 1, gap: \"0px\" }];\n this.element = element;\n this.extensions = options.extensions || [];\n\n const container = this.element.querySelector(\n \"[data-motionrail-scrollable]\",\n ) as HTMLElement;\n if (!container) {\n throw new Error(\n \"MotionRail: [data-motionrail-scrollable] element not found\",\n );\n }\n this.scrollable = container;\n\n this.delay = options.delay || 3000;\n this.resumeDelay = options.resumeDelay || 4000;\n this.onChange = options.onChange;\n this.state.totalItems = this.element.querySelectorAll(\n \"[data-motionrail-grid] > *\",\n ).length;\n\n this.setBreakPoints();\n\n this.setLogicalScroll(0);\n\n this.attachPointerEvents();\n this.cacheSnapPoints();\n this.observeResize();\n this.observeEdgeItems();\n if (this.autoplay) this.play();\n\n const state = this.getState();\n this.extensions.forEach((ext) => {\n if (ext.onInit) {\n ext.onInit(this, state);\n }\n });\n }\n\n // ============================================================================\n // UTILITY METHODS\n // ============================================================================\n\n private randomContainerName(): string {\n return `motion-rail-${Math.random().toString(36).substring(2, 11)}`;\n }\n\n private setBreakPoints() {\n const motionRailContainer = this.element.querySelector(\n \"[data-motionrail-scrollable]\",\n ) as HTMLElement;\n if (!motionRailContainer) return;\n\n // give random css-safe container-name\n let randomName = \"\";\n if (!motionRailContainer.style.containerName) {\n randomName = this.randomContainerName();\n motionRailContainer.style.containerName = randomName;\n } else {\n randomName = motionRailContainer.style.containerName;\n }\n\n // setup container query\n const styleElement = document.createElement(\"style\");\n let containerQueries = \"\";\n\n // Find the smallest width for base case max-width\n const withWidth = this.breakpoints.filter((bp) => bp.width);\n const smallestWidth =\n withWidth.length > 0\n ? Math.min(...withWidth.map((bp) => bp.width!))\n : null;\n\n this.breakpoints.forEach((bp) => {\n const columns = bp.columns || 1;\n const gapValue = bp.gap || \"0px\";\n const itemWidth = `calc((100cqw - (${columns - 1} * ${gapValue})) / ${columns})`;\n\n let condition = \"\";\n if (bp.width) {\n condition = `(min-width: ${bp.width}px)`;\n } else if (smallestWidth) {\n condition = `(max-width: ${smallestWidth - 1}px)`;\n } else {\n // Single breakpoint with no width\n condition = `(min-width: 0px)`;\n }\n\n containerQueries += `\n @container ${randomName} ${condition} {\n [data-motionrail] [data-motionrail-scrollable] [data-motionrail-grid] {\n grid-template-columns: repeat(${this.state.totalItems}, ${itemWidth}) !important;\n gap: ${gapValue} !important;\n opacity: 1 !important;\n }\n }\n `;\n });\n styleElement.textContent = containerQueries;\n document.head.appendChild(styleElement);\n\n return styleElement;\n }\n\n // ============================================================================\n // CORE LOGIC: All operations in logical space\n // ============================================================================\n\n private getLogicalScroll(): number {\n return this.scrollable.scrollLeft;\n }\n\n private setLogicalScroll(logicalScroll: number): void {\n this.scrollable.scrollLeft = logicalScroll;\n }\n\n private scrollToLogical(\n logicalScroll: number,\n behavior: ScrollBehavior = \"auto\",\n ): void {\n this.scrollable.scrollTo({ left: logicalScroll, behavior });\n }\n\n private observeResize() {\n if (typeof ResizeObserver === \"undefined\") return;\n\n this.resizeObserver = new ResizeObserver(() => {\n this.cacheSnapPoints();\n });\n\n this.resizeObserver.observe(this.scrollable);\n }\n\n private observeEdgeItems() {\n if (typeof IntersectionObserver === \"undefined\") return;\n\n const items = this.element.querySelectorAll(\"[data-motionrail-grid] > *\");\n if (items.length === 0) return;\n\n const firstItem = items[0];\n const lastItem = items[items.length - 1];\n\n this.intersectionObserver = new IntersectionObserver(\n (entries) => {\n let stateChanged = false;\n entries.forEach((entry) => {\n const index = Array.from(items).indexOf(entry.target as Element);\n if (index === -1) return;\n\n if (entry.isIntersecting) {\n if (!this.state.visibleItemIndexes.includes(index)) {\n this.state.visibleItemIndexes.push(index);\n this.state.visibleItemIndexes.sort((a, b) => a - b);\n stateChanged = true;\n }\n } else {\n const prevLength = this.state.visibleItemIndexes.length;\n this.state.visibleItemIndexes =\n this.state.visibleItemIndexes.filter((i) => i !== index);\n if (this.state.visibleItemIndexes.length !== prevLength) {\n stateChanged = true;\n }\n }\n\n // Update edge visibility flags\n if (entry.target === firstItem) {\n const newValue = entry.isIntersecting;\n if (this.state.isFirstItemVisible !== newValue) {\n this.state.isFirstItemVisible = newValue;\n stateChanged = true;\n }\n } else if (entry.target === lastItem) {\n const newValue = entry.isIntersecting;\n if (this.state.isLastItemVisible !== newValue) {\n this.state.isLastItemVisible = newValue;\n stateChanged = true;\n }\n }\n });\n\n if (stateChanged) {\n this.handleStateChange();\n }\n },\n {\n root: this.scrollable,\n threshold: 0.5,\n },\n );\n\n items.forEach((item) => this.intersectionObserver!.observe(item));\n }\n\n private cacheSnapPoints() {\n const items = this.element.querySelectorAll(\n \"[data-motionrail-grid] > *\",\n ) as NodeListOf<HTMLElement>;\n const maxScroll = this.scrollable.scrollWidth - this.scrollable.clientWidth;\n\n this.snapPoints = Array.from(items).map((item) => {\n // Snap points are stored in logical space (0-based, increasing forward)\n return Math.min(item.offsetLeft, maxScroll);\n });\n }\n\n private findNearestSnapPoint(logicalScroll: number): number {\n let nearestPoint = 0;\n let minDistance = Infinity;\n\n for (const point of this.snapPoints) {\n const distance = Math.abs(point - logicalScroll);\n if (distance < minDistance) {\n minDistance = distance;\n nearestPoint = point;\n }\n }\n\n return nearestPoint;\n }\n\n private attachPointerEvents() {\n // only on pointer: fine\n if (!window.matchMedia(\"(pointer: fine)\").matches) return;\n this.scrollable.addEventListener(\"pointerdown\", this.handlePointerDown);\n this.scrollable.addEventListener(\"pointermove\", this.handlePointerMove);\n this.scrollable.addEventListener(\"pointerup\", this.handlePointerUp);\n this.scrollable.addEventListener(\"pointerleave\", this.handlePointerUp);\n }\n\n private handlePointerDown = (e: PointerEvent) => {\n if (this.pointerId !== null) return;\n\n this.pointerId = e.pointerId;\n this.scrollable.setPointerCapture(e.pointerId);\n this.isDragging = true;\n this.startX = e.clientX;\n this.startLogicalScroll = this.getLogicalScroll();\n this.lastPointerX = e.clientX;\n this.lastPointerTime = e.timeStamp;\n this.velocity = 0;\n\n this.scrollable.style.userSelect = \"none\";\n this.scrollable.style.scrollSnapType = \"none\";\n this.pause();\n if (this.cancelScroll) {\n this.cancelScroll();\n }\n if (this.autoPlayTimeoutId) clearTimeout(this.autoPlayTimeoutId);\n };\n\n private handlePointerMove = (e: PointerEvent) => {\n if (!this.isDragging || e.pointerId !== this.pointerId) return;\n e.preventDefault();\n\n const deltaX = e.clientX - this.startX;\n const logicalScroll = this.startLogicalScroll - deltaX;\n this.setLogicalScroll(logicalScroll);\n\n const deltaTime = e.timeStamp - this.lastPointerTime;\n if (deltaTime > 0) {\n const pointerDelta = e.clientX - this.lastPointerX;\n this.velocity = pointerDelta / deltaTime;\n this.lastPointerX = e.clientX;\n this.lastPointerTime = e.timeStamp;\n }\n };\n\n private handlePointerUp = (e: PointerEvent) => {\n if (!this.isDragging || e.pointerId !== this.pointerId) return;\n\n this.scrollable.releasePointerCapture(e.pointerId);\n this.pointerId = null;\n this.isDragging = false;\n\n this.scrollable.style.userSelect = \"\";\n\n const velocityMagnitude = Math.abs(this.velocity);\n const baseTime = 100;\n const maxTime = 200;\n const momentumTime = Math.min(\n baseTime + Math.sqrt(velocityMagnitude) * 50,\n maxTime,\n );\n const momentum = -this.velocity * momentumTime;\n\n const currentLogicalScroll = this.getLogicalScroll();\n const targetLogicalScroll = currentLogicalScroll + momentum;\n\n if (this.cancelScroll) {\n this.cancelScroll();\n }\n\n const snapPoint = this.findNearestSnapPoint(targetLogicalScroll);\n\n const onScrollEnd = () => {\n this.scrollable.style.scrollSnapType = \"x mandatory\";\n this.cancelScroll = null;\n if (this.autoplay) {\n this.autoPlayTimeoutId = window.setTimeout(() => {\n this.play();\n this.autoPlayTimeoutId = null;\n }, this.resumeDelay);\n }\n };\n\n // Create a wrapper that animates in logical space\n this.cancelScroll = this.animateLogicalScroll(\n snapPoint,\n momentumTime,\n onScrollEnd,\n );\n this.velocity = 0;\n };\n\n private animateLogicalScroll(\n targetLogical: number,\n duration: number,\n onComplete: () => void,\n ): () => void {\n const startLogical = this.getLogicalScroll();\n const startTime = performance.now();\n let cancelled = false;\n\n const animate = (currentTime: number) => {\n if (cancelled) return;\n\n const elapsed = currentTime - startTime;\n const progress = Math.min(elapsed / duration, 1);\n const eased = 1 - Math.pow(1 - progress, 3);\n const currentLogical =\n startLogical + (targetLogical - startLogical) * eased;\n\n this.setLogicalScroll(currentLogical);\n\n if (progress < 1) {\n requestAnimationFrame(animate);\n } else {\n onComplete();\n }\n };\n\n requestAnimationFrame(animate);\n return () => {\n cancelled = true;\n };\n }\n\n private scrollByPage(direction: 1 | -1) {\n if (this.state.visibleItemIndexes.length === 0) return;\n\n let targetIndex: number;\n const firstVisibleIndex = this.state.visibleItemIndexes[0];\n const lastVisibleIndex =\n this.state.visibleItemIndexes[this.state.visibleItemIndexes.length - 1];\n const visibleCount = this.state.visibleItemIndexes.length;\n\n if (direction === 1) {\n // Going forward: skip all visible items, go to the next one\n targetIndex = lastVisibleIndex + 1;\n if (this.rtl) {\n targetIndex = lastVisibleIndex + visibleCount;\n }\n\n if (\n targetIndex >= this.snapPoints.length - 1 &&\n this.state.isLastItemVisible\n ) {\n targetIndex = 0;\n } else if (\n targetIndex >= this.snapPoints.length - 1 &&\n !this.state.isLastItemVisible\n ) {\n targetIndex = this.snapPoints.length - 1;\n }\n } else {\n // Going backward: skip all visible items, go to the previous one\n targetIndex = firstVisibleIndex - visibleCount;\n if (this.rtl) {\n targetIndex = lastVisibleIndex - visibleCount;\n }\n\n if (targetIndex <= 0 && this.state.isFirstItemVisible) {\n targetIndex = this.snapPoints.length - 1;\n } else if (targetIndex <= 0 && !this.state.isFirstItemVisible) {\n targetIndex = 0;\n }\n }\n\n this.scrollToLogical(this.snapPoints[targetIndex], \"smooth\");\n }\n\n play() {\n this.autoPlayIntervalId = window.setInterval(() => {\n this.scrollByPage(1);\n }, this.delay);\n }\n\n next() {\n this.pause();\n this.scrollByPage(1);\n }\n\n prev() {\n this.pause();\n this.scrollByPage(-1);\n }\n\n pause() {\n if (this.autoPlayIntervalId) {\n clearInterval(this.autoPlayIntervalId);\n this.autoPlayIntervalId = null;\n }\n\n if (this.autoPlayTimeoutId) {\n clearTimeout(this.autoPlayTimeoutId);\n this.autoPlayTimeoutId = null;\n }\n\n if (this.isDragging) return;\n if (!this.autoplay) return;\n this.autoPlayTimeoutId = window.setTimeout(() => {\n this.play();\n this.autoPlayTimeoutId = null;\n }, this.resumeDelay);\n }\n\n scrollToIndex(index: number) {\n this.pause();\n if (index >= 0 && index < this.snapPoints.length) {\n this.scrollToLogical(this.snapPoints[index], \"smooth\");\n }\n }\n\n getState() {\n return {\n ...this.state,\n visibleItemIndexes: [...this.state.visibleItemIndexes],\n };\n }\n\n getOptions() {\n return {\n autoplay: this.autoplay,\n rtl: this.rtl,\n delay: this.delay,\n resumeDelay: this.resumeDelay,\n breakpoints: this.breakpoints.map((bp) => ({ ...bp })),\n };\n }\n\n update() {\n // Disconnect existing intersection observer\n if (this.intersectionObserver) {\n this.intersectionObserver.disconnect();\n this.intersectionObserver = null;\n }\n\n // Update total items count\n this.state.totalItems = this.element.querySelectorAll(\n \"[data-motionrail-grid] > *\",\n ).length;\n\n // Reset visible items state\n this.state.visibleItemIndexes = [];\n this.state.isFirstItemVisible = false;\n this.state.isLastItemVisible = false;\n\n // Re-apply breakpoints with new item count\n this.setBreakPoints();\n\n // Recache snap points with new items\n this.cacheSnapPoints();\n\n // Re-observe edge items\n this.observeEdgeItems();\n\n // Notify state change\n this.handleStateChange();\n }\n\n destroy() {\n const state = this.getState();\n this.extensions.forEach((ext) => {\n if (ext.onDestroy) {\n ext.onDestroy(this, state);\n }\n });\n\n if (this.autoPlayIntervalId) {\n clearInterval(this.autoPlayIntervalId);\n this.autoPlayIntervalId = null;\n }\n\n if (this.cancelScroll) {\n this.cancelScroll();\n this.cancelScroll = null;\n }\n\n if (this.autoPlayTimeoutId) {\n clearTimeout(this.autoPlayTimeoutId);\n this.autoPlayTimeoutId = null;\n }\n\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n\n if (this.intersectionObserver) {\n this.intersectionObserver.disconnect();\n this.intersectionObserver = null;\n }\n\n this.scrollable.removeEventListener(\"pointerdown\", this.handlePointerDown);\n this.scrollable.removeEventListener(\"pointermove\", this.handlePointerMove);\n this.scrollable.removeEventListener(\"pointerup\", this.handlePointerUp);\n this.scrollable.removeEventListener(\"pointerleave\", this.handlePointerUp);\n }\n}\n"],"mappings":"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"}
|
package/main/dist/style.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
[data-motionrail-scrollable]::-webkit-scrollbar{display:none}[data-motionrail]{position:relative}[data-motionrail] [data-motionrail-scrollable]{position:relative;overflow:hidden}@supports (container-type:inline-size){[data-motionrail] [data-motionrail-scrollable]{container-type:inline-size}}@supports (anchor-name:--motionrail-scrollable){[data-motionrail] [data-motionrail-scrollable]{anchor-name:--motionrail-scrollable}}[data-motionrail] [data-motionrail-scrollable]{scroll-snap-type:x mandatory;scrollbar-width:none;-ms-overflow-style:none;cursor:grab;overflow-x:scroll}[data-motionrail] [data-motionrail-scrollable]:active{cursor:grabbing}[data-motionrail] [data-motionrail-scrollable] [data-motionrail-grid]{grid-auto-flow:column;display:grid}[data-motionrail] [data-motionrail-scrollable] [data-motionrail-grid]>*{pointer-events:none;scroll-snap-align:start}
|
|
1
|
+
[data-motionrail-scrollable]::-webkit-scrollbar{display:none}[data-motionrail]{position:relative}[data-motionrail] [data-motionrail-scrollable]{position:relative;overflow:hidden}@supports (container-type:inline-size){[data-motionrail] [data-motionrail-scrollable]{container-type:inline-size}}@supports (anchor-name:--motionrail-scrollable){[data-motionrail] [data-motionrail-scrollable]{anchor-name:--motionrail-scrollable}}[data-motionrail] [data-motionrail-scrollable]{scroll-snap-type:x mandatory;scrollbar-width:none;-ms-overflow-style:none;cursor:grab;overflow-x:scroll}[data-motionrail] [data-motionrail-scrollable]:active{cursor:grabbing}[data-motionrail] [data-motionrail-scrollable] [data-motionrail-grid]{opacity:0;grid-template-columns:100%;grid-auto-flow:column;gap:0;display:grid}[data-motionrail] [data-motionrail-scrollable] [data-motionrail-grid]>*{pointer-events:none;scroll-snap-align:start}
|
|
2
2
|
/*$vite$:1*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "motionrail",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./main/dist/motionrail.umd.js",
|
|
6
6
|
"module": "./main/dist/motionrail.es.js",
|
|
@@ -107,12 +107,18 @@
|
|
|
107
107
|
"type": "git",
|
|
108
108
|
"url": "https://github.com/juji/motionrail"
|
|
109
109
|
},
|
|
110
|
+
"devDependencies": {
|
|
111
|
+
"husky": "^9.1.7"
|
|
112
|
+
},
|
|
110
113
|
"scripts": {
|
|
111
114
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
112
|
-
"
|
|
115
|
+
"install:prod": "pnpm install --filter '!./manual/*'",
|
|
116
|
+
"build": "pnpm build:main && pnpm build:libs",
|
|
113
117
|
"build:main": "pnpm --filter main build",
|
|
114
118
|
"build:libs": "pnpm --parallel --filter './extensions/*' --filter './frameworks/*' build",
|
|
115
119
|
"build:rest": "pnpm --parallel --filter '!main' --filter '!./extensions/*' --filter '!./frameworks/*' build",
|
|
120
|
+
"build:local": "pnpm build:main && pnpm build:libs && pnpm build:rest",
|
|
121
|
+
"build:doc": "pnpm build:main && pnpm build:libs && pnpm --filter doc build",
|
|
116
122
|
"dev": "pnpm --parallel dev"
|
|
117
123
|
}
|
|
118
124
|
}
|