scroll-to-future 1.0.0

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/dist/index.mjs ADDED
@@ -0,0 +1,1770 @@
1
+ "use client";
2
+
3
+ // src/ScrollToFuture.tsx
4
+ import { useRef as useRef3, useState as useState6 } from "react";
5
+
6
+ // src/components/Axios/ScrollAxis.tsx
7
+ import { useRef, useState } from "react";
8
+
9
+ // src/utils/constants.ts
10
+ var MIN_THUMB_SIZE = 24;
11
+ var DEFAULT_TRACK_THICKNESS = 8;
12
+ var MAX_THUMB_RATIO = 0.8;
13
+ var EMPTY_AXIS_METRICS = {
14
+ scrollSize: 0,
15
+ clientSize: 0,
16
+ scrollPos: 0,
17
+ canScroll: false
18
+ };
19
+
20
+ // src/utils/helper.ts
21
+ var clamp = (value, min, max) => Math.min(Math.max(value, min), Math.max(min, max));
22
+ var parsePxValue = (value) => {
23
+ if (!value) return null;
24
+ const match = /^(-?\d+(?:\.\d+)?)px$/.exec(value.trim());
25
+ return match ? Number(match[1]) : null;
26
+ };
27
+ var parseBoundaryOffset = (value) => {
28
+ if (!value) {
29
+ return {
30
+ start: 0,
31
+ end: 0
32
+ };
33
+ }
34
+ const parts = value.trim().split(/\s+/);
35
+ const start = Math.max(0, parsePxValue(parts[0]) ?? 0);
36
+ const end = parts.length > 1 ? Math.max(0, parsePxValue(parts[1]) ?? 0) : start;
37
+ return {
38
+ start,
39
+ end
40
+ };
41
+ };
42
+ var fitInsetsWithinSize = (start, end, size, minInnerSize = 1) => {
43
+ const safeSize = Math.max(0, size);
44
+ if (safeSize <= 0) {
45
+ return {
46
+ start: 0,
47
+ end: 0,
48
+ innerSize: 0
49
+ };
50
+ }
51
+ const actualMinInnerSize = Math.min(Math.max(0, minInnerSize), safeSize);
52
+ const safeStart = Math.max(0, start);
53
+ const safeEnd = Math.max(0, end);
54
+ const requestedInsetsSize = safeStart + safeEnd;
55
+ const maximumInsetsSize = safeSize - actualMinInnerSize;
56
+ if (requestedInsetsSize <= maximumInsetsSize) {
57
+ return {
58
+ start: safeStart,
59
+ end: safeEnd,
60
+ innerSize: safeSize - safeStart - safeEnd
61
+ };
62
+ }
63
+ if (requestedInsetsSize <= 0) {
64
+ return {
65
+ start: 0,
66
+ end: 0,
67
+ innerSize: safeSize
68
+ };
69
+ }
70
+ const scale = maximumInsetsSize / requestedInsetsSize;
71
+ const fittedStart = safeStart * scale;
72
+ const fittedEnd = safeEnd * scale;
73
+ return {
74
+ start: fittedStart,
75
+ end: fittedEnd,
76
+ innerSize: Math.max(
77
+ actualMinInnerSize,
78
+ safeSize - fittedStart - fittedEnd
79
+ )
80
+ };
81
+ };
82
+ var resolveTrackLength = (value, containerSize) => {
83
+ if (!value) return containerSize;
84
+ const px = parsePxValue(value);
85
+ if (px !== null) return px;
86
+ const percentMatch = /^(-?\d+(?:\.\d+)?)%$/.exec(value);
87
+ if (percentMatch) return Number(percentMatch[1]) / 100 * containerSize;
88
+ const vhMatch = /^(-?\d+(?:\.\d+)?)vh$/.exec(value);
89
+ if (vhMatch) {
90
+ const vh = typeof window !== "undefined" ? window.innerHeight : containerSize;
91
+ return Number(vhMatch[1]) / 100 * vh;
92
+ }
93
+ const dvhMatch = /^(-?\d+(?:\.\d+)?)d(s)?vh$/.exec(value);
94
+ if (dvhMatch) {
95
+ const dvh = typeof window !== "undefined" ? window.visualViewport?.height ?? window.innerHeight : containerSize;
96
+ return Number(dvhMatch[1]) / 100 * dvh;
97
+ }
98
+ return containerSize;
99
+ };
100
+ var computeThumbSize = (trackLength, metrics, thumbHeightTrack) => {
101
+ if (trackLength <= 0) return 0;
102
+ const minSize = Math.min(MIN_THUMB_SIZE, trackLength);
103
+ const maxSize = Math.min(
104
+ trackLength,
105
+ Math.max(minSize, trackLength * MAX_THUMB_RATIO)
106
+ );
107
+ if (!thumbHeightTrack || thumbHeightTrack === "auto") {
108
+ const ratio = metrics.scrollSize > 0 ? metrics.clientSize / metrics.scrollSize : 1;
109
+ const autoSize = trackLength * clamp(ratio, 0, 1);
110
+ return clamp(autoSize, minSize, maxSize);
111
+ }
112
+ const px = parsePxValue(thumbHeightTrack);
113
+ if (px !== null) {
114
+ return clamp(px, minSize, maxSize);
115
+ }
116
+ const percentMatch = /^(-?\d+(?:\.\d+)?)%$/.exec(thumbHeightTrack.trim());
117
+ if (percentMatch) {
118
+ const size = Number(percentMatch[1]) / 100 * trackLength;
119
+ return clamp(size, minSize, maxSize);
120
+ }
121
+ return maxSize;
122
+ };
123
+ var computeThumbPosition = (trackLength, thumbSize, metrics) => {
124
+ const maxScroll = metrics.scrollSize - metrics.clientSize;
125
+ const maxThumbTravel = trackLength - thumbSize;
126
+ if (maxScroll <= 0 || maxThumbTravel <= 0) return 0;
127
+ const ratio = clamp(metrics.scrollPos / maxScroll, 0, 1);
128
+ return ratio * maxThumbTravel;
129
+ };
130
+ var trackPositionToScroll = (trackPos, trackLength, thumbSize, metrics) => {
131
+ const maxScroll = metrics.scrollSize - metrics.clientSize;
132
+ const maxThumbTravel = trackLength - thumbSize;
133
+ if (maxScroll <= 0 || maxThumbTravel <= 0) return 0;
134
+ const ratio = clamp(trackPos / maxThumbTravel, 0, 1);
135
+ return ratio * maxScroll;
136
+ };
137
+ var computeReservedSpace = (boundaryOffset, trackThicknessPx, superimposition) => {
138
+ if (superimposition !== "after") {
139
+ return 0;
140
+ }
141
+ const { start, end } = parseBoundaryOffset(boundaryOffset);
142
+ return start + trackThicknessPx + end;
143
+ };
144
+ var isPageScrollTarget = (el) => typeof document !== "undefined" && (el === document.body || el === document.documentElement);
145
+
146
+ // src/components/Axios/ScrollAxis.tsx
147
+ import { jsx } from "react/jsx-runtime";
148
+ var ScrollAxis = ({
149
+ axis,
150
+ target,
151
+ metrics,
152
+ scrollBar,
153
+ thumb,
154
+ positionMode,
155
+ superimposition,
156
+ hasCrossAxis,
157
+ vars
158
+ }) => {
159
+ const trackRef = useRef(null);
160
+ const dragState = useRef(null);
161
+ const [isDragging, setIsDragging] = useState(false);
162
+ const requestedTrackThickness = parsePxValue(scrollBar.widthTrack) ?? DEFAULT_TRACK_THICKNESS;
163
+ const trackThickness = Math.max(1, requestedTrackThickness);
164
+ const trackBoundary = parseBoundaryOffset(scrollBar.boundaryOffset);
165
+ const thumbBoundary = parseBoundaryOffset(thumb.boundaryOffset);
166
+ const requestedCrossAxisOccupation = trackBoundary.start + trackThickness + trackBoundary.end;
167
+ const maximumCrossAxisOccupation = Math.max(0, metrics.clientSize - 1);
168
+ const crossAxisOccupation = hasCrossAxis ? clamp(requestedCrossAxisOccupation, 0, maximumCrossAxisOccupation) : 0;
169
+ const availableMainStart = hasCrossAxis && positionMode === "before" ? crossAxisOccupation : 0;
170
+ const availableMainSize = Math.max(
171
+ 0,
172
+ metrics.clientSize - crossAxisOccupation
173
+ );
174
+ const trackCenter = availableMainStart + availableMainSize / 2;
175
+ const requestedTrackLength = resolveTrackLength(
176
+ scrollBar.heightTrack,
177
+ metrics.clientSize
178
+ );
179
+ const trackLength = availableMainSize > 0 ? clamp(requestedTrackLength, 1, availableMainSize) : 0;
180
+ const fittedThumbMainInsets = fitInsetsWithinSize(
181
+ thumbBoundary.start,
182
+ thumbBoundary.end,
183
+ trackLength,
184
+ 1
185
+ );
186
+ const thumbMainStart = fittedThumbMainInsets.start;
187
+ const innerTrackLength = fittedThumbMainInsets.innerSize;
188
+ const requestedThumbCrossSize = trackThickness - thumbBoundary.start - thumbBoundary.end;
189
+ const thumbCrossSize = clamp(requestedThumbCrossSize, 1, trackThickness);
190
+ const thumbSize = computeThumbSize(
191
+ innerTrackLength,
192
+ metrics,
193
+ thumb.heightTrack
194
+ );
195
+ const thumbPositionInsideTrack = computeThumbPosition(
196
+ innerTrackLength,
197
+ thumbSize,
198
+ metrics
199
+ );
200
+ const thumbPosition = thumbMainStart + thumbPositionInsideTrack;
201
+ const maxScroll = Math.max(0, metrics.scrollSize - metrics.clientSize);
202
+ const maxThumbTravel = Math.max(0, innerTrackLength - thumbSize);
203
+ const setScrollPosition = (value) => {
204
+ const targetElement = target;
205
+ if (!targetElement) return;
206
+ const nextValue = clamp(value, 0, Math.max(0, maxScroll));
207
+ if (!isPageScrollTarget(targetElement)) {
208
+ if (axis === "x") {
209
+ targetElement.scrollLeft = nextValue;
210
+ } else {
211
+ targetElement.scrollTop = nextValue;
212
+ }
213
+ return;
214
+ }
215
+ const targetStyle = window.getComputedStyle(targetElement);
216
+ const overflowValue = axis === "x" ? targetStyle.overflowX : targetStyle.overflowY;
217
+ const targetCanScroll = axis === "x" ? targetElement.scrollWidth - targetElement.clientWidth > 1 : targetElement.scrollHeight - targetElement.clientHeight > 1;
218
+ const targetOwnsScroll = targetCanScroll && /^(auto|scroll|overlay)$/.test(overflowValue);
219
+ if (targetOwnsScroll) {
220
+ if (axis === "x") {
221
+ targetElement.scrollLeft = nextValue;
222
+ } else {
223
+ targetElement.scrollTop = nextValue;
224
+ }
225
+ return;
226
+ }
227
+ window.scrollTo({
228
+ left: axis === "x" ? nextValue : window.scrollX,
229
+ top: axis === "y" ? nextValue : window.scrollY,
230
+ behavior: "auto"
231
+ });
232
+ };
233
+ const handleThumbPointerDown = (event) => {
234
+ event.preventDefault();
235
+ event.stopPropagation();
236
+ dragState.current = {
237
+ startPointer: axis === "x" ? event.clientX : event.clientY,
238
+ startScroll: metrics.scrollPos
239
+ };
240
+ setIsDragging(true);
241
+ event.currentTarget.setPointerCapture(event.pointerId);
242
+ };
243
+ const handleThumbPointerMove = (event) => {
244
+ const currentDragState = dragState.current;
245
+ if (!currentDragState) return;
246
+ if (maxScroll <= 0 || maxThumbTravel <= 0) return;
247
+ const pointerPosition = axis === "x" ? event.clientX : event.clientY;
248
+ const pointerDelta = pointerPosition - currentDragState.startPointer;
249
+ const scrollDelta = pointerDelta / maxThumbTravel * maxScroll;
250
+ setScrollPosition(currentDragState.startScroll + scrollDelta);
251
+ };
252
+ const stopDrag = (event) => {
253
+ if (!dragState.current) return;
254
+ dragState.current = null;
255
+ setIsDragging(false);
256
+ try {
257
+ event.currentTarget.releasePointerCapture(event.pointerId);
258
+ } catch {
259
+ }
260
+ };
261
+ const handleTrackPointerDown = (event) => {
262
+ if (event.target !== event.currentTarget) {
263
+ return;
264
+ }
265
+ const trackElement = trackRef.current;
266
+ if (!trackElement) return;
267
+ if (maxThumbTravel <= 0) return;
268
+ const rect = trackElement.getBoundingClientRect();
269
+ const clickPosition = axis === "x" ? event.clientX - rect.left : event.clientY - rect.top;
270
+ const clickInsideInnerTrack = clickPosition - thumbMainStart;
271
+ const targetThumbStart = clamp(
272
+ clickInsideInnerTrack - thumbSize / 2,
273
+ 0,
274
+ maxThumbTravel
275
+ );
276
+ const nextScroll = trackPositionToScroll(
277
+ targetThumbStart,
278
+ innerTrackLength,
279
+ thumbSize,
280
+ metrics
281
+ );
282
+ setScrollPosition(nextScroll);
283
+ };
284
+ if (!metrics.canScroll || metrics.clientSize <= 0 || trackLength < 1 || innerTrackLength < 1 || thumbSize < 1) {
285
+ return null;
286
+ }
287
+ const trackStyle = axis === "y" ? {
288
+ position: "absolute",
289
+ top: trackCenter,
290
+ transform: "translateY(-50%)",
291
+ width: trackThickness,
292
+ height: trackLength,
293
+ ...positionMode === "before" ? {
294
+ left: trackBoundary.start
295
+ } : {
296
+ right: trackBoundary.end
297
+ },
298
+ pointerEvents: "auto"
299
+ } : {
300
+ position: "absolute",
301
+ left: trackCenter,
302
+ transform: "translateX(-50%)",
303
+ width: trackLength,
304
+ height: trackThickness,
305
+ ...positionMode === "before" ? {
306
+ top: trackBoundary.start
307
+ } : {
308
+ bottom: trackBoundary.end
309
+ },
310
+ pointerEvents: "auto"
311
+ };
312
+ const thumbStyle = axis === "y" ? {
313
+ top: thumbPosition,
314
+ height: thumbSize,
315
+ //* horizontal center
316
+ left: "50%",
317
+ width: thumbCrossSize,
318
+ transform: "translateX(-50%)"
319
+ } : {
320
+ left: thumbPosition,
321
+ width: thumbSize,
322
+ //* vertical center
323
+ top: "50%",
324
+ height: thumbCrossSize,
325
+ transform: "translateY(-50%)"
326
+ };
327
+ return /* @__PURE__ */ jsx(
328
+ "div",
329
+ {
330
+ ref: trackRef,
331
+ className: "scroll-to-future__track",
332
+ style: { ...trackStyle, ...vars },
333
+ onPointerDown: handleTrackPointerDown,
334
+ "data-axis": axis,
335
+ "data-superimposition": superimposition,
336
+ children: /* @__PURE__ */ jsx(
337
+ "div",
338
+ {
339
+ className: `scroll-to-future__thumb ${isDragging ? "scroll-to-future__thumb--dragging" : ""}`.trim(),
340
+ style: thumbStyle,
341
+ onPointerDown: handleThumbPointerDown,
342
+ onPointerMove: handleThumbPointerMove,
343
+ onPointerUp: stopDrag,
344
+ onPointerCancel: stopDrag
345
+ }
346
+ )
347
+ }
348
+ );
349
+ };
350
+
351
+ // src/hooks/useElementScrollObserver.ts
352
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
353
+
354
+ // src/hooks/useRefReady.ts
355
+ import { useEffect, useState as useState2 } from "react";
356
+ var useRefReady = (target) => {
357
+ const [tick, setTick] = useState2(0);
358
+ useEffect(() => {
359
+ if (target) return;
360
+ let rafId;
361
+ const check = () => {
362
+ if (target) {
363
+ setTick((t) => t + 1);
364
+ } else {
365
+ rafId = requestAnimationFrame(check);
366
+ }
367
+ };
368
+ rafId = requestAnimationFrame(check);
369
+ return () => cancelAnimationFrame(rafId);
370
+ }, [target]);
371
+ return tick;
372
+ };
373
+
374
+ // src/hooks/useElementScrollObserver.ts
375
+ var isSameAxisMetrics = (previous, next) => previous.scrollSize === next.scrollSize && previous.clientSize === next.clientSize && previous.scrollPos === next.scrollPos && previous.canScroll === next.canScroll;
376
+ var getActualScrollPosition = (...values) => values.reduce((current, value) => {
377
+ return Math.abs(value) > Math.abs(current) ? value : current;
378
+ }, 0);
379
+ var useElementScrollObserver = (target) => {
380
+ const [metrics, setMetrics] = useState3({
381
+ x: EMPTY_AXIS_METRICS,
382
+ y: EMPTY_AXIS_METRICS
383
+ });
384
+ const rafRef = useRef2(null);
385
+ const ready = useRefReady(target);
386
+ useEffect2(() => {
387
+ const targetElement = target;
388
+ if (!targetElement) {
389
+ setMetrics({
390
+ x: EMPTY_AXIS_METRICS,
391
+ y: EMPTY_AXIS_METRICS
392
+ });
393
+ return;
394
+ }
395
+ const pageScroll = isPageScrollTarget(targetElement);
396
+ const measure = () => {
397
+ let nextX;
398
+ let nextY;
399
+ if (pageScroll) {
400
+ const root = document.documentElement;
401
+ const body = document.body;
402
+ const scrollingElement = document.scrollingElement;
403
+ const scrollWidth = Math.max(
404
+ root.scrollWidth,
405
+ body.scrollWidth,
406
+ scrollingElement?.scrollWidth ?? 0,
407
+ targetElement.scrollWidth
408
+ );
409
+ const scrollHeight = Math.max(
410
+ root.scrollHeight,
411
+ body.scrollHeight,
412
+ scrollingElement?.scrollHeight ?? 0,
413
+ targetElement.scrollHeight
414
+ );
415
+ const clientWidth = window.visualViewport?.width ?? window.innerWidth;
416
+ const clientHeight = window.visualViewport?.height ?? window.innerHeight;
417
+ const scrollLeft = getActualScrollPosition(
418
+ window.scrollX,
419
+ root.scrollLeft,
420
+ body.scrollLeft,
421
+ scrollingElement?.scrollLeft ?? 0,
422
+ targetElement.scrollLeft
423
+ );
424
+ const scrollTop = getActualScrollPosition(
425
+ window.scrollY,
426
+ root.scrollTop,
427
+ body.scrollTop,
428
+ scrollingElement?.scrollTop ?? 0,
429
+ targetElement.scrollTop
430
+ );
431
+ nextX = {
432
+ scrollSize: scrollWidth,
433
+ clientSize: clientWidth,
434
+ scrollPos: scrollLeft,
435
+ canScroll: scrollWidth - clientWidth > 1
436
+ };
437
+ nextY = {
438
+ scrollSize: scrollHeight,
439
+ clientSize: clientHeight,
440
+ scrollPos: scrollTop,
441
+ canScroll: scrollHeight - clientHeight > 1
442
+ };
443
+ } else {
444
+ nextX = {
445
+ scrollSize: targetElement.scrollWidth,
446
+ clientSize: targetElement.clientWidth,
447
+ scrollPos: targetElement.scrollLeft,
448
+ canScroll: targetElement.scrollWidth - targetElement.clientWidth > 1
449
+ };
450
+ nextY = {
451
+ scrollSize: targetElement.scrollHeight,
452
+ clientSize: targetElement.clientHeight,
453
+ scrollPos: targetElement.scrollTop,
454
+ canScroll: targetElement.scrollHeight - targetElement.clientHeight > 1
455
+ };
456
+ }
457
+ setMetrics((previous) => {
458
+ const sameX = isSameAxisMetrics(previous.x, nextX);
459
+ const sameY = isSameAxisMetrics(previous.y, nextY);
460
+ if (sameX && sameY) {
461
+ return previous;
462
+ }
463
+ return {
464
+ x: nextX,
465
+ y: nextY
466
+ };
467
+ });
468
+ };
469
+ const scheduleMeasure = () => {
470
+ if (rafRef.current !== null) return;
471
+ rafRef.current = requestAnimationFrame(() => {
472
+ rafRef.current = null;
473
+ measure();
474
+ });
475
+ };
476
+ measure();
477
+ const resizeObserver = new ResizeObserver(scheduleMeasure);
478
+ if (pageScroll) {
479
+ resizeObserver.observe(document.documentElement);
480
+ resizeObserver.observe(document.body);
481
+ if (targetElement !== document.documentElement && targetElement !== document.body) {
482
+ resizeObserver.observe(targetElement);
483
+ }
484
+ } else {
485
+ resizeObserver.observe(targetElement);
486
+ Array.from(targetElement.children).forEach((child) => {
487
+ resizeObserver.observe(child);
488
+ });
489
+ }
490
+ const mutationRoot = pageScroll ? document.documentElement : targetElement;
491
+ const mutationObserver = new MutationObserver((mutations) => {
492
+ for (const mutation of mutations) {
493
+ if (mutation.type !== "childList") continue;
494
+ mutation.addedNodes.forEach((node) => {
495
+ if (!(node instanceof Element)) return;
496
+ try {
497
+ resizeObserver.observe(node);
498
+ } catch {
499
+ }
500
+ });
501
+ }
502
+ scheduleMeasure();
503
+ });
504
+ mutationObserver.observe(mutationRoot, {
505
+ childList: true,
506
+ subtree: true,
507
+ attributes: true,
508
+ attributeFilter: ["style", "class", "hidden"]
509
+ });
510
+ window.addEventListener("scroll", scheduleMeasure, {
511
+ capture: true,
512
+ passive: true
513
+ });
514
+ targetElement.addEventListener("scroll", scheduleMeasure, {
515
+ passive: true
516
+ });
517
+ window.addEventListener("resize", scheduleMeasure);
518
+ window.visualViewport?.addEventListener("resize", scheduleMeasure);
519
+ window.visualViewport?.addEventListener("scroll", scheduleMeasure);
520
+ return () => {
521
+ resizeObserver.disconnect();
522
+ mutationObserver.disconnect();
523
+ window.removeEventListener("scroll", scheduleMeasure, true);
524
+ targetElement.removeEventListener("scroll", scheduleMeasure);
525
+ window.removeEventListener("resize", scheduleMeasure);
526
+ window.visualViewport?.removeEventListener(
527
+ "resize",
528
+ scheduleMeasure
529
+ );
530
+ window.visualViewport?.removeEventListener(
531
+ "scroll",
532
+ scheduleMeasure
533
+ );
534
+ if (rafRef.current !== null) {
535
+ cancelAnimationFrame(rafRef.current);
536
+ rafRef.current = null;
537
+ }
538
+ };
539
+ }, [target, ready]);
540
+ return metrics;
541
+ };
542
+
543
+ // src/hooks/useFuture.ts
544
+ import { useEffect as useEffect3 } from "react";
545
+
546
+ // src/utils/native-scrollbar.ts
547
+ var STYLE_ID = "scroll-to-future-native-scrollbar-styles";
548
+ var ALWAYS_CLASS = "scroll-to-future-hide-native-scrollbar";
549
+ var FINE_POINTER_CLASS = "scroll-to-future-hide-native-scrollbar-fine";
550
+ var classCounters = /* @__PURE__ */ new WeakMap();
551
+ var installStyles = () => {
552
+ if (typeof document === "undefined") return;
553
+ if (document.getElementById(STYLE_ID)) {
554
+ return;
555
+ }
556
+ const style = document.createElement("style");
557
+ style.id = STYLE_ID;
558
+ style.textContent = `
559
+ .${ALWAYS_CLASS} {
560
+ scrollbar-width: none !important;
561
+ -ms-overflow-style: none !important;
562
+ }
563
+
564
+ .${ALWAYS_CLASS}::-webkit-scrollbar {
565
+ display: none !important;
566
+ width: 0 !important;
567
+ height: 0 !important;
568
+ background: transparent !important;
569
+ }
570
+
571
+ @media (any-pointer: fine) {
572
+ .${FINE_POINTER_CLASS} {
573
+ scrollbar-width: none !important;
574
+ -ms-overflow-style: none !important;
575
+ }
576
+
577
+ .${FINE_POINTER_CLASS}::-webkit-scrollbar {
578
+ display: none !important;
579
+ width: 0 !important;
580
+ height: 0 !important;
581
+ background: transparent !important;
582
+ }
583
+ }
584
+ `;
585
+ document.head.appendChild(style);
586
+ };
587
+ var retainClass = (element, className) => {
588
+ let elementCounters = classCounters.get(element);
589
+ if (!elementCounters) {
590
+ elementCounters = /* @__PURE__ */ new Map();
591
+ classCounters.set(element, elementCounters);
592
+ }
593
+ const currentCount = elementCounters.get(className) ?? 0;
594
+ elementCounters.set(className, currentCount + 1);
595
+ if (currentCount === 0) {
596
+ element.classList.add(className);
597
+ }
598
+ return () => {
599
+ const counters = classCounters.get(element);
600
+ if (!counters) return;
601
+ const count = counters.get(className) ?? 0;
602
+ if (count <= 1) {
603
+ counters.delete(className);
604
+ element.classList.remove(className);
605
+ } else {
606
+ counters.set(className, count - 1);
607
+ }
608
+ if (counters.size === 0) {
609
+ classCounters.delete(element);
610
+ }
611
+ };
612
+ };
613
+ var isDocumentScrollTarget = (target) => {
614
+ if (typeof document === "undefined") {
615
+ return false;
616
+ }
617
+ return target === document.body || target === document.documentElement || target === document.scrollingElement;
618
+ };
619
+ var resolveStyleTargets = (target) => {
620
+ if (!isDocumentScrollTarget(target)) {
621
+ return [target];
622
+ }
623
+ const targets = /* @__PURE__ */ new Set();
624
+ targets.add(document.documentElement);
625
+ if (document.body) {
626
+ targets.add(document.body);
627
+ }
628
+ const scrollingElement = document.scrollingElement;
629
+ if (scrollingElement instanceof HTMLElement) {
630
+ targets.add(scrollingElement);
631
+ }
632
+ return Array.from(targets);
633
+ };
634
+ var isMobileInputDevice = () => {
635
+ if (typeof window === "undefined" || typeof navigator === "undefined") {
636
+ return false;
637
+ }
638
+ const coarsePointer = window.matchMedia("(pointer: coarse)").matches;
639
+ const cannotHover = window.matchMedia("(hover: none)").matches;
640
+ const hasTouch = navigator.maxTouchPoints > 0;
641
+ return hasTouch && coarsePointer && cannotHover;
642
+ };
643
+ var hideNativeScrollbar = (target, mode, nativeOnMobile) => {
644
+ if (typeof window === "undefined" || typeof document === "undefined" || mode === false) {
645
+ return () => {
646
+ };
647
+ }
648
+ if (nativeOnMobile && isMobileInputDevice()) {
649
+ return () => {
650
+ };
651
+ }
652
+ installStyles();
653
+ const className = mode === "always" ? ALWAYS_CLASS : FINE_POINTER_CLASS;
654
+ const targets = resolveStyleTargets(target);
655
+ const cleanups = targets.map((element) => retainClass(element, className));
656
+ return () => {
657
+ cleanups.forEach((cleanup) => {
658
+ cleanup();
659
+ });
660
+ };
661
+ };
662
+
663
+ // src/hooks/useFuture.ts
664
+ var useFuture = ({
665
+ target,
666
+ anchorRef,
667
+ targetRef,
668
+ setFindedTarget,
669
+ mounted,
670
+ config,
671
+ showY,
672
+ showX,
673
+ superimposition,
674
+ findedTarget,
675
+ positionMode,
676
+ coversAllScrollableAxes,
677
+ nativeOnMobile
678
+ }) => {
679
+ useEffect3(() => {
680
+ if (!mounted) return;
681
+ let rafId = null;
682
+ const resolveTarget = () => {
683
+ const nextTarget = target ? target.current : anchorRef.current?.parentElement ?? null;
684
+ if (nextTarget) {
685
+ targetRef.current = nextTarget;
686
+ setFindedTarget(
687
+ (previousTarget) => previousTarget === nextTarget ? previousTarget : nextTarget
688
+ );
689
+ return;
690
+ }
691
+ rafId = requestAnimationFrame(resolveTarget);
692
+ };
693
+ resolveTarget();
694
+ return () => {
695
+ if (rafId !== null) {
696
+ cancelAnimationFrame(rafId);
697
+ }
698
+ };
699
+ }, [mounted, target]);
700
+ useEffect3(() => {
701
+ const el = targetRef.current;
702
+ if (!el) return;
703
+ const trackThickness = parsePxValue(config.scrollBar?.widthTrack) ?? DEFAULT_TRACK_THICKNESS;
704
+ const reservedY = showY ? computeReservedSpace(
705
+ config.scrollBar?.boundaryOffset,
706
+ trackThickness,
707
+ superimposition
708
+ ) : 0;
709
+ const reservedX = showX ? computeReservedSpace(
710
+ config.scrollBar?.boundaryOffset,
711
+ trackThickness,
712
+ superimposition
713
+ ) : 0;
714
+ const previousInlinePadding = {
715
+ left: el.style.paddingLeft,
716
+ right: el.style.paddingRight,
717
+ top: el.style.paddingTop,
718
+ bottom: el.style.paddingBottom
719
+ };
720
+ const computedStyle = window.getComputedStyle(el);
721
+ const basePadding = {
722
+ left: Number.parseFloat(computedStyle.paddingLeft) || 0,
723
+ right: Number.parseFloat(computedStyle.paddingRight) || 0,
724
+ top: Number.parseFloat(computedStyle.paddingTop) || 0,
725
+ bottom: Number.parseFloat(computedStyle.paddingBottom) || 0
726
+ };
727
+ if (reservedY > 0) {
728
+ if (positionMode === "before") {
729
+ el.style.paddingLeft = `${basePadding.left + reservedY}px`;
730
+ } else {
731
+ el.style.paddingRight = `${basePadding.right + reservedY}px`;
732
+ }
733
+ }
734
+ if (reservedX > 0) {
735
+ if (positionMode === "before") {
736
+ el.style.paddingTop = `${basePadding.top + reservedX}px`;
737
+ } else {
738
+ el.style.paddingBottom = `${basePadding.bottom + reservedX}px`;
739
+ }
740
+ }
741
+ return () => {
742
+ el.style.paddingLeft = previousInlinePadding.left;
743
+ el.style.paddingRight = previousInlinePadding.right;
744
+ el.style.paddingTop = previousInlinePadding.top;
745
+ el.style.paddingBottom = previousInlinePadding.bottom;
746
+ };
747
+ }, [
748
+ findedTarget,
749
+ showX,
750
+ showY,
751
+ positionMode,
752
+ superimposition,
753
+ config.scrollBar?.boundaryOffset,
754
+ config.scrollBar?.widthTrack
755
+ ]);
756
+ useEffect3(() => {
757
+ if (!findedTarget) return;
758
+ const mode = config.scrollBar?.hideNativeScrollbar ?? false;
759
+ console.log("native scrollbar effect:", {
760
+ mode,
761
+ nativeOnMobile,
762
+ coversAllScrollableAxes,
763
+ target: findedTarget
764
+ });
765
+ if (mode === false || !coversAllScrollableAxes) {
766
+ return;
767
+ }
768
+ return hideNativeScrollbar(findedTarget, mode, nativeOnMobile);
769
+ }, [
770
+ findedTarget,
771
+ coversAllScrollableAxes,
772
+ nativeOnMobile,
773
+ config.scrollBar?.hideNativeScrollbar
774
+ ]);
775
+ };
776
+
777
+ // src/hooks/useMounted.tsx
778
+ import { useEffect as useEffect4, useState as useState4 } from "react";
779
+ function useMounted() {
780
+ const [mounted, setMounted] = useState4(false);
781
+ useEffect4(() => {
782
+ if (mounted) return;
783
+ setMounted(true);
784
+ }, []);
785
+ return mounted;
786
+ }
787
+
788
+ // src/hooks/useTargetRect.ts
789
+ import { useEffect as useEffect5, useState as useState5 } from "react";
790
+ var EMPTY_RECT = { top: 0, left: 0, width: 0, height: 0 };
791
+ var useTargetRect = (target) => {
792
+ const [rect, setRect] = useState5(EMPTY_RECT);
793
+ const ready = useRefReady(target);
794
+ useEffect5(() => {
795
+ const el = target;
796
+ if (!el) {
797
+ setRect(EMPTY_RECT);
798
+ return;
799
+ }
800
+ let rafId = null;
801
+ const pageScroll = isPageScrollTarget(el);
802
+ const measure = () => {
803
+ const r = pageScroll ? {
804
+ top: 0,
805
+ left: 0,
806
+ width: window.innerWidth,
807
+ height: window.innerHeight
808
+ } : el.getBoundingClientRect();
809
+ setRect((prev) => {
810
+ if (prev.top === r.top && prev.left === r.left && prev.width === r.width && prev.height === r.height) {
811
+ return prev;
812
+ }
813
+ return {
814
+ top: r.top,
815
+ left: r.left,
816
+ width: r.width,
817
+ height: r.height
818
+ };
819
+ });
820
+ };
821
+ const scheduleMeasure = () => {
822
+ if (rafId != null) return;
823
+ rafId = requestAnimationFrame(() => {
824
+ rafId = null;
825
+ measure();
826
+ });
827
+ };
828
+ measure();
829
+ const resizeObserver = new ResizeObserver(scheduleMeasure);
830
+ resizeObserver.observe(el);
831
+ window.addEventListener("scroll", scheduleMeasure, {
832
+ capture: true,
833
+ passive: true
834
+ });
835
+ window.addEventListener("resize", scheduleMeasure);
836
+ return () => {
837
+ resizeObserver.disconnect();
838
+ window.removeEventListener("scroll", scheduleMeasure, true);
839
+ window.removeEventListener("resize", scheduleMeasure);
840
+ if (rafId != null) cancelAnimationFrame(rafId);
841
+ };
842
+ }, [target, ready]);
843
+ return rect;
844
+ };
845
+
846
+ // src/themes/collection.theme.ts
847
+ var primary = {
848
+ scrollBar: {
849
+ inactive: {
850
+ backgroundColor: "rgba(0, 0, 0, 0.15)",
851
+ borderRadius: "8px",
852
+ transition: "background-color 0.3s ease"
853
+ },
854
+ hover: {
855
+ backgroundColor: "rgba(0, 0, 0, 0.25)"
856
+ },
857
+ active: {
858
+ backgroundColor: "rgba(0, 0, 0, 0.35)",
859
+ transition: "background-color 0.15s ease"
860
+ }
861
+ },
862
+ thumb: {
863
+ inactive: {
864
+ backgroundColor: "rgba(255, 255, 255, 0.35)",
865
+ borderRadius: "8px"
866
+ },
867
+ hover: {
868
+ backgroundColor: "rgba(255, 255, 255, 0.55)",
869
+ transform: "scale(1)",
870
+ transition: "background-color 0s ease, transform 0s ease"
871
+ },
872
+ active: {
873
+ backgroundColor: "rgba(255, 255, 255, 0.75)",
874
+ transform: "scale(1.1)",
875
+ transition: "background-color 0.15s ease, transform 0.15s ease"
876
+ }
877
+ }
878
+ };
879
+ var midnight = {
880
+ scrollBar: {
881
+ inactive: {
882
+ backgroundColor: "rgba(15, 23, 42, 0.55)",
883
+ borderRadius: "8px",
884
+ transition: "background-color 0.3s ease"
885
+ },
886
+ hover: {
887
+ backgroundColor: "rgba(30, 41, 59, 0.72)"
888
+ },
889
+ active: {
890
+ backgroundColor: "rgba(51, 65, 85, 0.88)",
891
+ transition: "background-color 0.15s ease"
892
+ }
893
+ },
894
+ thumb: {
895
+ inactive: {
896
+ backgroundColor: "rgba(148, 163, 184, 0.55)",
897
+ borderRadius: "8px"
898
+ },
899
+ hover: {
900
+ backgroundColor: "rgba(203, 213, 225, 0.78)",
901
+ transform: "scale(1)",
902
+ transition: "background-color 0s ease, transform 0s ease"
903
+ },
904
+ active: {
905
+ backgroundColor: "rgba(241, 245, 249, 0.96)",
906
+ transform: "scale(1.1)",
907
+ transition: "background-color 0.15s ease, transform 0.15s ease"
908
+ }
909
+ }
910
+ };
911
+ var neonCyan = {
912
+ scrollBar: {
913
+ inactive: {
914
+ backgroundColor: "rgba(6, 78, 89, 0.35)",
915
+ borderRadius: "4px",
916
+ transition: "background-color 0.25s ease"
917
+ },
918
+ hover: {
919
+ backgroundColor: "rgba(8, 145, 178, 0.48)"
920
+ },
921
+ active: {
922
+ backgroundColor: "rgba(14, 116, 144, 0.68)",
923
+ transition: "background-color 0.12s ease"
924
+ }
925
+ },
926
+ thumb: {
927
+ inactive: {
928
+ backgroundColor: "rgba(34, 211, 238, 0.58)",
929
+ borderRadius: "3px"
930
+ },
931
+ hover: {
932
+ backgroundColor: "rgba(103, 232, 249, 0.85)",
933
+ transform: "scale(1.02)",
934
+ transition: "background-color 0.15s ease, transform 0.15s ease"
935
+ },
936
+ active: {
937
+ backgroundColor: "rgba(207, 250, 254, 1)",
938
+ transform: "scale(1.12)",
939
+ transition: "background-color 0.12s ease, transform 0.12s ease"
940
+ }
941
+ }
942
+ };
943
+ var ocean = {
944
+ scrollBar: {
945
+ inactive: {
946
+ backgroundColor: "rgba(7, 89, 133, 0.22)",
947
+ borderRadius: "999px",
948
+ transition: "background-color 0.35s ease"
949
+ },
950
+ hover: {
951
+ backgroundColor: "rgba(3, 105, 161, 0.35)"
952
+ },
953
+ active: {
954
+ backgroundColor: "rgba(2, 132, 199, 0.5)",
955
+ transition: "background-color 0.18s ease"
956
+ }
957
+ },
958
+ thumb: {
959
+ inactive: {
960
+ backgroundColor: "rgba(14, 165, 233, 0.58)",
961
+ borderRadius: "999px"
962
+ },
963
+ hover: {
964
+ backgroundColor: "rgba(56, 189, 248, 0.82)",
965
+ transform: "scale(1)",
966
+ transition: "background-color 0.2s ease"
967
+ },
968
+ active: {
969
+ backgroundColor: "rgba(186, 230, 253, 0.98)",
970
+ transform: "scale(1.08)",
971
+ transition: "background-color 0.14s ease, transform 0.14s ease"
972
+ }
973
+ }
974
+ };
975
+ var deepSea = {
976
+ scrollBar: {
977
+ inactive: {
978
+ backgroundColor: "rgba(2, 44, 55, 0.62)",
979
+ borderRadius: "999px",
980
+ transition: "background-color 0.3s ease"
981
+ },
982
+ hover: {
983
+ backgroundColor: "rgba(6, 78, 89, 0.78)"
984
+ },
985
+ active: {
986
+ backgroundColor: "rgba(14, 116, 144, 0.9)",
987
+ transition: "background-color 0.15s ease"
988
+ }
989
+ },
990
+ thumb: {
991
+ inactive: {
992
+ backgroundColor: "rgba(8, 145, 178, 0.62)",
993
+ borderRadius: "999px"
994
+ },
995
+ hover: {
996
+ backgroundColor: "rgba(34, 211, 238, 0.82)",
997
+ transform: "scale(1.03)",
998
+ transition: "background-color 0.18s ease, transform 0.18s ease"
999
+ },
1000
+ active: {
1001
+ backgroundColor: "rgba(165, 243, 252, 0.98)",
1002
+ transform: "scale(1.12)",
1003
+ transition: "background-color 0.12s ease, transform 0.12s ease"
1004
+ }
1005
+ }
1006
+ };
1007
+ var forest = {
1008
+ scrollBar: {
1009
+ inactive: {
1010
+ backgroundColor: "rgba(20, 83, 45, 0.25)",
1011
+ borderRadius: "999px",
1012
+ transition: "background-color 0.3s ease"
1013
+ },
1014
+ hover: {
1015
+ backgroundColor: "rgba(21, 128, 61, 0.38)"
1016
+ },
1017
+ active: {
1018
+ backgroundColor: "rgba(22, 163, 74, 0.52)",
1019
+ transition: "background-color 0.15s ease"
1020
+ }
1021
+ },
1022
+ thumb: {
1023
+ inactive: {
1024
+ backgroundColor: "rgba(34, 197, 94, 0.55)",
1025
+ borderRadius: "999px"
1026
+ },
1027
+ hover: {
1028
+ backgroundColor: "rgba(74, 222, 128, 0.8)",
1029
+ transform: "scale(1)",
1030
+ transition: "background-color 0.2s ease"
1031
+ },
1032
+ active: {
1033
+ backgroundColor: "rgba(187, 247, 208, 0.98)",
1034
+ transform: "scale(1.1)",
1035
+ transition: "background-color 0.15s ease, transform 0.15s ease"
1036
+ }
1037
+ }
1038
+ };
1039
+ var moss = {
1040
+ scrollBar: {
1041
+ inactive: {
1042
+ backgroundColor: "rgba(54, 83, 20, 0.28)",
1043
+ borderRadius: "6px",
1044
+ transition: "background-color 0.3s ease"
1045
+ },
1046
+ hover: {
1047
+ backgroundColor: "rgba(63, 98, 18, 0.42)"
1048
+ },
1049
+ active: {
1050
+ backgroundColor: "rgba(77, 124, 15, 0.58)",
1051
+ transition: "background-color 0.14s ease"
1052
+ }
1053
+ },
1054
+ thumb: {
1055
+ inactive: {
1056
+ backgroundColor: "rgba(132, 204, 22, 0.55)",
1057
+ borderRadius: "5px"
1058
+ },
1059
+ hover: {
1060
+ backgroundColor: "rgba(163, 230, 53, 0.8)",
1061
+ transform: "scale(1.02)",
1062
+ transition: "background-color 0.18s ease, transform 0.18s ease"
1063
+ },
1064
+ active: {
1065
+ backgroundColor: "rgba(217, 249, 157, 0.98)",
1066
+ transform: "scale(1.1)",
1067
+ transition: "background-color 0.12s ease, transform 0.12s ease"
1068
+ }
1069
+ }
1070
+ };
1071
+ var lava = {
1072
+ scrollBar: {
1073
+ inactive: {
1074
+ backgroundColor: "rgba(69, 10, 10, 0.45)",
1075
+ borderRadius: "999px",
1076
+ transition: "background-color 0.22s ease"
1077
+ },
1078
+ hover: {
1079
+ backgroundColor: "rgba(127, 29, 29, 0.62)"
1080
+ },
1081
+ active: {
1082
+ backgroundColor: "rgba(153, 27, 27, 0.78)",
1083
+ transition: "background-color 0.1s ease"
1084
+ }
1085
+ },
1086
+ thumb: {
1087
+ inactive: {
1088
+ backgroundColor: "rgba(239, 68, 68, 0.62)",
1089
+ borderRadius: "999px"
1090
+ },
1091
+ hover: {
1092
+ backgroundColor: "rgba(249, 115, 22, 0.88)",
1093
+ transform: "scale(1.03)",
1094
+ transition: "background-color 0.16s ease, transform 0.16s ease"
1095
+ },
1096
+ active: {
1097
+ backgroundColor: "rgba(253, 186, 116, 1)",
1098
+ transform: "scale(1.14)",
1099
+ transition: "background-color 0.1s ease, transform 0.1s ease"
1100
+ }
1101
+ }
1102
+ };
1103
+ var ember = {
1104
+ scrollBar: {
1105
+ inactive: {
1106
+ backgroundColor: "rgba(67, 20, 7, 0.32)",
1107
+ borderRadius: "5px",
1108
+ transition: "background-color 0.25s ease"
1109
+ },
1110
+ hover: {
1111
+ backgroundColor: "rgba(124, 45, 18, 0.48)"
1112
+ },
1113
+ active: {
1114
+ backgroundColor: "rgba(154, 52, 18, 0.65)",
1115
+ transition: "background-color 0.12s ease"
1116
+ }
1117
+ },
1118
+ thumb: {
1119
+ inactive: {
1120
+ backgroundColor: "rgba(234, 88, 12, 0.6)",
1121
+ borderRadius: "4px"
1122
+ },
1123
+ hover: {
1124
+ backgroundColor: "rgba(251, 146, 60, 0.86)",
1125
+ transform: "scale(1)",
1126
+ transition: "background-color 0.18s ease"
1127
+ },
1128
+ active: {
1129
+ backgroundColor: "rgba(254, 215, 170, 0.98)",
1130
+ transform: "scale(1.1)",
1131
+ transition: "background-color 0.12s ease, transform 0.12s ease"
1132
+ }
1133
+ }
1134
+ };
1135
+ var gold = {
1136
+ scrollBar: {
1137
+ inactive: {
1138
+ backgroundColor: "rgba(113, 63, 18, 0.25)",
1139
+ borderRadius: "999px",
1140
+ transition: "background-color 0.3s ease"
1141
+ },
1142
+ hover: {
1143
+ backgroundColor: "rgba(161, 98, 7, 0.4)"
1144
+ },
1145
+ active: {
1146
+ backgroundColor: "rgba(202, 138, 4, 0.55)",
1147
+ transition: "background-color 0.15s ease"
1148
+ }
1149
+ },
1150
+ thumb: {
1151
+ inactive: {
1152
+ backgroundColor: "rgba(234, 179, 8, 0.62)",
1153
+ borderRadius: "999px"
1154
+ },
1155
+ hover: {
1156
+ backgroundColor: "rgba(250, 204, 21, 0.85)",
1157
+ transform: "scale(1.02)",
1158
+ transition: "background-color 0.2s ease, transform 0.2s ease"
1159
+ },
1160
+ active: {
1161
+ backgroundColor: "rgba(254, 240, 138, 1)",
1162
+ transform: "scale(1.1)",
1163
+ transition: "background-color 0.14s ease, transform 0.14s ease"
1164
+ }
1165
+ }
1166
+ };
1167
+ var roseQuartz = {
1168
+ scrollBar: {
1169
+ inactive: {
1170
+ backgroundColor: "rgba(136, 19, 55, 0.18)",
1171
+ borderRadius: "999px",
1172
+ transition: "background-color 0.32s ease"
1173
+ },
1174
+ hover: {
1175
+ backgroundColor: "rgba(190, 24, 93, 0.28)"
1176
+ },
1177
+ active: {
1178
+ backgroundColor: "rgba(219, 39, 119, 0.4)",
1179
+ transition: "background-color 0.16s ease"
1180
+ }
1181
+ },
1182
+ thumb: {
1183
+ inactive: {
1184
+ backgroundColor: "rgba(251, 113, 133, 0.58)",
1185
+ borderRadius: "999px"
1186
+ },
1187
+ hover: {
1188
+ backgroundColor: "rgba(244, 114, 182, 0.8)",
1189
+ transform: "scale(1)",
1190
+ transition: "background-color 0.2s ease"
1191
+ },
1192
+ active: {
1193
+ backgroundColor: "rgba(253, 164, 175, 0.98)",
1194
+ transform: "scale(1.1)",
1195
+ transition: "background-color 0.15s ease, transform 0.15s ease"
1196
+ }
1197
+ }
1198
+ };
1199
+ var violet = {
1200
+ scrollBar: {
1201
+ inactive: {
1202
+ backgroundColor: "rgba(76, 29, 149, 0.3)",
1203
+ borderRadius: "999px",
1204
+ transition: "background-color 0.28s ease"
1205
+ },
1206
+ hover: {
1207
+ backgroundColor: "rgba(91, 33, 182, 0.45)"
1208
+ },
1209
+ active: {
1210
+ backgroundColor: "rgba(109, 40, 217, 0.62)",
1211
+ transition: "background-color 0.14s ease"
1212
+ }
1213
+ },
1214
+ thumb: {
1215
+ inactive: {
1216
+ backgroundColor: "rgba(139, 92, 246, 0.62)",
1217
+ borderRadius: "999px"
1218
+ },
1219
+ hover: {
1220
+ backgroundColor: "rgba(167, 139, 250, 0.86)",
1221
+ transform: "scale(1.03)",
1222
+ transition: "background-color 0.18s ease, transform 0.18s ease"
1223
+ },
1224
+ active: {
1225
+ backgroundColor: "rgba(221, 214, 254, 1)",
1226
+ transform: "scale(1.12)",
1227
+ transition: "background-color 0.12s ease, transform 0.12s ease"
1228
+ }
1229
+ }
1230
+ };
1231
+ var royal = {
1232
+ scrollBar: {
1233
+ inactive: {
1234
+ backgroundColor: "rgba(30, 27, 75, 0.48)",
1235
+ borderRadius: "999px",
1236
+ transition: "background-color 0.3s ease"
1237
+ },
1238
+ hover: {
1239
+ backgroundColor: "rgba(49, 46, 129, 0.65)"
1240
+ },
1241
+ active: {
1242
+ backgroundColor: "rgba(67, 56, 202, 0.82)",
1243
+ transition: "background-color 0.15s ease"
1244
+ }
1245
+ },
1246
+ thumb: {
1247
+ inactive: {
1248
+ backgroundColor: "rgba(99, 102, 241, 0.65)",
1249
+ borderRadius: "999px"
1250
+ },
1251
+ hover: {
1252
+ backgroundColor: "rgba(165, 180, 252, 0.86)",
1253
+ transform: "scale(1.02)",
1254
+ transition: "background-color 0.2s ease, transform 0.2s ease"
1255
+ },
1256
+ active: {
1257
+ backgroundColor: "rgba(224, 231, 255, 1)",
1258
+ transform: "scale(1.12)",
1259
+ transition: "background-color 0.14s ease, transform 0.14s ease"
1260
+ }
1261
+ }
1262
+ };
1263
+ var arctic = {
1264
+ scrollBar: {
1265
+ inactive: {
1266
+ backgroundColor: "rgba(186, 230, 253, 0.22)",
1267
+ borderRadius: "999px",
1268
+ transition: "background-color 0.35s ease"
1269
+ },
1270
+ hover: {
1271
+ backgroundColor: "rgba(125, 211, 252, 0.36)"
1272
+ },
1273
+ active: {
1274
+ backgroundColor: "rgba(56, 189, 248, 0.5)",
1275
+ transition: "background-color 0.18s ease"
1276
+ }
1277
+ },
1278
+ thumb: {
1279
+ inactive: {
1280
+ backgroundColor: "rgba(224, 242, 254, 0.65)",
1281
+ borderRadius: "999px"
1282
+ },
1283
+ hover: {
1284
+ backgroundColor: "rgba(240, 249, 255, 0.88)",
1285
+ transform: "scale(1)",
1286
+ transition: "background-color 0.2s ease"
1287
+ },
1288
+ active: {
1289
+ backgroundColor: "rgba(255, 255, 255, 1)",
1290
+ transform: "scale(1.08)",
1291
+ transition: "background-color 0.15s ease, transform 0.15s ease"
1292
+ }
1293
+ }
1294
+ };
1295
+ var glass = {
1296
+ scrollBar: {
1297
+ inactive: {
1298
+ backgroundColor: "rgba(255, 255, 255, 0.08)",
1299
+ borderRadius: "999px",
1300
+ transition: "background-color 0.3s ease"
1301
+ },
1302
+ hover: {
1303
+ backgroundColor: "rgba(255, 255, 255, 0.14)"
1304
+ },
1305
+ active: {
1306
+ backgroundColor: "rgba(255, 255, 255, 0.22)",
1307
+ transition: "background-color 0.15s ease"
1308
+ }
1309
+ },
1310
+ thumb: {
1311
+ inactive: {
1312
+ backgroundColor: "rgba(255, 255, 255, 0.28)",
1313
+ borderRadius: "999px"
1314
+ },
1315
+ hover: {
1316
+ backgroundColor: "rgba(255, 255, 255, 0.46)",
1317
+ transform: "scale(1)",
1318
+ transition: "background-color 0.2s ease"
1319
+ },
1320
+ active: {
1321
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
1322
+ transform: "scale(1.08)",
1323
+ transition: "background-color 0.15s ease, transform 0.15s ease"
1324
+ }
1325
+ }
1326
+ };
1327
+ var graphite = {
1328
+ scrollBar: {
1329
+ inactive: {
1330
+ backgroundColor: "rgba(17, 24, 39, 0.52)",
1331
+ borderRadius: "3px",
1332
+ transition: "background-color 0.22s ease"
1333
+ },
1334
+ hover: {
1335
+ backgroundColor: "rgba(31, 41, 55, 0.7)"
1336
+ },
1337
+ active: {
1338
+ backgroundColor: "rgba(55, 65, 81, 0.88)",
1339
+ transition: "background-color 0.1s ease"
1340
+ }
1341
+ },
1342
+ thumb: {
1343
+ inactive: {
1344
+ backgroundColor: "rgba(107, 114, 128, 0.65)",
1345
+ borderRadius: "2px"
1346
+ },
1347
+ hover: {
1348
+ backgroundColor: "rgba(156, 163, 175, 0.82)",
1349
+ transform: "scale(1)",
1350
+ transition: "background-color 0.15s ease"
1351
+ },
1352
+ active: {
1353
+ backgroundColor: "rgba(229, 231, 235, 0.98)",
1354
+ transform: "scale(1.06)",
1355
+ transition: "background-color 0.1s ease, transform 0.1s ease"
1356
+ }
1357
+ }
1358
+ };
1359
+ var terminal = {
1360
+ scrollBar: {
1361
+ inactive: {
1362
+ backgroundColor: "rgba(0, 20, 5, 0.72)",
1363
+ borderRadius: "0px",
1364
+ transition: "background-color 0.15s linear"
1365
+ },
1366
+ hover: {
1367
+ backgroundColor: "rgba(0, 40, 10, 0.82)"
1368
+ },
1369
+ active: {
1370
+ backgroundColor: "rgba(0, 65, 18, 0.92)",
1371
+ transition: "background-color 0.08s linear"
1372
+ }
1373
+ },
1374
+ thumb: {
1375
+ inactive: {
1376
+ backgroundColor: "rgba(34, 197, 94, 0.62)",
1377
+ borderRadius: "0px"
1378
+ },
1379
+ hover: {
1380
+ backgroundColor: "rgba(74, 222, 128, 0.85)",
1381
+ transform: "scale(1)",
1382
+ transition: "background-color 0.1s linear"
1383
+ },
1384
+ active: {
1385
+ backgroundColor: "rgba(187, 247, 208, 1)",
1386
+ transform: "scale(1.06)",
1387
+ transition: "background-color 0.08s linear, transform 0.08s linear"
1388
+ }
1389
+ }
1390
+ };
1391
+ var toxic = {
1392
+ scrollBar: {
1393
+ inactive: {
1394
+ backgroundColor: "rgba(54, 83, 20, 0.48)",
1395
+ borderRadius: "2px",
1396
+ transition: "background-color 0.2s ease"
1397
+ },
1398
+ hover: {
1399
+ backgroundColor: "rgba(77, 124, 15, 0.65)"
1400
+ },
1401
+ active: {
1402
+ backgroundColor: "rgba(101, 163, 13, 0.82)",
1403
+ transition: "background-color 0.1s ease"
1404
+ }
1405
+ },
1406
+ thumb: {
1407
+ inactive: {
1408
+ backgroundColor: "rgba(163, 230, 53, 0.68)",
1409
+ borderRadius: "1px"
1410
+ },
1411
+ hover: {
1412
+ backgroundColor: "rgba(190, 242, 100, 0.9)",
1413
+ transform: "scale(1.04)",
1414
+ transition: "background-color 0.14s ease, transform 0.14s ease"
1415
+ },
1416
+ active: {
1417
+ backgroundColor: "rgba(236, 252, 203, 1)",
1418
+ transform: "scale(1.15)",
1419
+ transition: "background-color 0.08s ease, transform 0.08s ease"
1420
+ }
1421
+ }
1422
+ };
1423
+ var candy = {
1424
+ scrollBar: {
1425
+ inactive: {
1426
+ backgroundColor: "rgba(244, 114, 182, 0.18)",
1427
+ borderRadius: "999px",
1428
+ transition: "background-color 0.3s ease"
1429
+ },
1430
+ hover: {
1431
+ backgroundColor: "rgba(192, 132, 252, 0.28)"
1432
+ },
1433
+ active: {
1434
+ backgroundColor: "rgba(129, 140, 248, 0.42)",
1435
+ transition: "background-color 0.15s ease"
1436
+ }
1437
+ },
1438
+ thumb: {
1439
+ inactive: {
1440
+ backgroundColor: "rgba(251, 113, 133, 0.62)",
1441
+ borderRadius: "999px"
1442
+ },
1443
+ hover: {
1444
+ backgroundColor: "rgba(244, 114, 182, 0.84)",
1445
+ transform: "scale(1.03)",
1446
+ transition: "background-color 0.18s ease, transform 0.18s ease"
1447
+ },
1448
+ active: {
1449
+ backgroundColor: "rgba(224, 231, 255, 1)",
1450
+ transform: "scale(1.12)",
1451
+ transition: "background-color 0.12s ease, transform 0.12s ease"
1452
+ }
1453
+ }
1454
+ };
1455
+ var sand = {
1456
+ scrollBar: {
1457
+ inactive: {
1458
+ backgroundColor: "rgba(120, 53, 15, 0.16)",
1459
+ borderRadius: "999px",
1460
+ transition: "background-color 0.32s ease"
1461
+ },
1462
+ hover: {
1463
+ backgroundColor: "rgba(180, 83, 9, 0.25)"
1464
+ },
1465
+ active: {
1466
+ backgroundColor: "rgba(217, 119, 6, 0.38)",
1467
+ transition: "background-color 0.16s ease"
1468
+ }
1469
+ },
1470
+ thumb: {
1471
+ inactive: {
1472
+ backgroundColor: "rgba(217, 119, 6, 0.52)",
1473
+ borderRadius: "999px"
1474
+ },
1475
+ hover: {
1476
+ backgroundColor: "rgba(245, 158, 11, 0.75)",
1477
+ transform: "scale(1)",
1478
+ transition: "background-color 0.2s ease"
1479
+ },
1480
+ active: {
1481
+ backgroundColor: "rgba(253, 230, 138, 0.98)",
1482
+ transform: "scale(1.08)",
1483
+ transition: "background-color 0.15s ease, transform 0.15s ease"
1484
+ }
1485
+ }
1486
+ };
1487
+ var monoLight = {
1488
+ scrollBar: {
1489
+ inactive: {
1490
+ backgroundColor: "rgba(0, 0, 0, 0.08)",
1491
+ borderRadius: "6px",
1492
+ transition: "background-color 0.3s ease"
1493
+ },
1494
+ hover: {
1495
+ backgroundColor: "rgba(0, 0, 0, 0.14)"
1496
+ },
1497
+ active: {
1498
+ backgroundColor: "rgba(0, 0, 0, 0.22)",
1499
+ transition: "background-color 0.15s ease"
1500
+ }
1501
+ },
1502
+ thumb: {
1503
+ inactive: {
1504
+ backgroundColor: "rgba(0, 0, 0, 0.32)",
1505
+ borderRadius: "6px"
1506
+ },
1507
+ hover: {
1508
+ backgroundColor: "rgba(0, 0, 0, 0.52)",
1509
+ transform: "scale(1)",
1510
+ transition: "background-color 0.18s ease"
1511
+ },
1512
+ active: {
1513
+ backgroundColor: "rgba(0, 0, 0, 0.75)",
1514
+ transform: "scale(1.08)",
1515
+ transition: "background-color 0.12s ease, transform 0.12s ease"
1516
+ }
1517
+ }
1518
+ };
1519
+ var monoDark = {
1520
+ scrollBar: {
1521
+ inactive: {
1522
+ backgroundColor: "rgba(255, 255, 255, 0.08)",
1523
+ borderRadius: "6px",
1524
+ transition: "background-color 0.3s ease"
1525
+ },
1526
+ hover: {
1527
+ backgroundColor: "rgba(255, 255, 255, 0.14)"
1528
+ },
1529
+ active: {
1530
+ backgroundColor: "rgba(255, 255, 255, 0.22)",
1531
+ transition: "background-color 0.15s ease"
1532
+ }
1533
+ },
1534
+ thumb: {
1535
+ inactive: {
1536
+ backgroundColor: "rgba(255, 255, 255, 0.32)",
1537
+ borderRadius: "6px"
1538
+ },
1539
+ hover: {
1540
+ backgroundColor: "rgba(255, 255, 255, 0.55)",
1541
+ transform: "scale(1)",
1542
+ transition: "background-color 0.18s ease"
1543
+ },
1544
+ active: {
1545
+ backgroundColor: "rgba(255, 255, 255, 0.82)",
1546
+ transform: "scale(1.08)",
1547
+ transition: "background-color 0.12s ease, transform 0.12s ease"
1548
+ }
1549
+ }
1550
+ };
1551
+
1552
+ // src/themes/preset.ts
1553
+ var presets = {
1554
+ primary,
1555
+ midnight,
1556
+ neonCyan,
1557
+ ocean,
1558
+ deepSea,
1559
+ forest,
1560
+ moss,
1561
+ lava,
1562
+ ember,
1563
+ gold,
1564
+ roseQuartz,
1565
+ violet,
1566
+ royal,
1567
+ arctic,
1568
+ glass,
1569
+ graphite,
1570
+ terminal,
1571
+ toxic,
1572
+ candy,
1573
+ sand,
1574
+ monoLight,
1575
+ monoDark
1576
+ };
1577
+
1578
+ // src/utils/config.ts
1579
+ var defaultConfig = {
1580
+ scrollBar: {
1581
+ mode: "both",
1582
+ positionMode: "after",
1583
+ superimposition: "after",
1584
+ boundaryOffset: "4px",
1585
+ heightTrack: "98%",
1586
+ hideNativeScrollbar: "always"
1587
+ },
1588
+ thumb: {
1589
+ boundaryOffset: "1px 1px",
1590
+ heightTrack: "auto"
1591
+ },
1592
+ nativeOnMobile: true,
1593
+ selectTheme: "primary",
1594
+ optionsTheme: {}
1595
+ };
1596
+
1597
+ // src/utils/merge.ts
1598
+ var merge = (config) => {
1599
+ const selectedTheme = presets[config.selectTheme ?? defaultConfig.selectTheme];
1600
+ return {
1601
+ scrollBar: {
1602
+ ...defaultConfig.scrollBar,
1603
+ ...config.scrollBar
1604
+ },
1605
+ thumb: {
1606
+ ...defaultConfig.thumb,
1607
+ ...config.thumb
1608
+ },
1609
+ optionsTheme: themeMerge(selectedTheme, config.optionsTheme)
1610
+ };
1611
+ };
1612
+ var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1613
+ var themeMerge = (base, override) => {
1614
+ if (!override) return base;
1615
+ const result = { ...base };
1616
+ for (const key of Object.keys(override)) {
1617
+ const overrideValue = override[key];
1618
+ const baseValue = base[key];
1619
+ if (overrideValue === void 0) continue;
1620
+ if (isPlainObject(overrideValue) && isPlainObject(baseValue)) {
1621
+ result[key] = themeMerge(
1622
+ baseValue,
1623
+ overrideValue
1624
+ );
1625
+ } else {
1626
+ result[key] = overrideValue;
1627
+ }
1628
+ }
1629
+ return result;
1630
+ };
1631
+
1632
+ // src/utils/mobile-detect.ts
1633
+ var shouldUseNativeScrollbar = () => {
1634
+ if (typeof window === "undefined") {
1635
+ return true;
1636
+ }
1637
+ const primaryPointerIsCoarse = window.matchMedia("(pointer: coarse)").matches;
1638
+ const hasAnyFinePointer = window.matchMedia("(any-pointer: fine)").matches;
1639
+ return primaryPointerIsCoarse && !hasAnyFinePointer;
1640
+ };
1641
+
1642
+ // src/utils/variables-css.ts
1643
+ var variables = (theme) => {
1644
+ const styles = {};
1645
+ for (const key in theme) {
1646
+ const k = key;
1647
+ const statusTheme = theme[k];
1648
+ if (!statusTheme) continue;
1649
+ const type = k === "scrollBar" ? "scrollbar" : "thumb";
1650
+ for (const status in statusTheme) {
1651
+ const properties = statusTheme[status];
1652
+ if (!properties) continue;
1653
+ const statusPrefix = status === "inactive" ? "" : `-${status}`;
1654
+ for (const prop in properties) {
1655
+ const value = properties[prop];
1656
+ if (value === void 0) continue;
1657
+ styles[`--${type}${statusPrefix}-${prop}`] = value;
1658
+ }
1659
+ }
1660
+ }
1661
+ return styles;
1662
+ };
1663
+
1664
+ // src/ScrollToFuture.tsx
1665
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
1666
+ var ScrollToFuture = ({
1667
+ target,
1668
+ scrollBar = {},
1669
+ thumb = {},
1670
+ selectTheme = "primary",
1671
+ optionsTheme = {},
1672
+ nativeOnMobile = true
1673
+ }) => {
1674
+ const anchorRef = useRef3(null);
1675
+ const targetRef = useRef3(null);
1676
+ const mounted = useMounted();
1677
+ const [findedTarget, setFindedTarget] = useState6(null);
1678
+ const config = merge({ scrollBar, thumb, selectTheme, optionsTheme });
1679
+ console.log("hideNativeScrollbar:", {
1680
+ original: scrollBar.hideNativeScrollbar,
1681
+ merged: config.scrollBar.hideNativeScrollbar
1682
+ });
1683
+ const vars = variables(config.optionsTheme);
1684
+ const mode = config.scrollBar?.mode ?? "both";
1685
+ const positionMode = config.scrollBar?.positionMode ?? "after";
1686
+ const superimposition = config.scrollBar?.superimposition ?? "over";
1687
+ const nativeScrollOnMobile = shouldUseNativeScrollbar() && nativeOnMobile;
1688
+ const metrics = useElementScrollObserver(findedTarget);
1689
+ const rect = useTargetRect(findedTarget);
1690
+ const wantsY = mode === "vertical" || mode === "both";
1691
+ const wantsX = mode === "horizontal" || mode === "both";
1692
+ const showY = wantsY && metrics.y.canScroll;
1693
+ const showX = wantsX && metrics.x.canScroll;
1694
+ const coversAllScrollableAxes = (!metrics.x.canScroll || showX) && (!metrics.y.canScroll || showY);
1695
+ useFuture({
1696
+ target,
1697
+ anchorRef,
1698
+ targetRef,
1699
+ setFindedTarget,
1700
+ mounted,
1701
+ config,
1702
+ showY,
1703
+ showX,
1704
+ superimposition,
1705
+ findedTarget,
1706
+ positionMode,
1707
+ coversAllScrollableAxes,
1708
+ nativeOnMobile
1709
+ });
1710
+ if (!mounted || nativeScrollOnMobile) {
1711
+ return null;
1712
+ }
1713
+ const canRenderOverlay = findedTarget !== null && rect.width > 0 && rect.height > 0;
1714
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1715
+ !target && /* @__PURE__ */ jsx2(
1716
+ "span",
1717
+ {
1718
+ ref: anchorRef,
1719
+ "aria-hidden": "true",
1720
+ style: { display: "none" }
1721
+ }
1722
+ ),
1723
+ canRenderOverlay && /* @__PURE__ */ jsxs(
1724
+ "div",
1725
+ {
1726
+ className: "scroll-to-future__overlay",
1727
+ style: {
1728
+ top: rect.top,
1729
+ left: rect.left,
1730
+ width: rect.width,
1731
+ height: rect.height
1732
+ },
1733
+ children: [
1734
+ showY && /* @__PURE__ */ jsx2(
1735
+ ScrollAxis,
1736
+ {
1737
+ vars,
1738
+ axis: "y",
1739
+ target: findedTarget,
1740
+ metrics: metrics.y,
1741
+ scrollBar: config.scrollBar,
1742
+ thumb,
1743
+ positionMode,
1744
+ superimposition,
1745
+ hasCrossAxis: showX
1746
+ }
1747
+ ),
1748
+ showX && /* @__PURE__ */ jsx2(
1749
+ ScrollAxis,
1750
+ {
1751
+ vars,
1752
+ axis: "x",
1753
+ target: findedTarget,
1754
+ metrics: metrics.x,
1755
+ scrollBar: config.scrollBar,
1756
+ thumb,
1757
+ positionMode,
1758
+ superimposition,
1759
+ hasCrossAxis: showY
1760
+ }
1761
+ )
1762
+ ]
1763
+ }
1764
+ )
1765
+ ] });
1766
+ };
1767
+ export {
1768
+ ScrollToFuture
1769
+ };
1770
+ //# sourceMappingURL=index.mjs.map