@anyknown/axis 0.0.0-stage → 0.2.1

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.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +217 -2
  3. package/dist/axis/constants.d.ts +12 -0
  4. package/dist/axis/constants.js +27 -0
  5. package/dist/axis/create-axis.d.ts +10 -0
  6. package/dist/axis/create-axis.js +212 -0
  7. package/dist/axis/create-drag-client.d.ts +6 -0
  8. package/dist/axis/create-drag-client.js +95 -0
  9. package/dist/axis/create-geometry.d.ts +2 -0
  10. package/dist/axis/create-geometry.js +11 -0
  11. package/dist/axis/create-hold.d.ts +2 -0
  12. package/dist/axis/create-hold.js +38 -0
  13. package/dist/axis/create-scroll-driver.d.ts +2 -0
  14. package/dist/axis/create-scroll-driver.js +139 -0
  15. package/dist/axis/create-snap-suspension.d.ts +2 -0
  16. package/dist/axis/create-snap-suspension.js +23 -0
  17. package/dist/axis/create-style-overrides.d.ts +2 -0
  18. package/dist/axis/create-style-overrides.js +35 -0
  19. package/dist/axis/create-wheel.d.ts +5 -0
  20. package/dist/axis/create-wheel.js +57 -0
  21. package/dist/axis/drags-along.d.ts +2 -0
  22. package/dist/axis/drags-along.js +1 -0
  23. package/dist/axis/index.d.ts +2 -0
  24. package/dist/axis/index.js +1 -0
  25. package/dist/axis/merge-options.d.ts +2 -0
  26. package/dist/axis/merge-options.js +9 -0
  27. package/dist/axis/next-axis-state.d.ts +2 -0
  28. package/dist/axis/next-axis-state.js +14 -0
  29. package/dist/axis/resolve-axis.d.ts +2 -0
  30. package/dist/axis/resolve-axis.js +8 -0
  31. package/dist/axis/types.d.ts +146 -0
  32. package/dist/axis/types.js +1 -0
  33. package/dist/coordinator/constants.d.ts +5 -0
  34. package/dist/coordinator/constants.js +5 -0
  35. package/dist/coordinator/create-coordinator.d.ts +2 -0
  36. package/dist/coordinator/create-coordinator.js +191 -0
  37. package/dist/coordinator/index.d.ts +2 -0
  38. package/dist/coordinator/index.js +1 -0
  39. package/dist/coordinator/pick-owner.d.ts +3 -0
  40. package/dist/coordinator/pick-owner.js +7 -0
  41. package/dist/coordinator/register.d.ts +2 -0
  42. package/dist/coordinator/register.js +11 -0
  43. package/dist/coordinator/types.d.ts +24 -0
  44. package/dist/coordinator/types.js +1 -0
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.js +1 -0
  47. package/dist/react/constants.d.ts +3 -0
  48. package/dist/react/constants.js +8 -0
  49. package/dist/react/index.d.ts +3 -0
  50. package/dist/react/index.js +1 -0
  51. package/dist/react/types.d.ts +12 -0
  52. package/dist/react/types.js +1 -0
  53. package/dist/react/use-applied-options.d.ts +3 -0
  54. package/dist/react/use-applied-options.js +11 -0
  55. package/dist/react/use-axis-instance.d.ts +3 -0
  56. package/dist/react/use-axis-instance.js +19 -0
  57. package/dist/react/use-axis.d.ts +11 -0
  58. package/dist/react/use-axis.js +14 -0
  59. package/dist/react/use-isomorphic-layout-effect.d.ts +2 -0
  60. package/dist/react/use-isomorphic-layout-effect.js +2 -0
  61. package/dist/react/use-selected-state.d.ts +4 -0
  62. package/dist/react/use-selected-state.js +14 -0
  63. package/dist/react/use-stable-api.d.ts +4 -0
  64. package/dist/react/use-stable-api.js +7 -0
  65. package/dist/utils/adjacent-snap.d.ts +1 -0
  66. package/dist/utils/adjacent-snap.js +8 -0
  67. package/dist/utils/cap-velocity.d.ts +2 -0
  68. package/dist/utils/cap-velocity.js +6 -0
  69. package/dist/utils/clamp.d.ts +1 -0
  70. package/dist/utils/clamp.js +1 -0
  71. package/dist/utils/composed-path-matches.d.ts +1 -0
  72. package/dist/utils/composed-path-matches.js +9 -0
  73. package/dist/utils/constants.d.ts +9 -0
  74. package/dist/utils/constants.js +19 -0
  75. package/dist/utils/css-length-to-px.d.ts +1 -0
  76. package/dist/utils/css-length-to-px.js +6 -0
  77. package/dist/utils/dominant-axis.d.ts +2 -0
  78. package/dist/utils/dominant-axis.js +3 -0
  79. package/dist/utils/ease-out-cubic.d.ts +1 -0
  80. package/dist/utils/ease-out-cubic.js +1 -0
  81. package/dist/utils/glide-duration.d.ts +1 -0
  82. package/dist/utils/glide-duration.js +9 -0
  83. package/dist/utils/index.d.ts +28 -0
  84. package/dist/utils/index.js +27 -0
  85. package/dist/utils/is-inside-editable.d.ts +1 -0
  86. package/dist/utils/is-inside-editable.js +2 -0
  87. package/dist/utils/is-on-scrollbar.d.ts +1 -0
  88. package/dist/utils/is-on-scrollbar.js +6 -0
  89. package/dist/utils/is-rtl.d.ts +1 -0
  90. package/dist/utils/is-rtl.js +1 -0
  91. package/dist/utils/is-scroll-key.d.ts +1 -0
  92. package/dist/utils/is-scroll-key.js +7 -0
  93. package/dist/utils/max-scroll.d.ts +2 -0
  94. package/dist/utils/max-scroll.js +4 -0
  95. package/dist/utils/measure-snaps.d.ts +1 -0
  96. package/dist/utils/measure-snaps.js +31 -0
  97. package/dist/utils/momentum-distance.d.ts +1 -0
  98. package/dist/utils/momentum-distance.js +4 -0
  99. package/dist/utils/momentum-step.d.ts +4 -0
  100. package/dist/utils/momentum-step.js +4 -0
  101. package/dist/utils/nearest-snap.d.ts +1 -0
  102. package/dist/utils/nearest-snap.js +12 -0
  103. package/dist/utils/parse-snap-align.d.ts +5 -0
  104. package/dist/utils/parse-snap-align.js +5 -0
  105. package/dist/utils/parse-snap-type.d.ts +4 -0
  106. package/dist/utils/parse-snap-type.js +7 -0
  107. package/dist/utils/pointer-sample.d.ts +2 -0
  108. package/dist/utils/pointer-sample.js +5 -0
  109. package/dist/utils/prefers-reduced-motion.d.ts +1 -0
  110. package/dist/utils/prefers-reduced-motion.js +1 -0
  111. package/dist/utils/read-scroll.d.ts +2 -0
  112. package/dist/utils/read-scroll.js +6 -0
  113. package/dist/utils/snap-items.d.ts +2 -0
  114. package/dist/utils/snap-items.js +18 -0
  115. package/dist/utils/snap-positions.d.ts +2 -0
  116. package/dist/utils/snap-positions.js +28 -0
  117. package/dist/utils/supported-user-select.d.ts +1 -0
  118. package/dist/utils/supported-user-select.js +1 -0
  119. package/dist/utils/to-logical.d.ts +1 -0
  120. package/dist/utils/to-logical.js +3 -0
  121. package/dist/utils/to-physical.d.ts +1 -0
  122. package/dist/utils/to-physical.js +3 -0
  123. package/dist/utils/try-capture-pointer.d.ts +1 -0
  124. package/dist/utils/try-capture-pointer.js +6 -0
  125. package/dist/utils/try-release-pointer.d.ts +1 -0
  126. package/dist/utils/try-release-pointer.js +7 -0
  127. package/dist/utils/types.d.ts +22 -0
  128. package/dist/utils/types.js +1 -0
  129. package/dist/utils/velocity-from-samples.d.ts +2 -0
  130. package/dist/utils/velocity-from-samples.js +21 -0
  131. package/dist/utils/wheel-delta-y-px.d.ts +1 -0
  132. package/dist/utils/wheel-delta-y-px.js +5 -0
  133. package/dist/utils/write-scroll.d.ts +2 -0
  134. package/dist/utils/write-scroll.js +7 -0
  135. package/package.json +84 -5
