@nordwerk/scroll-carousel 0.1.3 → 0.1.5
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/AGENTS.md +5 -66
- package/CHANGELOG.md +18 -0
- package/README.md +6 -10
- package/dist/autoplay.d.ts +13 -11
- package/dist/autoplay.js +65 -66
- package/dist/carousel.css +4 -3
- package/dist/carousel.layer.css +4 -3
- package/dist/drag.d.ts +5 -3
- package/dist/drag.js +71 -78
- package/dist/index.d.ts +2 -95
- package/dist/index.js +391 -8
- package/dist/markup.d.ts +2 -51
- package/dist/markup.js +79 -13
- package/dist/preact.d.ts +11 -8
- package/dist/preact.js +13 -21
- package/dist/react.d.ts +11 -8
- package/dist/react.js +14 -22
- package/dist/shared/adapter-D4APPfnz.d.ts +36 -0
- package/dist/shared/adapter-DhmQTrqX.js +137 -0
- package/dist/shared/index-D1Np3wzP.d.ts +120 -0
- package/dist/shared/markup-CM54igQ_.d.ts +54 -0
- package/dist/shared/math-DhvRXjtu.js +73 -0
- package/package.json +58 -53
- package/dist/adapter.d.ts +0 -55
- package/dist/math.d.ts +0 -45
- package/dist/shared/chunk-3THCBXDW.js +0 -78
- package/dist/shared/chunk-HPGJKTV5.js +0 -377
- package/dist/shared/chunk-I3QEUR7L.js +0 -109
- package/dist/shared/chunk-QHWWA243.js +0 -61
package/AGENTS.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# scroll-carousel: guide for coding agents
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
https://
|
|
3
|
+
How to build a carousel with this package in a project. Human documentation is in README.md; live
|
|
4
|
+
examples with code are at https://www.nordwerk.studio/oss/scroll-carousel. Working on the package
|
|
5
|
+
itself: see AGENTS.md at the root of https://github.com/studio-nordwerk/oss-ui.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Using the package
|
|
8
8
|
|
|
9
9
|
### The model in one paragraph
|
|
10
10
|
|
|
@@ -45,7 +45,7 @@ JavaScript, never move slides with transforms, never clone slides.
|
|
|
45
45
|
`aria-disabled:opacity-30`). Copyable example: the `#tailwind` section of the docs site.
|
|
46
46
|
12. Next.js App Router: import `<Carousel>` from `@nordwerk/scroll-carousel/react` in a Server
|
|
47
47
|
Component as is; the entry carries `'use client'`.
|
|
48
|
-
13. shadcn/ui projects: `npx shadcn@latest add studio-nordwerk/
|
|
48
|
+
13. shadcn/ui projects: `npx shadcn@latest add studio-nordwerk/oss-ui/scroll-carousel`, or a
|
|
49
49
|
block (`product-row`, `brand-teasers`, `hero-autoplay`, `image-gallery`, `logo-belt`). Compose
|
|
50
50
|
`ScrollCarousel` > `ScrollCarouselContent` > `ScrollCarouselItem`, with `ScrollCarouselPrevious`,
|
|
51
51
|
`ScrollCarouselNext`, `ScrollCarouselDots` and `ScrollCarouselPlay` inside `ScrollCarousel`;
|
|
@@ -77,64 +77,3 @@ JavaScript, never move slides with transforms, never clone slides.
|
|
|
77
77
|
mark the initial slide with `data-sc-initial` yourself.
|
|
78
78
|
- Anything else on the server: `baseStyle(props)`, `responsiveCss(id, props)` and `PRE_POSITION`
|
|
79
79
|
from `@nordwerk/scroll-carousel/markup`.
|
|
80
|
-
|
|
81
|
-
## Part 2: working on this repository
|
|
82
|
-
|
|
83
|
-
### Layout
|
|
84
|
-
|
|
85
|
-
- `src/math.ts`: pure positioning maths (snap positions, pages, visible range). Unit tested.
|
|
86
|
-
- `src/index.ts`: the controller, `attach()`. Keep it free of framework code.
|
|
87
|
-
- `src/drag.ts`, `src/autoplay.ts`: plugins against the `PluginContext` in index.ts.
|
|
88
|
-
- `src/markup.ts`: server helpers shared by the adapters.
|
|
89
|
-
- `src/adapter.ts`: the React and Preact adapter, written once against a small `Framework`
|
|
90
|
-
interface; `src/react.ts` and `src/preact.ts` only bind it.
|
|
91
|
-
- `src/astro/Carousel.astro`: shipped as source.
|
|
92
|
-
- `src/carousel.css`: all layout and the default controls. The build also writes
|
|
93
|
-
`carousel.layer.css`, the same rules inside Tailwind's components layer.
|
|
94
|
-
- `registry.json` and `registry/`: the shadcn registry, read by the shadcn CLI straight from this
|
|
95
|
-
public repository (`studio-nordwerk/scroll-carousel/<item>`). `registry/ui` is the component,
|
|
96
|
-
`registry/blocks` the blocks, `registry/shims` stand-ins that only exist for the type check.
|
|
97
|
-
`node scripts/shadcn-smoke.mjs` installs everything into a fresh shadcn project, builds and
|
|
98
|
-
renders it; CI runs it on every push.
|
|
99
|
-
- `site/`: the documentation site (`generate.mjs` writes `_site/`), including the wireframe
|
|
100
|
-
patterns in `wireframes.mjs`. Content there is fictional. CI publishes it to GitHub Pages;
|
|
101
|
-
www.nordwerk.studio/oss/scroll-carousel serves the same files through a proxy that rewrites
|
|
102
|
-
`<base href>`, so every link in the site must stay relative to `<base>`. The proxy also puts the
|
|
103
|
-
studio site's own header and footer in place of `<!--nw:header-->` (first child of `.sheet`)
|
|
104
|
-
and `<!--nw:footer-->` (right after `.sheet`); keep both, keep `<html lang="en">` and `<body>`
|
|
105
|
-
free of other attributes, keep `data-hero` on the introduction, and never style the studio's
|
|
106
|
-
class names (band, pill, brand, brand-mark, links, link, cta, foot, grid, brand-col, tag, label,
|
|
107
|
-
base) or set `--wrap` in the site CSS. Locally and on GitHub Pages the site has no header or
|
|
108
|
-
footer.
|
|
109
|
-
- `test/unit/`: node's test runner on the TypeScript sources.
|
|
110
|
-
- `test/e2e/`: Playwright against `_site/` and the adapter fixtures from `scripts/fixtures.mjs`.
|
|
111
|
-
|
|
112
|
-
### Commands
|
|
113
|
-
|
|
114
|
-
```sh
|
|
115
|
-
pnpm install
|
|
116
|
-
pnpm typecheck
|
|
117
|
-
pnpm test # unit tests, no build needed
|
|
118
|
-
pnpm build # dist/: esbuild modules plus tsc declarations
|
|
119
|
-
pnpm size # fails when an entry exceeds its gzip budget
|
|
120
|
-
pnpm site # build, then _site/
|
|
121
|
-
node scripts/fixtures.mjs # React, Preact and Astro fixtures into _site/fixtures/
|
|
122
|
-
pnpm test:e2e # Chromium, WebKit and Firefox
|
|
123
|
-
pnpm check # all of the above in order
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
Visual baselines exist per platform in `test/e2e/__screenshots__/<platform>`; update them with
|
|
127
|
-
`UPDATE_VISUAL=1 pnpm test:e2e --project=chromium visual --update-snapshots=all`.
|
|
128
|
-
|
|
129
|
-
### Conventions
|
|
130
|
-
|
|
131
|
-
- No runtime dependencies. Anything a consumer does not use must tree-shake away; new optional
|
|
132
|
-
behaviour becomes a plugin.
|
|
133
|
-
- Size budgets live in `scripts/size.mjs`. Raising one needs a reason in the commit message.
|
|
134
|
-
- Layout stays in CSS. The script reads layout (custom properties, computed styles, geometry)
|
|
135
|
-
and never writes it.
|
|
136
|
-
- Every must-have behaviour has a browser test that passes in all three engines. Fix flaky tests
|
|
137
|
-
instead of retrying them.
|
|
138
|
-
- Descriptive names, English in code, comments and docs, no project code names.
|
|
139
|
-
- Examples, fixtures and docs use fictional content. Never name or describe a client, its code,
|
|
140
|
-
its paths or its numbers in this repository, including commit messages.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.5 (2026-09-22)
|
|
4
|
+
|
|
5
|
+
- The repository is now [studio-nordwerk/oss-ui](https://github.com/studio-nordwerk/oss-ui), with
|
|
6
|
+
the package in `packages/scroll-carousel`. shadcn items install from
|
|
7
|
+
`studio-nordwerk/oss-ui/<item>`.
|
|
8
|
+
- Built with Vite+ (`vp pack`, on tsdown) instead of esbuild and tsc; the output and its sizes
|
|
9
|
+
are unchanged.
|
|
10
|
+
- React and Preact adapters, and the shadcn component: the latest options are kept in an effect
|
|
11
|
+
instead of being written to a ref during render, as React's rules require.
|
|
12
|
+
|
|
13
|
+
## 0.1.4 (2026-09-22)
|
|
14
|
+
|
|
15
|
+
- Types resolve in TypeScript projects still on `moduleResolution: "node"`, which ignores
|
|
16
|
+
`exports`: `main`, `types` and `typesVersions` point at the same files.
|
|
17
|
+
- No `engines` field any more: it asked consumers for Node 22 although the package runs in the
|
|
18
|
+
browser.
|
|
19
|
+
- Released from GitHub Actions through npm trusted publishing, with provenance.
|
|
20
|
+
|
|
3
21
|
## 0.1.3 (2026-09-22)
|
|
4
22
|
|
|
5
23
|
- `carousel.layer.css`: the stylesheet inside Tailwind's components layer, with Tailwind's layer
|
package/README.md
CHANGED
|
@@ -133,8 +133,8 @@ A component with the structure of shadcn's Carousel, on native scrolling, and fi
|
|
|
133
133
|
repository:
|
|
134
134
|
|
|
135
135
|
```sh
|
|
136
|
-
npx shadcn@latest add studio-nordwerk/
|
|
137
|
-
npx shadcn@latest add studio-nordwerk/
|
|
136
|
+
npx shadcn@latest add studio-nordwerk/oss-ui/scroll-carousel
|
|
137
|
+
npx shadcn@latest add studio-nordwerk/oss-ui/product-row
|
|
138
138
|
```
|
|
139
139
|
|
|
140
140
|
`ScrollCarousel`, `ScrollCarouselContent`, `ScrollCarouselItem`, `ScrollCarouselPrevious` and
|
|
@@ -259,14 +259,10 @@ not supported (true infinite loop, vertical, effects, virtual slides, synced thu
|
|
|
259
259
|
|
|
260
260
|
## Development
|
|
261
261
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
See [AGENTS.md](AGENTS.md) for the repository layout and conventions, and
|
|
269
|
-
[docs/testing.md](docs/testing.md) for what is tested automatically and by hand.
|
|
262
|
+
The package lives in [studio-nordwerk/oss-ui](https://github.com/studio-nordwerk/oss-ui), together
|
|
263
|
+
with the other open-source packages of Studio Nordwerk. Commands, layout and conventions are in
|
|
264
|
+
the repository's README and AGENTS.md; [docs/testing.md](docs/testing.md) says what is tested
|
|
265
|
+
automatically and by hand.
|
|
270
266
|
|
|
271
267
|
## Licence
|
|
272
268
|
|
package/dist/autoplay.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { s as Plugin } from "./shared/index-D1Np3wzP.js";
|
|
2
|
+
//#region src/autoplay.d.ts
|
|
2
3
|
export interface AutoplayOptions {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
/** Milliseconds per page. Default 5000. */
|
|
5
|
+
delay?: number;
|
|
6
|
+
/** Start running on attach. Default true, except with reduced motion. */
|
|
7
|
+
start?: boolean;
|
|
8
|
+
/** Default: [data-sc-play] inside the root. */
|
|
9
|
+
button?: HTMLElement | null;
|
|
10
|
+
labels?: {
|
|
11
|
+
play?: string;
|
|
12
|
+
pause?: string;
|
|
13
|
+
};
|
|
13
14
|
}
|
|
14
15
|
export declare const autoplay: ({ delay, start, button, labels }?: AutoplayOptions) => Plugin;
|
|
16
|
+
//#endregion
|
package/dist/autoplay.js
CHANGED
|
@@ -1,67 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
};
|
|
65
|
-
export {
|
|
66
|
-
autoplay
|
|
1
|
+
//#region src/autoplay.ts
|
|
2
|
+
const autoplay = ({ delay = 5e3, start = true, button, labels = {} } = {}) => ({ root, track, listen, layout, step, on }) => {
|
|
3
|
+
const play = button || root.querySelector("[data-sc-play]");
|
|
4
|
+
const holds = /* @__PURE__ */ new Set();
|
|
5
|
+
let playing = start && !matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
6
|
+
let timer = 0;
|
|
7
|
+
let left = delay;
|
|
8
|
+
let since = 0;
|
|
9
|
+
function sync() {
|
|
10
|
+
const running = playing && !holds.size && layout().pages.length > 1;
|
|
11
|
+
root.toggleAttribute("data-sc-playing", playing);
|
|
12
|
+
root.toggleAttribute("data-sc-paused", playing && !running);
|
|
13
|
+
play?.setAttribute("aria-label", playing ? labels.pause || "Stop automatic scrolling" : labels.play || "Start automatic scrolling");
|
|
14
|
+
if (running && !timer) {
|
|
15
|
+
since = performance.now();
|
|
16
|
+
timer = setTimeout(() => {
|
|
17
|
+
timer = 0;
|
|
18
|
+
left = delay;
|
|
19
|
+
step(1, true, true);
|
|
20
|
+
sync();
|
|
21
|
+
}, left);
|
|
22
|
+
} else if (!running && timer) {
|
|
23
|
+
clearTimeout(timer);
|
|
24
|
+
timer = 0;
|
|
25
|
+
left = Math.max(0, left - (performance.now() - since));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function set(on) {
|
|
29
|
+
playing = on;
|
|
30
|
+
left = delay;
|
|
31
|
+
clearTimeout(timer);
|
|
32
|
+
timer = 0;
|
|
33
|
+
sync();
|
|
34
|
+
}
|
|
35
|
+
const hold = (reason, on) => {
|
|
36
|
+
holds[on ? "add" : "delete"](reason);
|
|
37
|
+
sync();
|
|
38
|
+
};
|
|
39
|
+
if (track.matches(":hover")) holds.add("hover");
|
|
40
|
+
if (document.hidden) holds.add("hidden");
|
|
41
|
+
if (root.contains(document.activeElement) && !play?.contains(document.activeElement)) playing = false;
|
|
42
|
+
root.style.setProperty("--sc-autoplay-delay", `${delay}ms`);
|
|
43
|
+
on("control", () => playing && set(false));
|
|
44
|
+
on("measure", sync);
|
|
45
|
+
listen(play, "click", () => set(!playing));
|
|
46
|
+
listen(track, "pointerenter", (event) => event.pointerType == "mouse" && hold("hover", true));
|
|
47
|
+
listen(track, "pointerleave", (event) => event.pointerType == "mouse" && hold("hover", false));
|
|
48
|
+
listen(root, "focusin", (event) => !play?.contains(event.target) && playing && set(false));
|
|
49
|
+
listen(document, "visibilitychange", () => hold("hidden", document.hidden));
|
|
50
|
+
const visibility = new IntersectionObserver(([entry]) => hold("offscreen", !entry.isIntersecting));
|
|
51
|
+
visibility.observe(root);
|
|
52
|
+
sync();
|
|
53
|
+
return {
|
|
54
|
+
play: () => set(true),
|
|
55
|
+
pause: () => set(false),
|
|
56
|
+
destroy() {
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
visibility.disconnect();
|
|
59
|
+
root.removeAttribute("data-sc-playing");
|
|
60
|
+
root.removeAttribute("data-sc-paused");
|
|
61
|
+
root.style.removeProperty("--sc-autoplay-delay");
|
|
62
|
+
}
|
|
63
|
+
};
|
|
67
64
|
};
|
|
65
|
+
//#endregion
|
|
66
|
+
export { autoplay };
|
package/dist/carousel.css
CHANGED
|
@@ -34,8 +34,7 @@
|
|
|
34
34
|
--_n: min(var(--sc-per-view, 1), var(--sc-count, 1000));
|
|
35
35
|
--_gap: var(--sc-gap, 1rem);
|
|
36
36
|
--_size: calc(
|
|
37
|
-
(100cqi - var(--sc-offset-before, 0px) - var(--sc-offset-after, 0px) - (var(--_n) - 1) * var(--_gap)) /
|
|
38
|
-
var(--_n)
|
|
37
|
+
(100cqi - var(--sc-offset-before, 0px) - var(--sc-offset-after, 0px) - (var(--_n) - 1) * var(--_gap)) / var(--_n)
|
|
39
38
|
);
|
|
40
39
|
--_edge: calc((100cqi - var(--_size)) / 2 * var(--sc-centered, 0));
|
|
41
40
|
/* Also the containing block for anything absolutely positioned inside the slides, which the
|
|
@@ -194,7 +193,9 @@
|
|
|
194
193
|
border-radius: var(--sc-dot-radius, 1rem);
|
|
195
194
|
background: currentColor;
|
|
196
195
|
opacity: var(--sc-dot-idle, 0.35);
|
|
197
|
-
transition:
|
|
196
|
+
transition:
|
|
197
|
+
inline-size 0.2s ease,
|
|
198
|
+
opacity 0.2s ease;
|
|
198
199
|
}
|
|
199
200
|
|
|
200
201
|
.sc-dot[aria-current]::before {
|
package/dist/carousel.layer.css
CHANGED
|
@@ -37,8 +37,7 @@
|
|
|
37
37
|
--_n: min(var(--sc-per-view, 1), var(--sc-count, 1000));
|
|
38
38
|
--_gap: var(--sc-gap, 1rem);
|
|
39
39
|
--_size: calc(
|
|
40
|
-
(100cqi - var(--sc-offset-before, 0px) - var(--sc-offset-after, 0px) - (var(--_n) - 1) * var(--_gap)) /
|
|
41
|
-
var(--_n)
|
|
40
|
+
(100cqi - var(--sc-offset-before, 0px) - var(--sc-offset-after, 0px) - (var(--_n) - 1) * var(--_gap)) / var(--_n)
|
|
42
41
|
);
|
|
43
42
|
--_edge: calc((100cqi - var(--_size)) / 2 * var(--sc-centered, 0));
|
|
44
43
|
/* Also the containing block for anything absolutely positioned inside the slides, which the
|
|
@@ -197,7 +196,9 @@
|
|
|
197
196
|
border-radius: var(--sc-dot-radius, 1rem);
|
|
198
197
|
background: currentColor;
|
|
199
198
|
opacity: var(--sc-dot-idle, 0.35);
|
|
200
|
-
transition:
|
|
199
|
+
transition:
|
|
200
|
+
inline-size 0.2s ease,
|
|
201
|
+
opacity 0.2s ease;
|
|
201
202
|
}
|
|
202
203
|
|
|
203
204
|
.sc-dot[aria-current]::before {
|
package/dist/drag.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { s as Plugin } from "./shared/index-D1Np3wzP.js";
|
|
2
|
+
//#region src/drag.d.ts
|
|
2
3
|
export interface DragOptions {
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
/** Pixels the pointer must travel before a press becomes a drag. Default 6. */
|
|
5
|
+
threshold?: number;
|
|
5
6
|
}
|
|
6
7
|
export declare const drag: ({ threshold }?: DragOptions) => Plugin;
|
|
8
|
+
//#endregion
|
package/dist/drag.js
CHANGED
|
@@ -1,79 +1,72 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
destroy() {
|
|
72
|
-
root.removeAttribute("data-sc-drag");
|
|
73
|
-
track.removeAttribute("data-sc-dragging");
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
};
|
|
77
|
-
export {
|
|
78
|
-
drag
|
|
1
|
+
import { n as clamp, r as closest } from "./shared/math-DhvRXjtu.js";
|
|
2
|
+
//#region src/drag.ts
|
|
3
|
+
const drag = ({ threshold = 6 } = {}) => ({ root, track, listen, layout, toPage, scrollTo, grab, hold, on }) => {
|
|
4
|
+
let suppressUntil = 0;
|
|
5
|
+
let press = null;
|
|
6
|
+
const readPos = () => Math.abs(track.scrollLeft);
|
|
7
|
+
root.setAttribute("data-sc-drag", "");
|
|
8
|
+
listen(track, "dragstart", (event) => event.preventDefault());
|
|
9
|
+
listen(track, "click", (event) => {
|
|
10
|
+
if (event.timeStamp > suppressUntil) return;
|
|
11
|
+
event.preventDefault();
|
|
12
|
+
event.stopPropagation();
|
|
13
|
+
}, { capture: true });
|
|
14
|
+
on("settle", () => track.removeAttribute("data-sc-dragging"));
|
|
15
|
+
listen(track, "pointerdown", (event) => {
|
|
16
|
+
if (event.pointerType != "mouse" || event.button || !layout().state.overflow) return;
|
|
17
|
+
if (event.target.closest("input, textarea, select, label, [contenteditable], [data-sc-no-drag]")) return;
|
|
18
|
+
press = {
|
|
19
|
+
id: event.pointerId,
|
|
20
|
+
x: event.clientX,
|
|
21
|
+
left: track.scrollLeft,
|
|
22
|
+
from: readPos(),
|
|
23
|
+
moved: false,
|
|
24
|
+
trail: [[event.timeStamp, event.clientX]]
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
listen(document, "pointermove", (event) => {
|
|
28
|
+
if (!press || event.pointerId != press.id) return;
|
|
29
|
+
if (!(event.buttons & 1)) return release(event);
|
|
30
|
+
const dx = event.clientX - press.x;
|
|
31
|
+
if (!press.moved) {
|
|
32
|
+
if (Math.abs(dx) < threshold) return;
|
|
33
|
+
press.moved = true;
|
|
34
|
+
grab();
|
|
35
|
+
hold(true);
|
|
36
|
+
track.setPointerCapture(press.id);
|
|
37
|
+
track.setAttribute("data-sc-dragging", "");
|
|
38
|
+
getSelection()?.removeAllRanges();
|
|
39
|
+
}
|
|
40
|
+
track.scrollLeft = press.left - dx;
|
|
41
|
+
press.trail.push([event.timeStamp, event.clientX]);
|
|
42
|
+
while (press.trail.length > 2 && event.timeStamp - press.trail[0][0] > 100) press.trail.shift();
|
|
43
|
+
});
|
|
44
|
+
const release = (event) => {
|
|
45
|
+
const done = press;
|
|
46
|
+
if (!done || event.pointerId != done.id) return;
|
|
47
|
+
press = null;
|
|
48
|
+
if (!done.moved) return;
|
|
49
|
+
hold(false);
|
|
50
|
+
suppressUntil = event.timeStamp + 100;
|
|
51
|
+
const { pages, max, rtl, snap } = layout();
|
|
52
|
+
const [t0, x0] = done.trail[0];
|
|
53
|
+
const velocity = event.timeStamp > t0 ? (event.clientX - x0) / (event.timeStamp - t0) : 0;
|
|
54
|
+
const here = readPos();
|
|
55
|
+
const forward = rtl ? velocity : -velocity;
|
|
56
|
+
if (snap == "none") return scrollTo(clamp(here + forward * 200, 0, max));
|
|
57
|
+
const positions = pages.map((page) => page.pos);
|
|
58
|
+
const start = closest(positions, done.from);
|
|
59
|
+
let page = closest(positions, here);
|
|
60
|
+
const moved = here - done.from;
|
|
61
|
+
if (page == start && (Math.abs(moved) > 40 || Math.abs(forward) > .3)) page = clamp(start + Math.sign(Math.abs(moved) > 40 ? moved : forward), 0, pages.length - 1);
|
|
62
|
+
toPage(page);
|
|
63
|
+
};
|
|
64
|
+
listen(document, "pointerup", release);
|
|
65
|
+
listen(document, "pointercancel", release);
|
|
66
|
+
return { destroy() {
|
|
67
|
+
root.removeAttribute("data-sc-drag");
|
|
68
|
+
track.removeAttribute("data-sc-dragging");
|
|
69
|
+
} };
|
|
79
70
|
};
|
|
71
|
+
//#endregion
|
|
72
|
+
export { drag };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,95 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export type
|
|
3
|
-
export interface CarouselState {
|
|
4
|
-
/** The slide at the snap position, or the destination of a move in flight. */
|
|
5
|
-
index: number;
|
|
6
|
-
page: number;
|
|
7
|
-
pageCount: number;
|
|
8
|
-
isBeginning: boolean;
|
|
9
|
-
isEnd: boolean;
|
|
10
|
-
/** Whether the content overflows at all. Without overflow there are no controls. */
|
|
11
|
-
overflow: boolean;
|
|
12
|
-
}
|
|
13
|
-
export interface Labels {
|
|
14
|
-
/** Accessible name of a default dot. */
|
|
15
|
-
page: (n: number, count: number) => string;
|
|
16
|
-
/** Live-region text after a settled move, from the first and last visible slide (1-based). */
|
|
17
|
-
status: (first: number, last: number, count: number, slides: HTMLElement[]) => string;
|
|
18
|
-
}
|
|
19
|
-
export interface CarouselOptions {
|
|
20
|
-
/** Slides per step of next and previous: a number or 'page'. Default: --sc-group, then 1. */
|
|
21
|
-
group?: Group;
|
|
22
|
-
/** Initial slide. Default: the slide with [data-sc-initial], then 0. */
|
|
23
|
-
initial?: number;
|
|
24
|
-
/** Next at the end goes to the start and the reverse. true fades, 'scroll' scrolls back. */
|
|
25
|
-
rewind?: boolean | 'fade' | 'scroll';
|
|
26
|
-
labels?: Partial<Labels>;
|
|
27
|
-
/** Called after every settled move that changed the state, like the sc:change event. */
|
|
28
|
-
onChange?: (state: CarouselState) => void;
|
|
29
|
-
/** Default: [data-sc-prev], [data-sc-next], [data-sc-dots], [data-sc-status] inside the root. */
|
|
30
|
-
prev?: HTMLElement | null;
|
|
31
|
-
next?: HTMLElement | null;
|
|
32
|
-
dots?: HTMLElement | null;
|
|
33
|
-
status?: HTMLElement | null;
|
|
34
|
-
/** Opt-in behaviour, e.g. drag() and autoplay(). */
|
|
35
|
-
plugins?: Plugin[];
|
|
36
|
-
}
|
|
37
|
-
export interface MoveOptions {
|
|
38
|
-
/** Jump instead of scrolling smoothly. */
|
|
39
|
-
instant?: boolean;
|
|
40
|
-
}
|
|
41
|
-
export interface Carousel {
|
|
42
|
-
readonly root: HTMLElement;
|
|
43
|
-
readonly track: HTMLElement;
|
|
44
|
-
readonly slides: HTMLElement[];
|
|
45
|
-
readonly state: CarouselState;
|
|
46
|
-
readonly index: number;
|
|
47
|
-
readonly page: number;
|
|
48
|
-
readonly pageCount: number;
|
|
49
|
-
readonly isBeginning: boolean;
|
|
50
|
-
readonly isEnd: boolean;
|
|
51
|
-
next(): void;
|
|
52
|
-
prev(): void;
|
|
53
|
-
slideTo(index: number, options?: MoveOptions): void;
|
|
54
|
-
goToPage(page: number, options?: MoveOptions): void;
|
|
55
|
-
/** Measure again, e.g. after changing the direction or a custom property from script. */
|
|
56
|
-
update(): void;
|
|
57
|
-
destroy(): void;
|
|
58
|
-
/** Present when the autoplay plugin is attached. */
|
|
59
|
-
play?(): void;
|
|
60
|
-
pause?(): void;
|
|
61
|
-
}
|
|
62
|
-
export interface Layout {
|
|
63
|
-
pages: Page[];
|
|
64
|
-
max: number;
|
|
65
|
-
rtl: boolean;
|
|
66
|
-
snap: 'mandatory' | 'proximity' | 'none';
|
|
67
|
-
state: CarouselState;
|
|
68
|
-
}
|
|
69
|
-
/** What a plugin gets to work with. Listeners added through `listen` end with destroy(). */
|
|
70
|
-
export interface PluginContext {
|
|
71
|
-
root: HTMLElement;
|
|
72
|
-
track: HTMLElement;
|
|
73
|
-
listen(target: EventTarget | null | undefined, type: string, handler: (event: any) => void, options?: AddEventListenerOptions): void;
|
|
74
|
-
layout(): Layout;
|
|
75
|
-
toPage(page: number): void;
|
|
76
|
-
/** Scroll to a position, e.g. where a drag's momentum ends. */
|
|
77
|
-
scrollTo(pos: number): void;
|
|
78
|
-
/** Next or previous page; a quiet step is not announced in the live region. */
|
|
79
|
-
step(direction: 1 | -1, wrap: boolean, quiet?: boolean): void;
|
|
80
|
-
/** The user took hold of the track: a programmatic move in flight is abandoned. */
|
|
81
|
-
grab(): void;
|
|
82
|
-
/** While held, scrolling never counts as settled, e.g. during a drag. */
|
|
83
|
-
hold(on: boolean): void;
|
|
84
|
-
/** control: the user took over. settle: a move came to rest. measure: layout was read. */
|
|
85
|
-
on(event: 'control' | 'settle' | 'measure', handler: () => void): void;
|
|
86
|
-
}
|
|
87
|
-
export type Plugin = (context: PluginContext) => {
|
|
88
|
-
destroy?(): void;
|
|
89
|
-
play?(): void;
|
|
90
|
-
pause?(): void;
|
|
91
|
-
} | void;
|
|
92
|
-
/** The carousel attached to `root`, if any. */
|
|
93
|
-
export declare const getCarousel: (root: HTMLElement) => Carousel | undefined;
|
|
94
|
-
/** Attach to server-rendered markup. Attaching twice returns the same carousel. */
|
|
95
|
-
export declare function attach(root: HTMLElement, options?: CarouselOptions): Carousel;
|
|
1
|
+
import { a as Layout, c as PluginContext, d as Align, f as Geometry, i as Labels, l as attach, m as Page, n as CarouselOptions, o as MoveOptions, p as Group, r as CarouselState, s as Plugin, t as Carousel, u as getCarousel } from "./shared/index-D1Np3wzP.js";
|
|
2
|
+
export { type Align, Carousel, CarouselOptions, CarouselState, type Geometry, type Group, Labels, Layout, MoveOptions, type Page, Plugin, PluginContext, attach, getCarousel };
|