@anyknown/axis 0.1.0 → 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 (141) hide show
  1. package/README.md +1 -0
  2. package/dist/axis/constants.d.ts +12 -0
  3. package/dist/axis/constants.js +27 -0
  4. package/dist/axis/create-axis.d.ts +10 -0
  5. package/dist/axis/create-axis.js +212 -0
  6. package/dist/axis/create-drag-client.d.ts +6 -0
  7. package/dist/axis/create-drag-client.js +95 -0
  8. package/dist/axis/create-geometry.d.ts +2 -0
  9. package/dist/axis/create-geometry.js +11 -0
  10. package/dist/axis/create-hold.d.ts +2 -0
  11. package/dist/axis/create-hold.js +38 -0
  12. package/dist/axis/create-scroll-driver.d.ts +2 -0
  13. package/dist/axis/create-scroll-driver.js +139 -0
  14. package/dist/axis/create-snap-suspension.d.ts +2 -0
  15. package/dist/axis/create-snap-suspension.js +23 -0
  16. package/dist/axis/create-style-overrides.d.ts +2 -0
  17. package/dist/axis/create-style-overrides.js +35 -0
  18. package/dist/axis/create-wheel.d.ts +5 -0
  19. package/dist/axis/create-wheel.js +57 -0
  20. package/dist/axis/drags-along.d.ts +2 -0
  21. package/dist/axis/drags-along.js +1 -0
  22. package/dist/axis/index.d.ts +2 -0
  23. package/dist/axis/index.js +1 -0
  24. package/dist/axis/merge-options.d.ts +2 -0
  25. package/dist/axis/merge-options.js +9 -0
  26. package/dist/axis/next-axis-state.d.ts +2 -0
  27. package/dist/axis/next-axis-state.js +14 -0
  28. package/dist/axis/resolve-axis.d.ts +2 -0
  29. package/dist/axis/resolve-axis.js +8 -0
  30. package/dist/{axis.d.ts → axis/types.d.ts} +57 -9
  31. package/dist/axis/types.js +1 -0
  32. package/dist/coordinator/constants.d.ts +5 -0
  33. package/dist/coordinator/constants.js +5 -0
  34. package/dist/coordinator/create-coordinator.d.ts +2 -0
  35. package/dist/coordinator/create-coordinator.js +191 -0
  36. package/dist/coordinator/index.d.ts +2 -0
  37. package/dist/coordinator/index.js +1 -0
  38. package/dist/coordinator/pick-owner.d.ts +3 -0
  39. package/dist/coordinator/pick-owner.js +7 -0
  40. package/dist/coordinator/register.d.ts +2 -0
  41. package/dist/coordinator/register.js +11 -0
  42. package/dist/coordinator/types.d.ts +24 -0
  43. package/dist/coordinator/types.js +1 -0
  44. package/dist/index.d.ts +2 -2
  45. package/dist/index.js +1 -1
  46. package/dist/react/constants.d.ts +3 -0
  47. package/dist/react/constants.js +8 -0
  48. package/dist/react/index.d.ts +3 -0
  49. package/dist/react/index.js +1 -0
  50. package/dist/react/types.d.ts +12 -0
  51. package/dist/react/types.js +1 -0
  52. package/dist/react/use-applied-options.d.ts +3 -0
  53. package/dist/react/use-applied-options.js +11 -0
  54. package/dist/react/use-axis-instance.d.ts +3 -0
  55. package/dist/react/use-axis-instance.js +19 -0
  56. package/dist/react/use-axis.d.ts +11 -0
  57. package/dist/react/use-axis.js +14 -0
  58. package/dist/react/use-isomorphic-layout-effect.d.ts +2 -0
  59. package/dist/react/use-isomorphic-layout-effect.js +2 -0
  60. package/dist/react/use-selected-state.d.ts +4 -0
  61. package/dist/react/use-selected-state.js +14 -0
  62. package/dist/react/use-stable-api.d.ts +4 -0
  63. package/dist/react/use-stable-api.js +7 -0
  64. package/dist/utils/adjacent-snap.d.ts +1 -0
  65. package/dist/utils/adjacent-snap.js +8 -0
  66. package/dist/utils/cap-velocity.d.ts +2 -0
  67. package/dist/utils/cap-velocity.js +6 -0
  68. package/dist/utils/clamp.d.ts +1 -0
  69. package/dist/utils/clamp.js +1 -0
  70. package/dist/utils/composed-path-matches.d.ts +1 -0
  71. package/dist/utils/composed-path-matches.js +9 -0
  72. package/dist/utils/constants.d.ts +9 -0
  73. package/dist/utils/constants.js +19 -0
  74. package/dist/utils/css-length-to-px.d.ts +1 -0
  75. package/dist/utils/css-length-to-px.js +6 -0
  76. package/dist/utils/dominant-axis.d.ts +2 -0
  77. package/dist/utils/dominant-axis.js +3 -0
  78. package/dist/utils/ease-out-cubic.d.ts +1 -0
  79. package/dist/utils/ease-out-cubic.js +1 -0
  80. package/dist/utils/glide-duration.d.ts +1 -0
  81. package/dist/utils/glide-duration.js +9 -0
  82. package/dist/utils/index.d.ts +28 -0
  83. package/dist/utils/index.js +27 -0
  84. package/dist/utils/is-inside-editable.d.ts +1 -0
  85. package/dist/utils/is-inside-editable.js +2 -0
  86. package/dist/utils/is-on-scrollbar.d.ts +1 -0
  87. package/dist/utils/is-on-scrollbar.js +6 -0
  88. package/dist/utils/is-rtl.d.ts +1 -0
  89. package/dist/utils/is-rtl.js +1 -0
  90. package/dist/utils/is-scroll-key.d.ts +1 -0
  91. package/dist/utils/is-scroll-key.js +7 -0
  92. package/dist/utils/max-scroll.d.ts +2 -0
  93. package/dist/utils/max-scroll.js +4 -0
  94. package/dist/utils/measure-snaps.d.ts +1 -0
  95. package/dist/utils/measure-snaps.js +31 -0
  96. package/dist/utils/momentum-distance.d.ts +1 -0
  97. package/dist/utils/momentum-distance.js +4 -0
  98. package/dist/utils/momentum-step.d.ts +4 -0
  99. package/dist/utils/momentum-step.js +4 -0
  100. package/dist/utils/nearest-snap.d.ts +1 -0
  101. package/dist/utils/nearest-snap.js +12 -0
  102. package/dist/utils/parse-snap-align.d.ts +5 -0
  103. package/dist/utils/parse-snap-align.js +5 -0
  104. package/dist/utils/parse-snap-type.d.ts +4 -0
  105. package/dist/utils/parse-snap-type.js +7 -0
  106. package/dist/utils/pointer-sample.d.ts +2 -0
  107. package/dist/utils/pointer-sample.js +5 -0
  108. package/dist/utils/prefers-reduced-motion.d.ts +1 -0
  109. package/dist/utils/prefers-reduced-motion.js +1 -0
  110. package/dist/utils/read-scroll.d.ts +2 -0
  111. package/dist/utils/read-scroll.js +6 -0
  112. package/dist/utils/snap-items.d.ts +2 -0
  113. package/dist/utils/snap-items.js +18 -0
  114. package/dist/utils/snap-positions.d.ts +2 -0
  115. package/dist/utils/snap-positions.js +28 -0
  116. package/dist/utils/supported-user-select.d.ts +1 -0
  117. package/dist/utils/supported-user-select.js +1 -0
  118. package/dist/utils/to-logical.d.ts +1 -0
  119. package/dist/utils/to-logical.js +3 -0
  120. package/dist/utils/to-physical.d.ts +1 -0
  121. package/dist/utils/to-physical.js +3 -0
  122. package/dist/utils/try-capture-pointer.d.ts +1 -0
  123. package/dist/utils/try-capture-pointer.js +6 -0
  124. package/dist/utils/try-release-pointer.d.ts +1 -0
  125. package/dist/utils/try-release-pointer.js +7 -0
  126. package/dist/utils/types.d.ts +22 -0
  127. package/dist/utils/types.js +1 -0
  128. package/dist/utils/velocity-from-samples.d.ts +2 -0
  129. package/dist/utils/velocity-from-samples.js +21 -0
  130. package/dist/utils/wheel-delta-y-px.d.ts +1 -0
  131. package/dist/utils/wheel-delta-y-px.js +5 -0
  132. package/dist/utils/write-scroll.d.ts +2 -0
  133. package/dist/utils/write-scroll.js +7 -0
  134. package/package.json +6 -5
  135. package/dist/axis.js +0 -655
  136. package/dist/coordinator.d.ts +0 -36
  137. package/dist/coordinator.js +0 -247
  138. package/dist/math.d.ts +0 -90
  139. package/dist/math.js +0 -139
  140. package/dist/react.d.ts +0 -22
  141. package/dist/react.js +0 -49