@@ -0,0 +1,38 @@
1
+ export function createHold(el) {
2
+ let held = null;
3
+ let yielded = false;
4
+ const holdCurrent = () => {
5
+ held = { x: el.scrollLeft, y: el.scrollTop };
6
+ };
7
+ return {
8
+ holdCurrent,
9
+ release() {
10
+ held = null;
11
+ },
12
+ reset() {
13
+ held = null;
14
+ yielded = false;
15
+ },
16
+ yieldDrag(glideHoldsAtItsEnd) {
17
+ yielded = true;
18
+ if (!glideHoldsAtItsEnd)
19
+ holdCurrent();
20
+ },
21
+ holdIfYielded() {
22
+ if (yielded)
23
+ holdCurrent();
24
+ },
25
+ follow(axis) {
26
+ if (held)
27
+ held[axis] = axis === "x" ? el.scrollLeft : el.scrollTop;
28
+ },
29
+ undoAutoscroll() {
30
+ if (!held)
31
+ return;
32
+ if (Math.abs(el.scrollLeft - held.x) >= 1)
33
+ el.scrollLeft = held.x;
34
+ if (Math.abs(el.scrollTop - held.y) >= 1)
35
+ el.scrollTop = held.y;
36
+ },
37
+ };
38
+ }
@@ -0,0 +1,2 @@
1
+ import type { AxisCore, Hold, ScrollDriver, SnapSuspension } from "./types.js";
2
+ export declare function createScrollDriver(core: AxisCore, snap: SnapSuspension, hold: Hold): ScrollDriver;
@@ -0,0 +1,139 @@
1
+ import { clamp, easeOutCubic, glideDuration, MIN_VELOCITY, capVelocity, momentumDistance, momentumStep, nearestSnap, prefersReducedMotion, } from "../utils/index.js";
2
+ import { AXES, MAX_FRAME_MS, WHEEL_SETTLE_MS } from "./constants.js";
3
+ import { dragsAlong } from "./drags-along.js";
4
+ export function createScrollDriver(core, snap, hold) {
5
+ const { win, rtl, scrollRange, position, writePosition, options, motion, setMotion, isDestroyed } = core;
6
+ const { suspend, resume, snapAxes } = snap;
7
+ let frame = 0;
8
+ let wheelTimer = 0;
9
+ let glideTarget = { x: 0, y: 0 };
10
+ const stop = () => {
11
+ hold.release();
12
+ if (frame)
13
+ win.cancelAnimationFrame(frame);
14
+ frame = 0;
15
+ win.clearTimeout(wheelTimer);
16
+ wheelTimer = 0;
17
+ };
18
+ const settle = () => {
19
+ stop();
20
+ hold.holdIfYielded();
21
+ resume();
22
+ setMotion("idle");
23
+ };
24
+ const jump = (target) => {
25
+ stop();
26
+ suspend();
27
+ writePosition(target);
28
+ settle();
29
+ };
30
+ const glideTo = (target, velocity, kind) => {
31
+ stop();
32
+ const isRtlNow = rtl();
33
+ const from = position(isRtlNow);
34
+ const moving = AXES.filter((axis) => target[axis] !== undefined && Math.abs(target[axis] - from[axis]) >= 0.5);
35
+ if (moving.length === 0 || prefersReducedMotion(win)) {
36
+ jump(target);
37
+ return;
38
+ }
39
+ suspend();
40
+ glideTarget = { x: target.x ?? from.x, y: target.y ?? from.y };
41
+ const distance = { x: glideTarget.x - from.x, y: glideTarget.y - from.y };
42
+ const duration = Math.max(...moving.map((axis) => glideDuration(distance[axis], velocity[axis])));
43
+ const start = win.performance.now();
44
+ setMotion(kind);
45
+ if (isDestroyed())
46
+ return;
47
+ const tick = (now) => {
48
+ if (isDestroyed())
49
+ return;
50
+ const eased = easeOutCubic(clamp((now - start) / duration, 0, 1));
51
+ const next = {};
52
+ for (const axis of moving)
53
+ next[axis] = from[axis] + distance[axis] * eased;
54
+ writePosition(next, isRtlNow);
55
+ if (eased < 1)
56
+ frame = win.requestAnimationFrame(tick);
57
+ else
58
+ settle();
59
+ };
60
+ frame = win.requestAnimationFrame(tick);
61
+ };
62
+ const runMomentum = (velocity) => {
63
+ stop();
64
+ suspend();
65
+ setMotion("momentum");
66
+ if (isDestroyed())
67
+ return;
68
+ const isRtlNow = rtl();
69
+ const max = scrollRange();
70
+ const current = position(isRtlNow, max);
71
+ const speed = { ...velocity };
72
+ let last = win.performance.now();
73
+ const tick = (now) => {
74
+ if (isDestroyed())
75
+ return;
76
+ const dt = clamp(now - last, 0, MAX_FRAME_MS);
77
+ last = now;
78
+ const next = {};
79
+ for (const axis of AXES) {
80
+ if (speed[axis] === 0)
81
+ continue;
82
+ const advance = momentumStep(speed[axis], dt);
83
+ current[axis] = clamp(current[axis] + advance.distance, 0, max[axis]);
84
+ speed[axis] = current[axis] <= 0 || current[axis] >= max[axis] ? 0 : advance.velocity;
85
+ next[axis] = current[axis];
86
+ }
87
+ writePosition(next, isRtlNow);
88
+ if (Math.hypot(speed.x, speed.y) < MIN_VELOCITY)
89
+ settle();
90
+ else
91
+ frame = win.requestAnimationFrame(tick);
92
+ };
93
+ frame = win.requestAnimationFrame(tick);
94
+ };
95
+ const nearestSnapToLanding = (speed, snaps) => {
96
+ const max = scrollRange();
97
+ const from = position(rtl(), max);
98
+ const positions = core.snapPoints();
99
+ const target = {};
100
+ for (const axis of AXES) {
101
+ const landing = clamp(from[axis] + momentumDistance(speed[axis]), 0, max[axis]);
102
+ target[axis] = snaps[axis] ? nearestSnap(positions[axis], landing) : landing;
103
+ }
104
+ return target;
105
+ };
106
+ return {
107
+ stop,
108
+ settle,
109
+ jump,
110
+ glideTo,
111
+ interrupt() {
112
+ hold.reset();
113
+ if (motion() === "momentum" || motion() === "glide")
114
+ settle();
115
+ },
116
+ fling(scrollVelocity) {
117
+ const { momentum, axis, maxGlide } = options();
118
+ const allowed = momentum && !prefersReducedMotion(win);
119
+ const speed = {
120
+ x: allowed && dragsAlong(axis, "x") ? capVelocity(scrollVelocity.x, maxGlide) : 0,
121
+ y: allowed && dragsAlong(axis, "y") ? capVelocity(scrollVelocity.y, maxGlide) : 0,
122
+ };
123
+ const snaps = snapAxes();
124
+ if (snaps.x || snaps.y)
125
+ glideTo(nearestSnapToLanding(speed, snaps), speed, "momentum");
126
+ else if (Math.hypot(speed.x, speed.y) > MIN_VELOCITY)
127
+ runMomentum(speed);
128
+ else
129
+ settle();
130
+ },
131
+ afterWheelPause(settleWheel) {
132
+ wheelTimer = win.setTimeout(() => {
133
+ wheelTimer = 0;
134
+ settleWheel();
135
+ }, WHEEL_SETTLE_MS);
136
+ },
137
+ glideTarget: () => glideTarget,
138
+ };
139
+ }
@@ -0,0 +1,2 @@
1
+ import type { SnapSuspension, StyleOverrides } from "./types.js";
2
+ export declare function createSnapSuspension(win: Window, el: HTMLElement, styles: StyleOverrides): SnapSuspension;
@@ -0,0 +1,23 @@
1
+ import { parseSnapType } from "../utils/index.js";
2
+ export function createSnapSuspension(win, el, styles) {
3
+ const { override, restore } = styles;
4
+ let suspendedSnapType = null;
5
+ return {
6
+ suspend() {
7
+ if (suspendedSnapType !== null)
8
+ return;
9
+ suspendedSnapType = win.getComputedStyle(el).scrollSnapType;
10
+ override("scroll-snap-type", "none", true);
11
+ override("scroll-behavior", "auto", true);
12
+ },
13
+ resume() {
14
+ if (suspendedSnapType === null)
15
+ return;
16
+ suspendedSnapType = null;
17
+ restore("scroll-snap-type");
18
+ restore("scroll-behavior");
19
+ },
20
+ snapAxes: () => parseSnapType(suspendedSnapType ?? win.getComputedStyle(el).scrollSnapType),
21
+ isSuspended: () => suspendedSnapType !== null,
22
+ };
23
+ }
@@ -0,0 +1,2 @@
1
+ import type { StyleOverrides } from "./types.js";
2
+ export declare function createStyleOverrides(el: HTMLElement, observer: MutationObserver, onMutations: (records: MutationRecord[]) => void): StyleOverrides;
@@ -0,0 +1,35 @@
1
+ export function createStyleOverrides(el, observer, onMutations) {
2
+ const saved = new Map();
3
+ const writeUnobserved = (write) => {
4
+ const pendingRecords = observer.takeRecords();
5
+ write();
6
+ observer.takeRecords();
7
+ if (pendingRecords.length > 0)
8
+ queueMicrotask(() => onMutations(pendingRecords));
9
+ };
10
+ const override = (prop, value, important = false) => {
11
+ if (!saved.has(prop)) {
12
+ saved.set(prop, [el.style.getPropertyValue(prop), el.style.getPropertyPriority(prop)]);
13
+ }
14
+ else if (el.style.getPropertyValue(prop) === value)
15
+ return;
16
+ writeUnobserved(() => el.style.setProperty(prop, value, important ? "important" : ""));
17
+ };
18
+ const restore = (prop) => {
19
+ const original = saved.get(prop);
20
+ if (!original)
21
+ return;
22
+ saved.delete(prop);
23
+ writeUnobserved(() => {
24
+ if (original[0])
25
+ el.style.setProperty(prop, original[0], original[1]);
26
+ else
27
+ el.style.removeProperty(prop);
28
+ });
29
+ };
30
+ const restoreAll = () => {
31
+ for (const prop of saved.keys())
32
+ restore(prop);
33
+ };
34
+ return { override, restore, restoreAll };
35
+ }
@@ -0,0 +1,5 @@
1
+ import type { AxisCore, ScrollDriver, SnapSuspension } from "./types.js";
2
+ export declare function createWheel(core: AxisCore, snap: SnapSuspension, driver: ScrollDriver): {
3
+ sync: () => void;
4
+ remove: () => void;
5
+ };
@@ -0,0 +1,57 @@
1
+ import { adjacentSnap, clamp, nearestSnap, wheelDeltaYPx } from "../utils/index.js";
2
+ import { EDGE, ZERO } from "./constants.js";
3
+ export function createWheel(core, snap, driver) {
4
+ const { el, rtl, scrollRange, position, writePosition, options, motion, setMotion, isDestroyed } = core;
5
+ let direction = 1;
6
+ let attached = false;
7
+ const settleOnNextSnapInWheelDirection = () => {
8
+ if (isDestroyed())
9
+ return;
10
+ if (!snap.snapAxes().x) {
11
+ driver.settle();
12
+ return;
13
+ }
14
+ const current = position().x;
15
+ const positions = core.snapPoints().x;
16
+ const target = adjacentSnap(positions, current - 2 * direction, direction) ?? nearestSnap(positions, current);
17
+ driver.glideTo({ x: target }, ZERO, "glide");
18
+ };
19
+ const onWheel = (event) => {
20
+ if (options().axis !== "x" || event.ctrlKey || event.shiftKey)
21
+ return;
22
+ if (Math.abs(event.deltaY) <= Math.abs(event.deltaX))
23
+ return;
24
+ if (motion() === "pressed" || motion() === "dragging")
25
+ return;
26
+ const delta = wheelDeltaYPx(event, el.clientWidth);
27
+ const isRtlNow = rtl();
28
+ const max = scrollRange();
29
+ const current = position(isRtlNow, max).x;
30
+ const leavesPageScrollingAtEdge = delta > 0 ? current >= max.x - EDGE : current <= EDGE;
31
+ if (leavesPageScrollingAtEdge)
32
+ return;
33
+ event.preventDefault();
34
+ driver.stop();
35
+ snap.suspend();
36
+ direction = delta > 0 ? 1 : -1;
37
+ writePosition({ x: clamp(current + delta, 0, max.x) }, isRtlNow);
38
+ if (motion() !== "wheel")
39
+ setMotion("wheel");
40
+ if (isDestroyed())
41
+ return;
42
+ driver.afterWheelPause(settleOnNextSnapInWheelDirection);
43
+ };
44
+ const sync = () => {
45
+ const { wheel, axis } = options();
46
+ const wanted = wheel && axis === "x";
47
+ if (wanted === attached)
48
+ return;
49
+ attached = wanted;
50
+ if (wanted)
51
+ el.addEventListener("wheel", onWheel, { passive: false });
52
+ else
53
+ el.removeEventListener("wheel", onWheel);
54
+ };
55
+ const remove = () => el.removeEventListener("wheel", onWheel);
56
+ return { sync, remove };
57
+ }
@@ -0,0 +1,2 @@
1
+ import type { AxisMode, AxisName } from "./types.js";
2
+ export declare const dragsAlong: (mode: AxisMode, axis: AxisName) => boolean;
@@ -0,0 +1 @@
1
+ export const dragsAlong = (mode, axis) => mode === "both" || mode === axis;
@@ -0,0 +1,2 @@
1
+ export { createAxis } from "./create-axis.js";
2
+ export type { AxisInstance, AxisMode, AxisName, AxisOptions, AxisPosition, AxisScrollState, AxisScrollToOptions, AxisState, } from "./types.js";
@@ -0,0 +1 @@
1
+ export { createAxis } from "./create-axis.js";
@@ -0,0 +1,2 @@
1
+ import type { AxisOptions } from "./types.js";
2
+ export declare function mergeOptions(base: Required<AxisOptions>, patch: AxisOptions): Required<AxisOptions>;
@@ -0,0 +1,9 @@
1
+ import { DEFAULTS } from "./constants.js";
2
+ export function mergeOptions(base, patch) {
3
+ const next = { ...base };
4
+ for (const key of Object.keys(DEFAULTS)) {
5
+ if (patch[key] !== undefined)
6
+ next[key] = patch[key];
7
+ }
8
+ return next;
9
+ }
@@ -0,0 +1,2 @@
1
+ import type { AxisState } from "./types.js";
2
+ export declare function nextAxisState(scroll: number, max: number, previous: AxisState): AxisState;
@@ -0,0 +1,14 @@
1
+ import { EDGE } from "./constants.js";
2
+ export function nextAxisState(scroll, max, previous) {
3
+ const next = {
4
+ scroll,
5
+ maxScroll: max,
6
+ progress: max > 0 ? scroll / max : 0,
7
+ canScrollPrev: scroll > EDGE,
8
+ canScrollNext: scroll < max - EDGE,
9
+ };
10
+ for (const key of Object.keys(next))
11
+ if (next[key] !== previous[key])
12
+ return next;
13
+ return previous;
14
+ }
@@ -0,0 +1,2 @@
1
+ import type { AxisMode, AxisName } from "./types.js";
2
+ export declare function resolveAxis(method: string, axis: AxisName | undefined, mode: AxisMode): AxisName;
@@ -0,0 +1,8 @@
1
+ export function resolveAxis(method, axis, mode) {
2
+ if (axis)
3
+ return axis;
4
+ if (mode === "both") {
5
+ throw new Error(`axis: ${method}() needs an axis ("x" or "y") when the instance uses axis: "both"`);
6
+ }
7
+ return mode;
8
+ }
@@ -0,0 +1,146 @@
1
+ import type { Vector } from "../utils/index.js";
2
+ /** The direction(s) a mouse drag scrolls: horizontal, vertical, or free 2D panning. */
3
+ export type AxisMode = "x" | "y" | "both";
4
+ /** A single scroll axis. */
5
+ export type AxisName = "x" | "y";
6
+ /** Options for {@link createAxis}. Every option can be changed later with {@link AxisInstance.setOptions}. */
7
+ export interface AxisOptions {
8
+ /**
9
+ * Which direction a mouse drag scrolls. `"x"` ignores vertical mouse movement, `"y"` ignores horizontal
10
+ * movement, and `"both"` pans freely in 2D.
11
+ * @default "x"
12
+ */
13
+ axis?: AxisMode;
14
+ /**
15
+ * Keep gliding after a fast mouse drag is released. Never applied under `prefers-reduced-motion: reduce`.
16
+ * @default true
17
+ */
18
+ momentum?: boolean;
19
+ /**
20
+ * The farthest a momentum glide may travel after one release, in px. A faster fling is slowed down to land
21
+ * within it, so one gesture never moves more than this.
22
+ * @default Infinity
23
+ */
24
+ maxGlide?: number;
25
+ /**
26
+ * Translate vertical mouse-wheel scrolling into horizontal scrolling. Only used with `axis: "x"`; at either
27
+ * end the wheel is left alone, so the page keeps scrolling.
28
+ * @default false
29
+ */
30
+ wheel?: boolean;
31
+ /**
32
+ * Set `cursor: grab` on the container while it can be dragged, and `grabbing` while dragging.
33
+ * @default true
34
+ */
35
+ cursor?: boolean;
36
+ }
37
+ /** Scroll state of one axis. Positions are logical: measured from the start edge, so positive in RTL too. */
38
+ export interface AxisState {
39
+ /** Distance scrolled from the start edge, in px (0 to `maxScroll`). */
40
+ scroll: number;
41
+ /** Largest possible `scroll`, in px. 0 when the content fits. */
42
+ maxScroll: number;
43
+ /** `scroll / maxScroll`, from 0 to 1. 0 when the content fits. */
44
+ progress: number;
45
+ /** True when the container is more than 1px away from the start edge. */
46
+ canScrollPrev: boolean;
47
+ /** True when the container is more than 1px away from the end edge. */
48
+ canScrollNext: boolean;
49
+ }
50
+ /** Scroll state of both axes, whatever `axis` is set to. */
51
+ export interface AxisScrollState {
52
+ x: AxisState;
53
+ y: AxisState;
54
+ /** True while a mouse drag is scrolling the container. */
55
+ isDragging: boolean;
56
+ /** True while the container glides after a drag is released (momentum or settling on a snap position). */
57
+ isMomentum: boolean;
58
+ }
59
+ /** A logical scroll position. Axes that are left out keep their current position. */
60
+ export interface AxisPosition {
61
+ x?: number;
62
+ y?: number;
63
+ }
64
+ /** Options for {@link AxisInstance.scrollTo}. */
65
+ export interface AxisScrollToOptions {
66
+ /** Animate the scroll. Ignored (jumps) under `prefers-reduced-motion: reduce`. @default true */
67
+ animate?: boolean;
68
+ }
69
+ /** An axis instance bound to one scroll container. */
70
+ export interface AxisInstance {
71
+ /** The current state. The object is replaced, never mutated, when a value changes. */
72
+ getState(): AxisScrollState;
73
+ /** Calls `listener` whenever the state changes. Returns a function that removes the listener. */
74
+ subscribe(listener: (state: AxisScrollState) => void): () => void;
75
+ /**
76
+ * Scrolls `axis` to the previous snap position, or back by 90% of the visible size when it does not snap.
77
+ * `axis` defaults to the configured axis and is required with `axis: "both"`.
78
+ */
79
+ scrollPrev(axis?: AxisName): void;
80
+ /**
81
+ * Scrolls `axis` to the next snap position, or forward by 90% of the visible size when it does not snap.
82
+ * `axis` defaults to the configured axis and is required with `axis: "both"`.
83
+ */
84
+ scrollNext(axis?: AxisName): void;
85
+ /** Scrolls to a logical position (px from the start edge of each axis). */
86
+ scrollTo(position: AxisPosition, options?: AxisScrollToOptions): void;
87
+ /** Changes some options. Keys that are left out or `undefined` keep their current value. */
88
+ setOptions(options: AxisOptions): void;
89
+ /**
90
+ * Re-reads the layout: clears the cached snap positions and updates the state. Only needed after a change
91
+ * axis cannot observe, such as a stylesheet or media query changing snap properties without a resize.
92
+ */
93
+ refresh(): void;
94
+ /** Removes every listener and observer and restores every inline style that axis changed. */
95
+ destroy(): void;
96
+ }
97
+ export type Motion = "idle" | "pressed" | "dragging" | "momentum" | "glide" | "wheel";
98
+ export type Glide = "momentum" | "glide";
99
+ export interface Geometry {
100
+ rtl(): boolean;
101
+ scrollRange(): Vector;
102
+ position(rtl?: boolean, range?: Vector): Vector;
103
+ writePosition(position: AxisPosition, rtl?: boolean): void;
104
+ }
105
+ export interface AxisCore extends Geometry {
106
+ el: HTMLElement;
107
+ win: Window;
108
+ options(): Required<AxisOptions>;
109
+ motion(): Motion;
110
+ setMotion(next: Motion): void;
111
+ isDestroyed(): boolean;
112
+ snapPoints(): Record<AxisName, number[]>;
113
+ }
114
+ export interface StyleOverrides {
115
+ override(prop: string, value: string, important?: boolean): void;
116
+ restore(prop: string): void;
117
+ restoreAll(): void;
118
+ }
119
+ export interface SnapSuspension {
120
+ suspend(): void;
121
+ resume(): void;
122
+ snapAxes(): {
123
+ x: boolean;
124
+ y: boolean;
125
+ };
126
+ isSuspended(): boolean;
127
+ }
128
+ export interface Hold {
129
+ holdCurrent(): void;
130
+ release(): void;
131
+ reset(): void;
132
+ yieldDrag(glideHoldsAtItsEnd: boolean): void;
133
+ holdIfYielded(): void;
134
+ follow(axis: AxisName): void;
135
+ undoAutoscroll(): void;
136
+ }
137
+ export interface ScrollDriver {
138
+ stop(): void;
139
+ settle(): void;
140
+ interrupt(): void;
141
+ jump(target: AxisPosition): void;
142
+ glideTo(target: AxisPosition, velocity: Vector, kind: Glide): void;
143
+ fling(scrollVelocity: Vector): void;
144
+ afterWheelPause(settle: () => void): void;
145
+ glideTarget(): Vector;
146
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { Coordinator } from "./types.js";
2
+ export declare const DRAG_THRESHOLD = 5;
3
+ export declare const CLICK_SUPPRESS_MS = 100;
4
+ export declare const NO_DRAG = "input, textarea, select, [contenteditable]:not([contenteditable='false']), [data-axis-no-drag]";
5
+ export declare const coordinators: WeakMap<Window, Coordinator>;
@@ -0,0 +1,5 @@
1
+ import { EDITABLE } from "../utils/index.js";
2
+ export const DRAG_THRESHOLD = 5;
3
+ export const CLICK_SUPPRESS_MS = 100;
4
+ export const NO_DRAG = `${EDITABLE}, [data-axis-no-drag]`;
5
+ export const coordinators = new WeakMap();
@@ -0,0 +1,2 @@
1
+ import type { Coordinator } from "./types.js";
2
+ export declare function createCoordinator(win: Window): Coordinator;