scroll-arrows 0.1.0 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,8 +2,15 @@
2
2
 
3
3
  [![CI](https://github.com/dancj/scroll-arrows/actions/workflows/ci.yml/badge.svg)](https://github.com/dancj/scroll-arrows/actions/workflows/ci.yml)
4
4
  [![npm](https://img.shields.io/npm/v/scroll-arrows.svg)](https://www.npmjs.com/package/scroll-arrows)
5
- [![bundle size](https://img.shields.io/bundlephobia/minzip/scroll-arrows)](https://bundlephobia.com/package/scroll-arrows)
6
- [![license](https://img.shields.io/npm/l/scroll-arrows.svg)](./LICENSE)
5
+ [![bundle size](https://img.shields.io/bundlejs/size/scroll-arrows)](https://bundlejs.com/?q=scroll-arrows)
6
+ [![license](https://img.shields.io/github/license/dancj/scroll-arrows.svg)](./LICENSE)
7
+
8
+ <p align="center">
9
+ <picture>
10
+ <source media="(prefers-color-scheme: dark)" srcset="./assets/hero-arrow-dark.svg" />
11
+ <img alt="A hand-drawn arrow drawing itself" src="./assets/hero-arrow-light.svg" width="640" />
12
+ </picture>
13
+ </p>
7
14
 
8
15
  Hand-drawn arrows that **draw themselves between two elements as you scroll**.
9
16
  A single `roughness` knob slides from clean straight lines (0) to scratchy,
@@ -20,15 +27,15 @@ npm install scroll-arrows
20
27
  ## Vanilla
21
28
 
22
29
  ```ts
23
- import { scrollArrow } from "scroll-arrows";
30
+ import { scrollArrow } from 'scroll-arrows';
24
31
 
25
32
  const arrow = scrollArrow({
26
- start: "#box-a", // Element or CSS selector
27
- end: "#box-b",
28
- roughness: 0.7, // 0 clean → 1 scratchy
29
- stroke: "#e7e9ee",
33
+ start: '#box-a', // Element or CSS selector
34
+ end: '#box-b',
35
+ roughness: 0.7, // 0 clean → 1 scratchy
36
+ stroke: '#e7e9ee',
30
37
  strokeWidth: 2.5,
31
- head: "end", // "start" | "end" | "both" | "none"
38
+ head: 'end', // "start" | "end" | "both" | "none"
32
39
  });
33
40
 
34
41
  // later
@@ -61,8 +68,8 @@ arrow.destroy();
61
68
  ## React
62
69
 
63
70
  ```tsx
64
- import { useRef } from "react";
65
- import { ScrollArrowLine } from "scroll-arrows/react";
71
+ import { useRef } from 'react';
72
+ import { ScrollArrowLine } from 'scroll-arrows/react';
66
73
 
67
74
  function Diagram() {
68
75
  const a = useRef<HTMLDivElement>(null);
@@ -137,8 +144,12 @@ titles):
137
144
  version from the newest `v*` tag, bumps `package.json` + prepends a
138
145
  `CHANGELOG.md` entry on a `release-<version>` branch, tags `v<version>`, and
139
146
  opens a sync PR back to `staging`.
140
- 4. The pushed `v*` tag triggers `release.yml`, publishing to npm via
141
- **trusted publishing (OIDC)** with provenance (version pinned to the tag).
147
+ 4. `release-changelog.yml` then dispatches `release.yml` via `workflow_dispatch`
148
+ (a tag pushed under `GITHUB_TOKEN` cannot trigger `on: push: tags` — GitHub's
149
+ anti-recursion guard), passing the tag. `release.yml` publishes to npm via
150
+ **trusted publishing (OIDC)** with provenance (version pinned to the tag). A
151
+ `v*` tag pushed manually with your own credentials also triggers `release.yml`
152
+ directly via `on: push: tags`.
142
153
 
143
154
  Release logic lives in `scripts/*.mjs` (pure helpers + injectable-deps
144
155
  orchestrators), unit-tested under vitest.
@@ -80,8 +80,14 @@ function buildPath(ep, curvature, belly = { x: 0, y: 0 }) {
80
80
  const reach = dist * (0.3 + curvature * 0.4);
81
81
  const sn = startNormal.x || startNormal.y ? startNormal : unit(dx, dy);
82
82
  const en = endNormal.x || endNormal.y ? endNormal : unit(-dx, -dy);
83
- const c1 = { x: start.x + sn.x * reach + belly.x, y: start.y + sn.y * reach + belly.y };
84
- const c2 = { x: end.x + en.x * reach + belly.x, y: end.y + en.y * reach + belly.y };
83
+ const c1 = {
84
+ x: start.x + sn.x * reach + belly.x,
85
+ y: start.y + sn.y * reach + belly.y
86
+ };
87
+ const c2 = {
88
+ x: end.x + en.x * reach + belly.x,
89
+ y: end.y + en.y * reach + belly.y
90
+ };
85
91
  return `M ${r(start.x)} ${r(start.y)} C ${r(c1.x)} ${r(c1.y)} ${r(c2.x)} ${r(c2.y)} ${r(end.x)} ${r(end.y)}`;
86
92
  }
87
93
  function routeOffset(start, end, obstacles, padding = 14) {
@@ -201,7 +207,8 @@ function getOverlay(container) {
201
207
  (_a = document.body.style).position || (_a.position = "relative");
202
208
  } else {
203
209
  const pos = getComputedStyle(container).position;
204
- if (pos === "static") container.style.position = "relative";
210
+ if (pos === "static")
211
+ container.style.position = "relative";
205
212
  }
206
213
  container.appendChild(svg);
207
214
  overlays.set(container, svg);
@@ -457,8 +464,12 @@ var ScrollArrow = class {
457
464
  let y = pt.y;
458
465
  if (offset && total > 0) {
459
466
  const eps = Math.min(1, total / 2);
460
- const before = this.lineEl.getPointAtLength(Math.max(0, at * total - eps));
461
- const after = this.lineEl.getPointAtLength(Math.min(total, at * total + eps));
467
+ const before = this.lineEl.getPointAtLength(
468
+ Math.max(0, at * total - eps)
469
+ );
470
+ const after = this.lineEl.getPointAtLength(
471
+ Math.min(total, at * total + eps)
472
+ );
462
473
  const n = unitNormal(before, after);
463
474
  x += n.x * offset;
464
475
  y += n.y * offset;
@@ -553,5 +564,5 @@ function clampAt(t) {
553
564
  }
554
565
 
555
566
  export { ScrollArrow, easeInOutCubic };
556
- //# sourceMappingURL=chunk-HLZXSGP5.js.map
557
- //# sourceMappingURL=chunk-HLZXSGP5.js.map
567
+ //# sourceMappingURL=chunk-GKWBGFLA.js.map
568
+ //# sourceMappingURL=chunk-GKWBGFLA.js.map
package/dist/index.cjs CHANGED
@@ -88,8 +88,14 @@ function buildPath(ep, curvature, belly = { x: 0, y: 0 }) {
88
88
  const reach = dist * (0.3 + curvature * 0.4);
89
89
  const sn = startNormal.x || startNormal.y ? startNormal : unit(dx, dy);
90
90
  const en = endNormal.x || endNormal.y ? endNormal : unit(-dx, -dy);
91
- const c1 = { x: start.x + sn.x * reach + belly.x, y: start.y + sn.y * reach + belly.y };
92
- const c2 = { x: end.x + en.x * reach + belly.x, y: end.y + en.y * reach + belly.y };
91
+ const c1 = {
92
+ x: start.x + sn.x * reach + belly.x,
93
+ y: start.y + sn.y * reach + belly.y
94
+ };
95
+ const c2 = {
96
+ x: end.x + en.x * reach + belly.x,
97
+ y: end.y + en.y * reach + belly.y
98
+ };
93
99
  return `M ${r(start.x)} ${r(start.y)} C ${r(c1.x)} ${r(c1.y)} ${r(c2.x)} ${r(c2.y)} ${r(end.x)} ${r(end.y)}`;
94
100
  }
95
101
  function routeOffset(start, end, obstacles, padding = 14) {
@@ -209,7 +215,8 @@ function getOverlay(container) {
209
215
  (_a = document.body.style).position || (_a.position = "relative");
210
216
  } else {
211
217
  const pos = getComputedStyle(container).position;
212
- if (pos === "static") container.style.position = "relative";
218
+ if (pos === "static")
219
+ container.style.position = "relative";
213
220
  }
214
221
  container.appendChild(svg);
215
222
  overlays.set(container, svg);
@@ -465,8 +472,12 @@ var ScrollArrow = class {
465
472
  let y = pt.y;
466
473
  if (offset && total > 0) {
467
474
  const eps = Math.min(1, total / 2);
468
- const before = this.lineEl.getPointAtLength(Math.max(0, at * total - eps));
469
- const after = this.lineEl.getPointAtLength(Math.min(total, at * total + eps));
475
+ const before = this.lineEl.getPointAtLength(
476
+ Math.max(0, at * total - eps)
477
+ );
478
+ const after = this.lineEl.getPointAtLength(
479
+ Math.min(total, at * total + eps)
480
+ );
470
481
  const n = unitNormal(before, after);
471
482
  x += n.x * offset;
472
483
  y += n.y * offset;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as ScrollArrowOptions } from './types-DehQP2Hx.cjs';
2
- export { A as ArrowHead, E as ElementRef, P as Point, a as ScrollOptions, b as Socket } from './types-DehQP2Hx.cjs';
1
+ import { S as ScrollArrowOptions } from './types-CDd8JqZX.cjs';
2
+ export { A as ArrowHead, E as ElementRef, P as Point, a as ScrollOptions, b as Socket } from './types-CDd8JqZX.cjs';
3
3
 
4
4
  /** A single hand-drawn arrow that draws itself between two elements on scroll. */
5
5
  declare class ScrollArrow {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as ScrollArrowOptions } from './types-DehQP2Hx.js';
2
- export { A as ArrowHead, E as ElementRef, P as Point, a as ScrollOptions, b as Socket } from './types-DehQP2Hx.js';
1
+ import { S as ScrollArrowOptions } from './types-CDd8JqZX.js';
2
+ export { A as ArrowHead, E as ElementRef, P as Point, a as ScrollOptions, b as Socket } from './types-CDd8JqZX.js';
3
3
 
4
4
  /** A single hand-drawn arrow that draws itself between two elements on scroll. */
5
5
  declare class ScrollArrow {
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { ScrollArrow } from './chunk-HLZXSGP5.js';
2
- export { ScrollArrow, easeInOutCubic } from './chunk-HLZXSGP5.js';
1
+ import { ScrollArrow } from './chunk-GKWBGFLA.js';
2
+ export { ScrollArrow, easeInOutCubic } from './chunk-GKWBGFLA.js';
3
3
 
4
4
  // src/index.ts
5
5
  function scrollArrow(options) {
package/dist/react.cjs CHANGED
@@ -89,8 +89,14 @@ function buildPath(ep, curvature, belly = { x: 0, y: 0 }) {
89
89
  const reach = dist * (0.3 + curvature * 0.4);
90
90
  const sn = startNormal.x || startNormal.y ? startNormal : unit(dx, dy);
91
91
  const en = endNormal.x || endNormal.y ? endNormal : unit(-dx, -dy);
92
- const c1 = { x: start.x + sn.x * reach + belly.x, y: start.y + sn.y * reach + belly.y };
93
- const c2 = { x: end.x + en.x * reach + belly.x, y: end.y + en.y * reach + belly.y };
92
+ const c1 = {
93
+ x: start.x + sn.x * reach + belly.x,
94
+ y: start.y + sn.y * reach + belly.y
95
+ };
96
+ const c2 = {
97
+ x: end.x + en.x * reach + belly.x,
98
+ y: end.y + en.y * reach + belly.y
99
+ };
94
100
  return `M ${r(start.x)} ${r(start.y)} C ${r(c1.x)} ${r(c1.y)} ${r(c2.x)} ${r(c2.y)} ${r(end.x)} ${r(end.y)}`;
95
101
  }
96
102
  function routeOffset(start, end, obstacles, padding = 14) {
@@ -210,7 +216,8 @@ function getOverlay(container) {
210
216
  (_a = document.body.style).position || (_a.position = "relative");
211
217
  } else {
212
218
  const pos = getComputedStyle(container).position;
213
- if (pos === "static") container.style.position = "relative";
219
+ if (pos === "static")
220
+ container.style.position = "relative";
214
221
  }
215
222
  container.appendChild(svg);
216
223
  overlays.set(container, svg);
@@ -466,8 +473,12 @@ var ScrollArrow = class {
466
473
  let y = pt.y;
467
474
  if (offset && total > 0) {
468
475
  const eps = Math.min(1, total / 2);
469
- const before = this.lineEl.getPointAtLength(Math.max(0, at * total - eps));
470
- const after = this.lineEl.getPointAtLength(Math.min(total, at * total + eps));
476
+ const before = this.lineEl.getPointAtLength(
477
+ Math.max(0, at * total - eps)
478
+ );
479
+ const after = this.lineEl.getPointAtLength(
480
+ Math.min(total, at * total + eps)
481
+ );
471
482
  const n = unitNormal(before, after);
472
483
  x += n.x * offset;
473
484
  y += n.y * offset;
package/dist/react.d.cts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { RefObject } from 'react';
2
- import { S as ScrollArrowOptions } from './types-DehQP2Hx.cjs';
2
+ import { S as ScrollArrowOptions } from './types-CDd8JqZX.cjs';
3
3
 
4
4
  type Anchor = RefObject<Element | null> | Element | string;
5
- interface UseScrollArrowOptions extends Omit<ScrollArrowOptions, "start" | "end"> {
5
+ interface UseScrollArrowOptions extends Omit<ScrollArrowOptions, 'start' | 'end'> {
6
6
  start: Anchor;
7
7
  end: Anchor;
8
8
  /** Re-create the arrow when any value here changes. */
@@ -13,8 +13,7 @@ interface UseScrollArrowOptions extends Omit<ScrollArrowOptions, "start" | "end"
13
13
  * the arrow lives in an overlay <svg>, not the React tree.
14
14
  */
15
15
  declare function useScrollArrow(options: UseScrollArrowOptions): void;
16
- interface ScrollArrowProps extends UseScrollArrowOptions {
17
- }
16
+ type ScrollArrowProps = UseScrollArrowOptions;
18
17
  /** Declarative component form. Renders nothing itself. */
19
18
  declare function ScrollArrowLine(props: ScrollArrowProps): null;
20
19
 
package/dist/react.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { RefObject } from 'react';
2
- import { S as ScrollArrowOptions } from './types-DehQP2Hx.js';
2
+ import { S as ScrollArrowOptions } from './types-CDd8JqZX.js';
3
3
 
4
4
  type Anchor = RefObject<Element | null> | Element | string;
5
- interface UseScrollArrowOptions extends Omit<ScrollArrowOptions, "start" | "end"> {
5
+ interface UseScrollArrowOptions extends Omit<ScrollArrowOptions, 'start' | 'end'> {
6
6
  start: Anchor;
7
7
  end: Anchor;
8
8
  /** Re-create the arrow when any value here changes. */
@@ -13,8 +13,7 @@ interface UseScrollArrowOptions extends Omit<ScrollArrowOptions, "start" | "end"
13
13
  * the arrow lives in an overlay <svg>, not the React tree.
14
14
  */
15
15
  declare function useScrollArrow(options: UseScrollArrowOptions): void;
16
- interface ScrollArrowProps extends UseScrollArrowOptions {
17
- }
16
+ type ScrollArrowProps = UseScrollArrowOptions;
18
17
  /** Declarative component form. Renders nothing itself. */
19
18
  declare function ScrollArrowLine(props: ScrollArrowProps): null;
20
19
 
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ScrollArrow } from './chunk-HLZXSGP5.js';
1
+ import { ScrollArrow } from './chunk-GKWBGFLA.js';
2
2
  import { useRef, useEffect } from 'react';
3
3
 
4
4
  function read(a) {
@@ -3,8 +3,8 @@ type Point = {
3
3
  y: number;
4
4
  };
5
5
  /** Which edge of an element the arrow attaches to. `auto` picks the best side. */
6
- type Socket = "auto" | "top" | "bottom" | "left" | "right" | "center";
7
- type ArrowHead = "start" | "end" | "both" | "none";
6
+ type Socket = 'auto' | 'top' | 'bottom' | 'left' | 'right' | 'center';
7
+ type ArrowHead = 'start' | 'end' | 'both' | 'none';
8
8
  /** Anything we can resolve to a live DOM element. */
9
9
  type ElementRef = Element | string;
10
10
  interface ScrollOptions {
@@ -3,8 +3,8 @@ type Point = {
3
3
  y: number;
4
4
  };
5
5
  /** Which edge of an element the arrow attaches to. `auto` picks the best side. */
6
- type Socket = "auto" | "top" | "bottom" | "left" | "right" | "center";
7
- type ArrowHead = "start" | "end" | "both" | "none";
6
+ type Socket = 'auto' | 'top' | 'bottom' | 'left' | 'right' | 'center';
7
+ type ArrowHead = 'start' | 'end' | 'both' | 'none';
8
8
  /** Anything we can resolve to a live DOM element. */
9
9
  type ElementRef = Element | string;
10
10
  interface ScrollOptions {
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "scroll-arrows",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "Hand-drawn arrows that draw themselves between two elements as you scroll. Roughness goes from clean straight lines to scratchy and curvy.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "license": "MIT",
8
- "author": "Dan <dan@dorvie.com>",
8
+ "author": "Dan (https://github.com/dancj)",
9
9
  "repository": {
10
10
  "type": "git",
11
11
  "url": "git+https://github.com/dancj/scroll-arrows.git"
@@ -54,11 +54,16 @@
54
54
  "build": "tsup",
55
55
  "dev": "tsup --watch",
56
56
  "demo": "vite demo",
57
+ "gen:hero": "node scripts/genHeroSvg.mjs",
57
58
  "typecheck": "tsc --noEmit",
59
+ "lint": "eslint .",
60
+ "lint:fix": "eslint . --fix",
61
+ "format": "prettier --write .",
62
+ "format:check": "prettier --check .",
58
63
  "test": "vitest run",
59
64
  "test:watch": "vitest",
60
65
  "coverage": "vitest run --coverage",
61
- "prepublishOnly": "npm run typecheck && npm run coverage && npm run build"
66
+ "prepublishOnly": "npm run lint && npm run typecheck && npm run coverage && npm run build"
62
67
  },
63
68
  "dependencies": {
64
69
  "roughjs": "^4.6.6"
@@ -72,12 +77,18 @@
72
77
  }
73
78
  },
74
79
  "devDependencies": {
80
+ "@eslint/js": "^9.39.4",
75
81
  "@types/react": "^18.3.0",
76
82
  "@vitest/coverage-v8": "^2.1.0",
83
+ "eslint": "^9.39.4",
84
+ "eslint-config-prettier": "^9.1.2",
85
+ "eslint-plugin-react-hooks": "^5.2.0",
77
86
  "jsdom": "^25.0.0",
87
+ "prettier": "^3.8.4",
78
88
  "react": "^18.3.0",
79
89
  "tsup": "^8.0.0",
80
90
  "typescript": "^5.4.0",
91
+ "typescript-eslint": "^8.61.1",
81
92
  "vite": "^5.0.0",
82
93
  "vitest": "^2.1.0"
83
94
  }