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 +23 -12
- package/dist/{chunk-HLZXSGP5.js → chunk-GKWBGFLA.js} +18 -7
- package/dist/index.cjs +16 -5
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/react.cjs +16 -5
- package/dist/react.d.cts +3 -4
- package/dist/react.d.ts +3 -4
- package/dist/react.js +1 -1
- package/dist/{types-DehQP2Hx.d.cts → types-CDd8JqZX.d.cts} +2 -2
- package/dist/{types-DehQP2Hx.d.ts → types-CDd8JqZX.d.ts} +2 -2
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -2,8 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/dancj/scroll-arrows/actions/workflows/ci.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/scroll-arrows)
|
|
5
|
-
[](https://bundlejs.com/?q=scroll-arrows)
|
|
6
|
+
[](./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
|
|
30
|
+
import { scrollArrow } from 'scroll-arrows';
|
|
24
31
|
|
|
25
32
|
const arrow = scrollArrow({
|
|
26
|
-
start:
|
|
27
|
-
end:
|
|
28
|
-
roughness: 0.7,
|
|
29
|
-
stroke:
|
|
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:
|
|
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
|
|
65
|
-
import { ScrollArrowLine } from
|
|
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.
|
|
141
|
-
|
|
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 = {
|
|
84
|
-
|
|
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")
|
|
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(
|
|
461
|
-
|
|
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-
|
|
557
|
-
//# sourceMappingURL=chunk-
|
|
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 = {
|
|
92
|
-
|
|
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")
|
|
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(
|
|
469
|
-
|
|
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-
|
|
2
|
-
export { A as ArrowHead, E as ElementRef, P as Point, a as ScrollOptions, b as Socket } from './types-
|
|
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-
|
|
2
|
-
export { A as ArrowHead, E as ElementRef, P as Point, a as ScrollOptions, b as Socket } from './types-
|
|
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-
|
|
2
|
-
export { ScrollArrow, easeInOutCubic } from './chunk-
|
|
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 = {
|
|
93
|
-
|
|
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")
|
|
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(
|
|
470
|
-
|
|
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-
|
|
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,
|
|
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
|
-
|
|
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-
|
|
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,
|
|
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
|
-
|
|
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
|
@@ -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 =
|
|
7
|
-
type ArrowHead =
|
|
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 =
|
|
7
|
-
type ArrowHead =
|
|
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.
|
|
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
|
|
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
|
}
|