@@ -0,0 +1,7 @@
1
+ import { useMemo } from "react";
2
+ export const useStableApi = (instanceRef) => useMemo(() => ({
3
+ scrollPrev: (target) => instanceRef.current?.scrollPrev(target),
4
+ scrollNext: (target) => instanceRef.current?.scrollNext(target),
5
+ scrollTo: (position, scrollOptions) => instanceRef.current?.scrollTo(position, scrollOptions),
6
+ refresh: () => instanceRef.current?.refresh(),
7
+ }), [instanceRef]);
@@ -0,0 +1 @@
1
+ export declare function adjacentSnap(positions: readonly number[], current: number, direction: 1 | -1): number | undefined;
@@ -0,0 +1,8 @@
1
+ export function adjacentSnap(positions, current, direction) {
2
+ if (direction === 1)
3
+ return positions.find((position) => position > current + 1);
4
+ for (let i = positions.length - 1; i >= 0; i--)
5
+ if (positions[i] < current - 1)
6
+ return positions[i];
7
+ return undefined;
8
+ }
@@ -0,0 +1,2 @@
1
+ /** Scale a release velocity (px/ms) down so its momentum glide travels at most `maxGlide` px. */
2
+ export declare function capVelocity(velocity: number, maxGlide: number): number;
@@ -0,0 +1,6 @@
1
+ import { DECAY, FRAME_MS } from "./constants.js";
2
+ /** Scale a release velocity (px/ms) down so its momentum glide travels at most `maxGlide` px. */
3
+ export function capVelocity(velocity, maxGlide) {
4
+ const limit = (maxGlide * (1 - DECAY)) / FRAME_MS;
5
+ return Math.sign(velocity) * Math.min(Math.abs(velocity), limit);
6
+ }
@@ -0,0 +1 @@
1
+ export declare const clamp: (value: number, min: number, max: number) => number;
@@ -0,0 +1 @@
1
+ export const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
@@ -0,0 +1 @@
1
+ export declare function composedPathMatches(path: readonly EventTarget[], selector: string, boundary: EventTarget): boolean;
@@ -0,0 +1,9 @@
1
+ export function composedPathMatches(path, selector, boundary) {
2
+ for (const node of path) {
3
+ if (node === boundary)
4
+ return false;
5
+ if (node.nodeType === 1 && node.matches(selector))
6
+ return true;
7
+ }
8
+ return false;
9
+ }
@@ -0,0 +1,9 @@
1
+ export declare const VELOCITY_WINDOW = 100;
2
+ export declare const MAX_VELOCITY = 5;
3
+ export declare const DECAY = 0.95;
4
+ export declare const FRAME_MS: number;
5
+ export declare const MIN_VELOCITY = 0.02;
6
+ export declare const EDITABLE = "input, textarea, select, [contenteditable]:not([contenteditable='false'])";
7
+ export declare const SCROLL_KEYS: Set<string>;
8
+ export declare const EASE_OUT_CUBIC_START_SPEED = 3;
9
+ export declare const LINE_HEIGHT = 16;
@@ -0,0 +1,19 @@
1
+ export const VELOCITY_WINDOW = 100;
2
+ export const MAX_VELOCITY = 5;
3
+ export const DECAY = 0.95;
4
+ export const FRAME_MS = 1000 / 60;
5
+ export const MIN_VELOCITY = 0.02;
6
+ export const EDITABLE = "input, textarea, select, [contenteditable]:not([contenteditable='false'])";
7
+ export const SCROLL_KEYS = new Set([
8
+ "ArrowUp",
9
+ "ArrowDown",
10
+ "ArrowLeft",
11
+ "ArrowRight",
12
+ "PageUp",
13
+ "PageDown",
14
+ "Home",
15
+ "End",
16
+ " ",
17
+ ]);
18
+ export const EASE_OUT_CUBIC_START_SPEED = 3;
19
+ export const LINE_HEIGHT = 16;
@@ -0,0 +1 @@
1
+ export declare function cssLengthToPx(value: string, percentBasis: number): number;
@@ -0,0 +1,6 @@
1
+ export function cssLengthToPx(value, percentBasis) {
2
+ const number = Number.parseFloat(value);
3
+ if (Number.isNaN(number))
4
+ return 0;
5
+ return value.endsWith("%") ? (percentBasis * number) / 100 : number;
6
+ }
@@ -0,0 +1,2 @@
1
+ import type { Vector } from "./types.js";
2
+ export declare function dominantAxis(delta: Vector): "x" | "y";
@@ -0,0 +1,3 @@
1
+ export function dominantAxis(delta) {
2
+ return Math.abs(delta.x) >= Math.abs(delta.y) ? "x" : "y";
3
+ }
@@ -0,0 +1 @@
1
+ export declare const easeOutCubic: (t: number) => number;
@@ -0,0 +1 @@
1
+ export const easeOutCubic = (t) => 1 - (1 - t) ** 3;
@@ -0,0 +1 @@
1
+ export declare function glideDuration(distance: number, velocity?: number): number;
@@ -0,0 +1,9 @@
1
+ import { clamp } from "./clamp.js";
2
+ import { EASE_OUT_CUBIC_START_SPEED } from "./constants.js";
3
+ export function glideDuration(distance, velocity = 0) {
4
+ const d = Math.abs(distance);
5
+ const continuesRelease = velocity !== 0 && Math.sign(velocity) === Math.sign(distance);
6
+ if (continuesRelease)
7
+ return clamp((EASE_OUT_CUBIC_START_SPEED * d) / Math.abs(velocity), 180, 900);
8
+ return clamp(200 + d * 0.5, 200, 600);
9
+ }
@@ -0,0 +1,28 @@
1
+ export { adjacentSnap } from "./adjacent-snap.js";
2
+ export { capVelocity } from "./cap-velocity.js";
3
+ export { clamp } from "./clamp.js";
4
+ export { composedPathMatches } from "./composed-path-matches.js";
5
+ export { dominantAxis } from "./dominant-axis.js";
6
+ export { easeOutCubic } from "./ease-out-cubic.js";
7
+ export { glideDuration } from "./glide-duration.js";
8
+ export { isOnScrollbar } from "./is-on-scrollbar.js";
9
+ export { isRtl } from "./is-rtl.js";
10
+ export { isScrollKey } from "./is-scroll-key.js";
11
+ export { maxScroll } from "./max-scroll.js";
12
+ export { measureSnaps } from "./measure-snaps.js";
13
+ export { momentumDistance } from "./momentum-distance.js";
14
+ export { momentumStep } from "./momentum-step.js";
15
+ export { nearestSnap } from "./nearest-snap.js";
16
+ export { parseSnapType } from "./parse-snap-type.js";
17
+ export { pointerSample } from "./pointer-sample.js";
18
+ export { prefersReducedMotion } from "./prefers-reduced-motion.js";
19
+ export { readScroll } from "./read-scroll.js";
20
+ export { supportedUserSelect } from "./supported-user-select.js";
21
+ export { toLogical } from "./to-logical.js";
22
+ export { tryCapturePointer } from "./try-capture-pointer.js";
23
+ export { tryReleasePointer } from "./try-release-pointer.js";
24
+ export { velocityFromSamples } from "./velocity-from-samples.js";
25
+ export { wheelDeltaYPx } from "./wheel-delta-y-px.js";
26
+ export { writeScroll } from "./write-scroll.js";
27
+ export { EDITABLE, MIN_VELOCITY, VELOCITY_WINDOW } from "./constants.js";
28
+ export type { Sample, Vector } from "./types.js";
@@ -0,0 +1,27 @@
1
+ export { adjacentSnap } from "./adjacent-snap.js";
2
+ export { capVelocity } from "./cap-velocity.js";
3
+ export { clamp } from "./clamp.js";
4
+ export { composedPathMatches } from "./composed-path-matches.js";
5
+ export { dominantAxis } from "./dominant-axis.js";
6
+ export { easeOutCubic } from "./ease-out-cubic.js";
7
+ export { glideDuration } from "./glide-duration.js";
8
+ export { isOnScrollbar } from "./is-on-scrollbar.js";
9
+ export { isRtl } from "./is-rtl.js";
10
+ export { isScrollKey } from "./is-scroll-key.js";
11
+ export { maxScroll } from "./max-scroll.js";
12
+ export { measureSnaps } from "./measure-snaps.js";
13
+ export { momentumDistance } from "./momentum-distance.js";
14
+ export { momentumStep } from "./momentum-step.js";
15
+ export { nearestSnap } from "./nearest-snap.js";
16
+ export { parseSnapType } from "./parse-snap-type.js";
17
+ export { pointerSample } from "./pointer-sample.js";
18
+ export { prefersReducedMotion } from "./prefers-reduced-motion.js";
19
+ export { readScroll } from "./read-scroll.js";
20
+ export { supportedUserSelect } from "./supported-user-select.js";
21
+ export { toLogical } from "./to-logical.js";
22
+ export { tryCapturePointer } from "./try-capture-pointer.js";
23
+ export { tryReleasePointer } from "./try-release-pointer.js";
24
+ export { velocityFromSamples } from "./velocity-from-samples.js";
25
+ export { wheelDeltaYPx } from "./wheel-delta-y-px.js";
26
+ export { writeScroll } from "./write-scroll.js";
27
+ export { EDITABLE, MIN_VELOCITY, VELOCITY_WINDOW } from "./constants.js";
@@ -0,0 +1 @@
1
+ export declare const isInsideEditable: (target: EventTarget | null) => boolean;
@@ -0,0 +1,2 @@
1
+ import { EDITABLE } from "./constants.js";
2
+ export const isInsideEditable = (target) => !!target?.closest?.(EDITABLE);
@@ -0,0 +1 @@
1
+ export declare function isOnScrollbar(el: Element, event: MouseEvent): boolean;
@@ -0,0 +1,6 @@
1
+ export function isOnScrollbar(el, event) {
2
+ const rect = el.getBoundingClientRect();
3
+ const x = event.clientX - rect.left - el.clientLeft;
4
+ const y = event.clientY - rect.top - el.clientTop;
5
+ return x < 0 || y < 0 || x >= el.clientWidth || y >= el.clientHeight;
6
+ }
@@ -0,0 +1 @@
1
+ export declare const isRtl: (win: Window, el: Element) => boolean;
@@ -0,0 +1 @@
1
+ export const isRtl = (win, el) => win.getComputedStyle(el).direction === "rtl";
@@ -0,0 +1 @@
1
+ export declare function isScrollKey(event: KeyboardEvent): boolean;
@@ -0,0 +1,7 @@
1
+ import { SCROLL_KEYS } from "./constants.js";
2
+ import { isInsideEditable } from "./is-inside-editable.js";
3
+ export function isScrollKey(event) {
4
+ if (!SCROLL_KEYS.has(event.key))
5
+ return false;
6
+ return !isInsideEditable(event.target);
7
+ }
@@ -0,0 +1,2 @@
1
+ import type { Vector } from "./types.js";
2
+ export declare const maxScroll: (el: Element) => Vector;
@@ -0,0 +1,4 @@
1
+ export const maxScroll = (el) => ({
2
+ x: Math.max(0, el.scrollWidth - el.clientWidth),
3
+ y: Math.max(0, el.scrollHeight - el.clientHeight),
4
+ });
@@ -0,0 +1 @@
1
+ export declare function measureSnaps(win: Window, el: Element): Record<"x" | "y", number[]>;
@@ -0,0 +1,31 @@
1
+ import { cssLengthToPx } from "./css-length-to-px.js";
2
+ import { maxScroll } from "./max-scroll.js";
3
+ import { snapItems } from "./snap-items.js";
4
+ import { snapPositions } from "./snap-positions.js";
5
+ export function measureSnaps(win, el) {
6
+ const style = win.getComputedStyle(el);
7
+ const rect = el.getBoundingClientRect();
8
+ const max = maxScroll(el);
9
+ const items = snapItems(win, el);
10
+ const padding = (side, basis) => cssLengthToPx(style.getPropertyValue(`scroll-padding-${side}`), basis);
11
+ return {
12
+ x: snapPositions({
13
+ start: rect.left + el.clientLeft,
14
+ size: el.clientWidth,
15
+ paddingStart: padding("left", el.clientWidth),
16
+ paddingEnd: padding("right", el.clientWidth),
17
+ scroll: el.scrollLeft,
18
+ maxScroll: max.x,
19
+ rtl: style.direction === "rtl",
20
+ }, items.x),
21
+ y: snapPositions({
22
+ start: rect.top + el.clientTop,
23
+ size: el.clientHeight,
24
+ paddingStart: padding("top", el.clientHeight),
25
+ paddingEnd: padding("bottom", el.clientHeight),
26
+ scroll: el.scrollTop,
27
+ maxScroll: max.y,
28
+ rtl: false,
29
+ }, items.y),
30
+ };
31
+ }
@@ -0,0 +1 @@
1
+ export declare function momentumDistance(velocity: number): number;
@@ -0,0 +1,4 @@
1
+ import { DECAY, FRAME_MS } from "./constants.js";
2
+ export function momentumDistance(velocity) {
3
+ return (velocity * FRAME_MS) / (1 - DECAY);
4
+ }
@@ -0,0 +1,4 @@
1
+ export declare function momentumStep(velocity: number, dt: number): {
2
+ distance: number;
3
+ velocity: number;
4
+ };
@@ -0,0 +1,4 @@
1
+ import { DECAY, FRAME_MS } from "./constants.js";
2
+ export function momentumStep(velocity, dt) {
3
+ return { distance: velocity * dt, velocity: velocity * DECAY ** (dt / FRAME_MS) };
4
+ }
@@ -0,0 +1 @@
1
+ export declare function nearestSnap(positions: readonly number[], target: number): number;
@@ -0,0 +1,12 @@
1
+ export function nearestSnap(positions, target) {
2
+ let best = target;
3
+ let bestDistance = Infinity;
4
+ for (const position of positions) {
5
+ const distance = Math.abs(position - target);
6
+ if (distance < bestDistance) {
7
+ best = position;
8
+ bestDistance = distance;
9
+ }
10
+ }
11
+ return best;
12
+ }
@@ -0,0 +1,5 @@
1
+ import type { SnapAlign } from "./types.js";
2
+ export declare function parseSnapAlign(value: string): {
3
+ x: SnapAlign;
4
+ y: SnapAlign;
5
+ };
@@ -0,0 +1,5 @@
1
+ const toAlign = (value) => value === "start" || value === "center" || value === "end" ? value : "none";
2
+ export function parseSnapAlign(value) {
3
+ const [block, inline = block] = value.trim().split(/\s+/);
4
+ return { x: toAlign(inline), y: toAlign(block) };
5
+ }
@@ -0,0 +1,4 @@
1
+ export declare function parseSnapType(value: string): {
2
+ x: boolean;
3
+ y: boolean;
4
+ };
@@ -0,0 +1,7 @@
1
+ export function parseSnapType(value) {
2
+ const axis = value.trim().split(/\s+/)[0];
3
+ return {
4
+ x: axis === "x" || axis === "both" || axis === "inline",
5
+ y: axis === "y" || axis === "both" || axis === "block",
6
+ };
7
+ }
@@ -0,0 +1,2 @@
1
+ import type { Sample } from "./types.js";
2
+ export declare const pointerSample: (event: PointerEvent) => Sample;
@@ -0,0 +1,5 @@
1
+ export const pointerSample = (event) => ({
2
+ t: event.timeStamp,
3
+ x: event.clientX,
4
+ y: event.clientY,
5
+ });
@@ -0,0 +1 @@
1
+ export declare const prefersReducedMotion: (win: Window) => boolean;
@@ -0,0 +1 @@
1
+ export const prefersReducedMotion = (win) => win.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
@@ -0,0 +1,2 @@
1
+ import type { Vector } from "./types.js";
2
+ export declare const readScroll: (el: Element, rtl: boolean, max: Vector) => Vector;
@@ -0,0 +1,6 @@
1
+ import { clamp } from "./clamp.js";
2
+ import { toLogical } from "./to-logical.js";
3
+ export const readScroll = (el, rtl, max) => ({
4
+ x: clamp(toLogical(el.scrollLeft, rtl), 0, max.x),
5
+ y: clamp(el.scrollTop, 0, max.y),
6
+ });
@@ -0,0 +1,2 @@
1
+ import type { SnapItem } from "./types.js";
2
+ export declare function snapItems(win: Window, el: Element): Record<"x" | "y", SnapItem[]>;
@@ -0,0 +1,18 @@
1
+ import { cssLengthToPx } from "./css-length-to-px.js";
2
+ import { parseSnapAlign } from "./parse-snap-align.js";
3
+ export function snapItems(win, el) {
4
+ const items = { x: [], y: [] };
5
+ for (const child of el.children) {
6
+ const childStyle = win.getComputedStyle(child);
7
+ const align = parseSnapAlign(childStyle.scrollSnapAlign);
8
+ if (align.x === "none" && align.y === "none")
9
+ continue;
10
+ const box = child.getBoundingClientRect();
11
+ const margin = (side) => cssLengthToPx(childStyle.getPropertyValue(`scroll-margin-${side}`), 0);
12
+ const left = margin("left");
13
+ const top = margin("top");
14
+ items.x.push({ start: box.left - left, size: box.width + left + margin("right"), align: align.x });
15
+ items.y.push({ start: box.top - top, size: box.height + top + margin("bottom"), align: align.y });
16
+ }
17
+ return items;
18
+ }
@@ -0,0 +1,2 @@
1
+ import type { SnapAxis, SnapItem } from "./types.js";
2
+ export declare function snapPositions(axis: SnapAxis, items: readonly SnapItem[]): number[];
@@ -0,0 +1,28 @@
1
+ import { clamp } from "./clamp.js";
2
+ import { toLogical } from "./to-logical.js";
3
+ export function snapPositions(axis, items) {
4
+ const { rtl } = axis;
5
+ const portLow = axis.start + axis.paddingStart;
6
+ const portHigh = axis.start + axis.size - axis.paddingEnd;
7
+ const positions = [];
8
+ for (const item of items) {
9
+ if (item.align === "none")
10
+ continue;
11
+ const high = item.start + item.size;
12
+ const alignsLowEdge = (item.align === "start") !== rtl;
13
+ const delta = item.align === "center"
14
+ ? (item.start + high) / 2 - (portLow + portHigh) / 2
15
+ : alignsLowEdge
16
+ ? item.start - portLow
17
+ : high - portHigh;
18
+ positions.push(clamp(toLogical(axis.scroll + delta, rtl), 0, axis.maxScroll));
19
+ }
20
+ positions.sort((a, b) => a - b);
21
+ const unique = [];
22
+ for (const position of positions) {
23
+ const previous = unique.at(-1);
24
+ if (previous === undefined || position - previous > 1)
25
+ unique.push(position);
26
+ }
27
+ return unique;
28
+ }
@@ -0,0 +1 @@
1
+ export declare const supportedUserSelect: (style: CSSStyleDeclaration) => "user-select" | "-webkit-user-select";
@@ -0,0 +1 @@
1
+ export const supportedUserSelect = (style) => "userSelect" in style ? "user-select" : "-webkit-user-select";
@@ -0,0 +1 @@
1
+ export declare function toLogical(scrollLeft: number, rtl: boolean): number;
@@ -0,0 +1,3 @@
1
+ export function toLogical(scrollLeft, rtl) {
2
+ return rtl ? -scrollLeft : scrollLeft;
3
+ }
@@ -0,0 +1 @@
1
+ export declare function toPhysical(logical: number, rtl: boolean): number;
@@ -0,0 +1,3 @@
1
+ export function toPhysical(logical, rtl) {
2
+ return rtl ? -logical : logical;
3
+ }
@@ -0,0 +1 @@
1
+ export declare function tryCapturePointer(el: Element, pointerId: number): void;
@@ -0,0 +1,6 @@
1
+ export function tryCapturePointer(el, pointerId) {
2
+ try {
3
+ el.setPointerCapture(pointerId);
4
+ }
5
+ catch { }
6
+ }
@@ -0,0 +1 @@
1
+ export declare function tryReleasePointer(el: Element, pointerId: number): void;
@@ -0,0 +1,7 @@
1
+ export function tryReleasePointer(el, pointerId) {
2
+ try {
3
+ if (el.hasPointerCapture(pointerId))
4
+ el.releasePointerCapture(pointerId);
5
+ }
6
+ catch { }
7
+ }
@@ -0,0 +1,22 @@
1
+ export interface Vector {
2
+ x: number;
3
+ y: number;
4
+ }
5
+ export interface Sample extends Vector {
6
+ t: number;
7
+ }
8
+ export type SnapAlign = "start" | "center" | "end" | "none";
9
+ export interface SnapAxis {
10
+ start: number;
11
+ size: number;
12
+ paddingStart: number;
13
+ paddingEnd: number;
14
+ scroll: number;
15
+ maxScroll: number;
16
+ rtl: boolean;
17
+ }
18
+ export interface SnapItem {
19
+ start: number;
20
+ size: number;
21
+ align: SnapAlign;
22
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { Sample, Vector } from "./types.js";
2
+ export declare function velocityFromSamples(samples: readonly Sample[], window?: number): Vector;
@@ -0,0 +1,21 @@
1
+ import { clamp } from "./clamp.js";
2
+ import { MAX_VELOCITY, VELOCITY_WINDOW } from "./constants.js";
3
+ export function velocityFromSamples(samples, window = VELOCITY_WINDOW) {
4
+ const last = samples.at(-1);
5
+ if (!last)
6
+ return { x: 0, y: 0 };
7
+ let first = last;
8
+ for (let i = samples.length - 2; i >= 0; i--) {
9
+ const sample = samples[i];
10
+ if (last.t - sample.t > window)
11
+ break;
12
+ first = sample;
13
+ }
14
+ const dt = last.t - first.t;
15
+ if (dt <= 0)
16
+ return { x: 0, y: 0 };
17
+ return {
18
+ x: clamp((last.x - first.x) / dt, -MAX_VELOCITY, MAX_VELOCITY),
19
+ y: clamp((last.y - first.y) / dt, -MAX_VELOCITY, MAX_VELOCITY),
20
+ };
21
+ }
@@ -0,0 +1 @@
1
+ export declare function wheelDeltaYPx(event: WheelEvent, pagePx: number): number;
@@ -0,0 +1,5 @@
1
+ import { LINE_HEIGHT } from "./constants.js";
2
+ export function wheelDeltaYPx(event, pagePx) {
3
+ const unitPx = event.deltaMode === 1 ? LINE_HEIGHT : event.deltaMode === 2 ? pagePx : 1;
4
+ return event.deltaY * unitPx;
5
+ }
@@ -0,0 +1,2 @@
1
+ import type { Vector } from "./types.js";
2
+ export declare function writeScroll(el: Element, position: Partial<Vector>, rtl: boolean): void;
@@ -0,0 +1,7 @@
1
+ import { toPhysical } from "./to-physical.js";
2
+ export function writeScroll(el, position, rtl) {
3
+ if (position.x !== undefined)
4
+ el.scrollLeft = toPhysical(position.x, rtl);
5
+ if (position.y !== undefined)
6
+ el.scrollTop = position.y;
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anyknown/axis",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Headless scroll primitive for x, y and 2D panning: native scroll first, mouse drag as an enhancement",
5
5
  "keywords": [
6
6
  "carousel",
@@ -30,7 +30,7 @@
30
30
  "typesVersions": {
31
31
  "*": {
32
32
  "react": [
33
- "./dist/react.d.ts"
33
+ "./dist/react/index.d.ts"
34
34
  ]
35
35
  }
36
36
  },
@@ -40,8 +40,8 @@
40
40
  "default": "./dist/index.js"
41
41
  },
42
42
  "./react": {
43
- "types": "./dist/react.d.ts",
44
- "default": "./dist/react.js"
43
+ "types": "./dist/react/index.d.ts",
44
+ "default": "./dist/react/index.js"
45
45
  }
46
46
  },
47
47
  "publishConfig": {
@@ -50,7 +50,7 @@
50
50
  "scripts": {
51
51
  "build": "rm -rf dist && tsc -p tsconfig.build.json && node scripts/esm-extensions.mjs",
52
52
  "typecheck": "tsc --noEmit",
53
- "lint": "oxlint src test playground",
53
+ "lint": "oxlint --deny-warnings src test playground scripts vitest.config.ts",
54
54
  "fmt": "oxfmt",
55
55
  "fmt:check": "oxfmt --check",
56
56
  "check": "pnpm typecheck && pnpm lint && pnpm fmt:check",
@@ -59,6 +59,7 @@
59
59
  "prepublishOnly": "pnpm check && pnpm test && pnpm build"
60
60
  },
61
61
  "devDependencies": {
62
+ "@stylistic/eslint-plugin": "5.10.0",
62
63
  "@types/node": "^26.4.0",
63
64
  "@types/react": "^19.2.0",
64
65
  "@types/react-dom": "^19.2.5",