@ailuracode/alpine-carousel 0.1.0 → 1.0.0
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 +4 -4
- package/dist/global.d.ts +11 -50
- package/dist/index.d.ts +122 -15
- package/dist/index.js +348 -242
- package/package.json +6 -2
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -5,14 +5,14 @@ Headless accessible carousel store for Alpine.js, powered by [Embla Carousel](ht
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
pnpm add @ailuracode/alpine-carousel alpinejs
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Peer dependencies: `embla-carousel` and `embla-carousel-autoplay` are bundled as direct dependencies.
|
|
12
12
|
|
|
13
13
|
## Setup
|
|
14
14
|
|
|
15
|
-
```
|
|
15
|
+
```ts
|
|
16
16
|
import Alpine from "alpinejs";
|
|
17
17
|
import carousel from "@ailuracode/alpine-carousel";
|
|
18
18
|
|
|
@@ -34,8 +34,8 @@ Alpine.start();
|
|
|
34
34
|
<section x-bind="$store.carousel.carouselProps('gallery', { label: 'Featured gallery' })">
|
|
35
35
|
<div x-ref="viewport" x-bind="$store.carousel.viewportProps('gallery')" class="min-w-0 w-full overflow-hidden">
|
|
36
36
|
<div class="flex touch-pan-y pinch-zoom">
|
|
37
|
-
<div class="min-w-0 shrink-0 grow-0 [
|
|
38
|
-
<div class="min-w-0 shrink-0 grow-0 [
|
|
37
|
+
<div class="min-w-0 shrink-0 grow-0 flex-[0_0_var(--slide-size,100%)]" x-bind="$store.carousel.slideProps('gallery', 0)">Slide 1</div>
|
|
38
|
+
<div class="min-w-0 shrink-0 grow-0 flex-[0_0_var(--slide-size,100%)]" x-bind="$store.carousel.slideProps('gallery', 1)">Slide 2</div>
|
|
39
39
|
</div>
|
|
40
40
|
</div>
|
|
41
41
|
|
package/dist/global.d.ts
CHANGED
|
@@ -1,57 +1,18 @@
|
|
|
1
1
|
/// <reference types="@types/alpinejs" />
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
delay?: number;
|
|
5
|
-
stopOnInteraction?: boolean;
|
|
6
|
-
stopOnMouseEnter?: boolean;
|
|
7
|
-
stopOnFocusIn?: boolean;
|
|
8
|
-
stopWhenHidden?: boolean;
|
|
9
|
-
};
|
|
3
|
+
import type { CarouselStore } from "./types";
|
|
10
4
|
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
ariaLive?: "off" | "polite" | "assertive";
|
|
21
|
-
onChange?: (index: number) => void;
|
|
22
|
-
};
|
|
5
|
+
export { CarouselController, createCarouselController, createCarouselStore } from "./controller";
|
|
6
|
+
export type { CarouselEvents, CarouselSlideChangeDetail } from "./events";
|
|
7
|
+
export type {
|
|
8
|
+
CarouselAutoplayOptions,
|
|
9
|
+
CarouselInstance,
|
|
10
|
+
CarouselOptions,
|
|
11
|
+
CarouselStore,
|
|
12
|
+
CreateCarouselOptions,
|
|
13
|
+
} from "./types";
|
|
23
14
|
|
|
24
|
-
export
|
|
25
|
-
instances: Record<string, import("./store.js").CarouselInstance>;
|
|
26
|
-
create(id: string, options?: CarouselOptions): void;
|
|
27
|
-
destroy(id: string): void;
|
|
28
|
-
bindViewport(id: string, viewport: HTMLElement | null): void;
|
|
29
|
-
next(id: string): void;
|
|
30
|
-
previous(id: string): void;
|
|
31
|
-
goTo(id: string, index: number): void;
|
|
32
|
-
current(id: string): number;
|
|
33
|
-
count(id: string): number;
|
|
34
|
-
canNext(id: string): boolean;
|
|
35
|
-
canPrevious(id: string): boolean;
|
|
36
|
-
play(id: string): void;
|
|
37
|
-
pause(id: string): void;
|
|
38
|
-
isPlaying(id: string): boolean;
|
|
39
|
-
instance(id: string): import("embla-carousel").EmblaCarouselType | null;
|
|
40
|
-
handleKeydown(id: string, event: KeyboardEvent): void;
|
|
41
|
-
carouselProps(
|
|
42
|
-
id: string,
|
|
43
|
-
options?: { label?: string }
|
|
44
|
-
): Record<string, string | boolean | undefined>;
|
|
45
|
-
viewportProps(
|
|
46
|
-
id: string,
|
|
47
|
-
options?: { slideSize?: string | false }
|
|
48
|
-
): Record<string, string | number | boolean | undefined>;
|
|
49
|
-
slideProps(id: string, index: number): Record<string, string | boolean | undefined>;
|
|
50
|
-
indicatorProps(id: string, index: number): Record<string, string | boolean | undefined>;
|
|
51
|
-
destroyAll(): void;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function createCarouselStore(): CarouselStore;
|
|
15
|
+
export function createCarouselController(id?: string): import("./types").CarouselStore;
|
|
55
16
|
|
|
56
17
|
export default function carouselPlugin(): import("alpinejs").PluginCallback;
|
|
57
18
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,36 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Alpine, PluginCallback, BaseController } from '@ailuracode/alpine-core';
|
|
2
|
+
export { Unsubscribe } from '@ailuracode/alpine-core';
|
|
2
3
|
import { EmblaOptionsType, EmblaCarouselType } from 'embla-carousel';
|
|
4
|
+
import { Alpine as Alpine$1 } from 'alpinejs';
|
|
3
5
|
import { AutoplayType } from 'embla-carousel-autoplay';
|
|
4
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Strongly-typed event map for the carousel controller.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Detail payload for the `slideChange` event.
|
|
12
|
+
*/
|
|
13
|
+
interface CarouselSlideChangeDetail {
|
|
14
|
+
readonly carouselId: string;
|
|
15
|
+
readonly index: number;
|
|
16
|
+
readonly totalSlides: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Event map for carousel state changes. Emits `slideChange` on
|
|
20
|
+
* every slide transition so consumers can react programmatically.
|
|
21
|
+
*/
|
|
22
|
+
interface CarouselEvents extends Record<string, unknown> {
|
|
23
|
+
slideChange: CarouselSlideChangeDetail;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Public type contracts for `@ailuracode/alpine-carousel`.
|
|
28
|
+
*
|
|
29
|
+
* Every public type lives in a `types.ts` module so consumers can import
|
|
30
|
+
* them without pulling the implementation. The shape IS the contract.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/** Autoplay configuration options. */
|
|
5
34
|
type CarouselAutoplayOptions = {
|
|
6
35
|
delay?: number;
|
|
7
36
|
stopOnInteraction?: boolean;
|
|
@@ -9,6 +38,7 @@ type CarouselAutoplayOptions = {
|
|
|
9
38
|
stopOnFocusIn?: boolean;
|
|
10
39
|
stopWhenHidden?: boolean;
|
|
11
40
|
};
|
|
41
|
+
/** Options passed when creating a carousel instance. */
|
|
12
42
|
type CarouselOptions = {
|
|
13
43
|
loop?: boolean;
|
|
14
44
|
autoplay?: boolean;
|
|
@@ -21,6 +51,7 @@ type CarouselOptions = {
|
|
|
21
51
|
ariaLive?: "off" | "polite" | "assertive";
|
|
22
52
|
onChange?: (index: number) => void;
|
|
23
53
|
};
|
|
54
|
+
/** Internal representation of a carousel instance. */
|
|
24
55
|
type CarouselInstance = {
|
|
25
56
|
currentIndex: number;
|
|
26
57
|
totalSlides: number;
|
|
@@ -37,6 +68,7 @@ type CarouselInstance = {
|
|
|
37
68
|
embla: EmblaCarouselType | null;
|
|
38
69
|
autoplay: AutoplayType | null;
|
|
39
70
|
};
|
|
71
|
+
/** Alpine-facing store surface. */
|
|
40
72
|
type CarouselStore = {
|
|
41
73
|
instances: Record<string, CarouselInstance>;
|
|
42
74
|
create(id: string, options?: CarouselOptions): void;
|
|
@@ -64,20 +96,95 @@ type CarouselStore = {
|
|
|
64
96
|
indicatorProps(id: string, index: number): Record<string, string | boolean | undefined>;
|
|
65
97
|
destroyAll(): void;
|
|
66
98
|
};
|
|
67
|
-
/**
|
|
68
|
-
|
|
99
|
+
/** Options accepted by the carousel plugin factory. */
|
|
100
|
+
interface CreateCarouselOptions {
|
|
101
|
+
readonly id?: string;
|
|
102
|
+
}
|
|
103
|
+
/** Typed view of `Alpine` the carousel plugin uses internally. */
|
|
104
|
+
type CarouselAlpine = Alpine<{
|
|
105
|
+
carousel: CarouselStore;
|
|
106
|
+
}> & {
|
|
107
|
+
cleanup?(callback: () => void): void;
|
|
108
|
+
};
|
|
109
|
+
/** `Alpine.plugin()` callback signature. */
|
|
110
|
+
type CarouselPluginCallback = PluginCallback<Alpine$1>;
|
|
69
111
|
|
|
70
|
-
/**
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Carousel controller — the framework-agnostic core of
|
|
114
|
+
* `@ailuracode/alpine-carousel`. Manages a registry of carousel
|
|
115
|
+
* instances powered by Embla Carousel, with autoplay, resize
|
|
116
|
+
* observation, and visibility handling.
|
|
117
|
+
*
|
|
118
|
+
* Emits a typed `slideChange` event on every slide transition so
|
|
119
|
+
* consumers can react programmatically.
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Headless carousel controller. Manages a registry of carousel
|
|
124
|
+
* instances powered by Embla Carousel.
|
|
125
|
+
*/
|
|
126
|
+
declare class CarouselController extends BaseController<CarouselEvents> {
|
|
127
|
+
#private;
|
|
128
|
+
constructor(id?: string);
|
|
129
|
+
get instances(): Readonly<Record<string, CarouselInstance>>;
|
|
130
|
+
create(id: string, options?: CarouselOptions): void;
|
|
131
|
+
/** Destroy a single carousel instance by id. */
|
|
132
|
+
destroy(id: string): void;
|
|
133
|
+
/** Destroy all instances and the controller itself. */
|
|
134
|
+
destroy(): void;
|
|
135
|
+
bindViewport(id: string, viewport: HTMLElement | null): void;
|
|
136
|
+
next(id: string): void;
|
|
137
|
+
previous(id: string): void;
|
|
138
|
+
goTo(id: string, index: number): void;
|
|
139
|
+
current(id: string): number;
|
|
140
|
+
count(id: string): number;
|
|
141
|
+
canNext(id: string): boolean;
|
|
142
|
+
canPrevious(id: string): boolean;
|
|
143
|
+
play(id: string): void;
|
|
144
|
+
pause(id: string): void;
|
|
145
|
+
isPlaying(id: string): boolean;
|
|
146
|
+
instance(id: string): EmblaCarouselType | null;
|
|
147
|
+
handleKeydown(id: string, event: KeyboardEvent): void;
|
|
148
|
+
carouselProps(id: string, options?: {
|
|
149
|
+
label?: string;
|
|
150
|
+
}): Record<string, string | boolean | undefined>;
|
|
151
|
+
viewportProps(_id: string, options?: {
|
|
152
|
+
slideSize?: string | false;
|
|
153
|
+
}): Record<string, string | number | boolean | undefined>;
|
|
154
|
+
slideProps(id: string, index: number): Record<string, string | boolean | undefined>;
|
|
155
|
+
indicatorProps(id: string, index: number): Record<string, string | boolean | undefined>;
|
|
156
|
+
destroyAll(): void;
|
|
157
|
+
/**
|
|
158
|
+
* Returns a store-shaped object for Alpine's `$store.carousel`.
|
|
159
|
+
* The store delegates to this controller.
|
|
160
|
+
*/
|
|
161
|
+
toStore(): CarouselStore;
|
|
81
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Creates a CarouselController and returns the controller instance.
|
|
165
|
+
* Convenience for non-Alpine consumers.
|
|
166
|
+
*/
|
|
167
|
+
declare function createCarouselController(id?: string): CarouselController;
|
|
168
|
+
/**
|
|
169
|
+
* Creates a CarouselStore (store-shaped object) directly.
|
|
170
|
+
* Backward-compatible alias.
|
|
171
|
+
*/
|
|
172
|
+
declare function createCarouselStore(): CarouselStore;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Alpine.js integration for `@ailuracode/alpine-carousel`.
|
|
176
|
+
*
|
|
177
|
+
* Thin adapter that wires {@link CarouselController} into
|
|
178
|
+
* `$store.carousel` and the `$carousel` magic.
|
|
179
|
+
*/
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Plugin factory — returns the `Alpine.plugin()` callback. Pass
|
|
183
|
+
* {@link CreateCarouselOptions} to configure the controller,
|
|
184
|
+
* or `{}` for the package defaults.
|
|
185
|
+
*/
|
|
186
|
+
declare function carouselPlugin(options?: CreateCarouselOptions): CarouselPluginCallback;
|
|
187
|
+
/** Builds typed carousel plugin options. */
|
|
188
|
+
declare function carouselOptions<const T extends CreateCarouselOptions>(options: T): T;
|
|
82
189
|
|
|
83
|
-
export { type CarouselAutoplayOptions, type CarouselInstance, type CarouselOptions, type CarouselStore, createCarouselStore, carouselPlugin as default };
|
|
190
|
+
export { type CarouselAlpine, type CarouselAutoplayOptions, CarouselController, type CarouselEvents, type CarouselInstance, type CarouselOptions, type CarouselPluginCallback, type CarouselSlideChangeDetail, type CarouselStore, type CreateCarouselOptions, carouselOptions, carouselPlugin, createCarouselController, createCarouselStore, carouselPlugin as default };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/controller.ts
|
|
2
|
+
import { BaseController, generateId } from "@ailuracode/alpine-core";
|
|
2
3
|
import EmblaCarousel from "embla-carousel";
|
|
3
4
|
import Autoplay from "embla-carousel-autoplay";
|
|
4
5
|
function createInstance(options = {}) {
|
|
@@ -44,269 +45,374 @@ function createAutoplayPlugin(options) {
|
|
|
44
45
|
playOnInit: true
|
|
45
46
|
});
|
|
46
47
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
var CarouselController = class extends BaseController {
|
|
49
|
+
#instances = {};
|
|
50
|
+
#cleanups = /* @__PURE__ */ new Map();
|
|
51
|
+
constructor(id) {
|
|
52
|
+
super(id ?? generateId("carousel"));
|
|
52
53
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
instance.totalSlides = embla.scrollSnapList().length;
|
|
56
|
-
instance.progress = embla.scrollProgress();
|
|
57
|
-
instance.canNext = embla.canScrollNext();
|
|
58
|
-
instance.canPrevious = embla.canScrollPrev();
|
|
59
|
-
instance.slidesInView = embla.slidesInView();
|
|
60
|
-
instance.isFirst = instance.currentIndex === 0;
|
|
61
|
-
instance.isLast = instance.totalSlides > 0 && instance.currentIndex === instance.totalSlides - 1;
|
|
62
|
-
instance.isPlaying = instance.autoplay?.isPlaying() ?? false;
|
|
63
|
-
if (previousIndex !== instance.currentIndex) {
|
|
64
|
-
instance.options.onChange?.(instance.currentIndex);
|
|
54
|
+
get instances() {
|
|
55
|
+
return this.#instances;
|
|
65
56
|
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
instance.embla = null;
|
|
72
|
-
instance.autoplay = null;
|
|
73
|
-
instance.viewport = null;
|
|
74
|
-
}
|
|
75
|
-
function addCleanup(id, cleanups, cleanup) {
|
|
76
|
-
const previous = cleanups.get(id);
|
|
77
|
-
cleanups.set(id, () => {
|
|
78
|
-
previous?.();
|
|
79
|
-
cleanup();
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
function setupVisibilityAutoplay(id, instance, cleanups) {
|
|
83
|
-
const stopWhenHidden = instance.options.autoplayOptions?.stopWhenHidden ?? true;
|
|
84
|
-
if (!(instance.options.autoplay && stopWhenHidden)) {
|
|
85
|
-
return;
|
|
57
|
+
create(id, options = {}) {
|
|
58
|
+
if (this.isDestroyed) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this.#getOrCreate(id, options);
|
|
86
62
|
}
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
|
|
63
|
+
destroy(id) {
|
|
64
|
+
if (id !== void 0) {
|
|
65
|
+
if (this.isDestroyed) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const instance = this.#instances[id];
|
|
69
|
+
if (!instance) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
this.#destroyEmbla(id);
|
|
73
|
+
delete this.#instances[id];
|
|
90
74
|
} else {
|
|
91
|
-
|
|
75
|
+
this.destroyAll();
|
|
76
|
+
super.destroy();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
bindViewport(id, viewport) {
|
|
80
|
+
if (this.isDestroyed) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!viewport) {
|
|
87
|
+
const instance = this.#instances[id];
|
|
88
|
+
if (instance) {
|
|
89
|
+
this.#destroyEmbla(id);
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
this.#getOrCreate(id);
|
|
94
|
+
this.#initEmbla(id, viewport);
|
|
95
|
+
}
|
|
96
|
+
next(id) {
|
|
97
|
+
this.#instances[id]?.embla?.scrollNext();
|
|
98
|
+
}
|
|
99
|
+
previous(id) {
|
|
100
|
+
this.#instances[id]?.embla?.scrollPrev();
|
|
101
|
+
}
|
|
102
|
+
goTo(id, index) {
|
|
103
|
+
this.#instances[id]?.embla?.scrollTo(index);
|
|
104
|
+
}
|
|
105
|
+
current(id) {
|
|
106
|
+
return this.#instances[id]?.currentIndex ?? 0;
|
|
107
|
+
}
|
|
108
|
+
count(id) {
|
|
109
|
+
return this.#instances[id]?.totalSlides ?? 0;
|
|
110
|
+
}
|
|
111
|
+
canNext(id) {
|
|
112
|
+
return this.#instances[id]?.canNext ?? false;
|
|
113
|
+
}
|
|
114
|
+
canPrevious(id) {
|
|
115
|
+
return this.#instances[id]?.canPrevious ?? false;
|
|
116
|
+
}
|
|
117
|
+
play(id) {
|
|
118
|
+
this.#instances[id]?.autoplay?.play();
|
|
119
|
+
const instance = this.#instances[id];
|
|
120
|
+
if (instance) {
|
|
121
|
+
instance.isPlaying = instance.autoplay?.isPlaying() ?? false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
pause(id) {
|
|
125
|
+
this.#instances[id]?.autoplay?.stop();
|
|
126
|
+
const instance = this.#instances[id];
|
|
127
|
+
if (instance) {
|
|
128
|
+
instance.isPlaying = instance.autoplay?.isPlaying() ?? false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
isPlaying(id) {
|
|
132
|
+
return this.#instances[id]?.isPlaying ?? false;
|
|
133
|
+
}
|
|
134
|
+
instance(id) {
|
|
135
|
+
return this.#instances[id]?.embla ?? null;
|
|
136
|
+
}
|
|
137
|
+
handleKeydown(id, event) {
|
|
138
|
+
const instance = this.#instances[id];
|
|
139
|
+
if (!instance?.embla) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
switch (event.key) {
|
|
143
|
+
case "ArrowLeft":
|
|
144
|
+
case "ArrowUp":
|
|
145
|
+
event.preventDefault();
|
|
146
|
+
this.previous(id);
|
|
147
|
+
break;
|
|
148
|
+
case "ArrowRight":
|
|
149
|
+
case "ArrowDown":
|
|
150
|
+
event.preventDefault();
|
|
151
|
+
this.next(id);
|
|
152
|
+
break;
|
|
153
|
+
case "Home":
|
|
154
|
+
event.preventDefault();
|
|
155
|
+
this.goTo(id, 0);
|
|
156
|
+
break;
|
|
157
|
+
case "End": {
|
|
158
|
+
event.preventDefault();
|
|
159
|
+
const last = this.count(id) - 1;
|
|
160
|
+
if (last >= 0) {
|
|
161
|
+
this.goTo(id, last);
|
|
162
|
+
}
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
default:
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
carouselProps(id, options = {}) {
|
|
170
|
+
const instance = this.#instances[id];
|
|
171
|
+
return {
|
|
172
|
+
role: "region",
|
|
173
|
+
"aria-roledescription": "carousel",
|
|
174
|
+
"aria-live": instance?.ariaLive ?? "polite",
|
|
175
|
+
"aria-label": options.label
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
viewportProps(_id, options = {}) {
|
|
179
|
+
const props = {
|
|
180
|
+
tabindex: 0
|
|
181
|
+
};
|
|
182
|
+
if (options.slideSize !== false) {
|
|
183
|
+
props.style = `--slide-size: ${options.slideSize ?? "100%"}`;
|
|
184
|
+
}
|
|
185
|
+
return props;
|
|
186
|
+
}
|
|
187
|
+
slideProps(id, index) {
|
|
188
|
+
const current = this.current(id);
|
|
189
|
+
const total = this.count(id);
|
|
190
|
+
return {
|
|
191
|
+
role: "group",
|
|
192
|
+
"aria-roledescription": "slide",
|
|
193
|
+
"aria-label": `${index + 1} of ${total}`,
|
|
194
|
+
"aria-hidden": current !== index ? true : void 0
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
indicatorProps(id, index) {
|
|
198
|
+
const selected = this.current(id) === index;
|
|
199
|
+
return {
|
|
200
|
+
type: "button",
|
|
201
|
+
"aria-label": `Go to slide ${index + 1}`,
|
|
202
|
+
"aria-current": selected ? "true" : void 0
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
destroyAll() {
|
|
206
|
+
for (const id of Object.keys(this.#instances)) {
|
|
207
|
+
this.destroy(id);
|
|
92
208
|
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Returns a store-shaped object for Alpine's `$store.carousel`.
|
|
212
|
+
* The store delegates to this controller.
|
|
213
|
+
*/
|
|
214
|
+
toStore() {
|
|
215
|
+
return {
|
|
216
|
+
instances: this.#instances,
|
|
217
|
+
create: (id, opts) => this.create(id, opts),
|
|
218
|
+
destroy: (id) => this.destroy(id),
|
|
219
|
+
bindViewport: (id, viewport) => this.bindViewport(id, viewport),
|
|
220
|
+
next: (id) => this.next(id),
|
|
221
|
+
previous: (id) => this.previous(id),
|
|
222
|
+
goTo: (id, index) => this.goTo(id, index),
|
|
223
|
+
current: (id) => this.current(id),
|
|
224
|
+
count: (id) => this.count(id),
|
|
225
|
+
canNext: (id) => this.canNext(id),
|
|
226
|
+
canPrevious: (id) => this.canPrevious(id),
|
|
227
|
+
play: (id) => this.play(id),
|
|
228
|
+
pause: (id) => this.pause(id),
|
|
229
|
+
isPlaying: (id) => this.isPlaying(id),
|
|
230
|
+
instance: (id) => this.instance(id),
|
|
231
|
+
handleKeydown: (id, event) => this.handleKeydown(id, event),
|
|
232
|
+
carouselProps: (id, opts) => this.carouselProps(id, opts),
|
|
233
|
+
viewportProps: (id, opts) => this.viewportProps(id, opts),
|
|
234
|
+
slideProps: (id, index) => this.slideProps(id, index),
|
|
235
|
+
indicatorProps: (id, index) => this.indicatorProps(id, index),
|
|
236
|
+
destroyAll: () => this.destroyAll()
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
#getOrCreate(id, options) {
|
|
240
|
+
if (!this.#instances[id]) {
|
|
241
|
+
this.#instances[id] = createInstance(options);
|
|
242
|
+
} else if (options) {
|
|
243
|
+
this.#instances[id].options = { ...this.#instances[id].options, ...options };
|
|
244
|
+
this.#instances[id].ariaLive = options.ariaLive ?? this.#instances[id].ariaLive;
|
|
245
|
+
}
|
|
246
|
+
return this.#instances[id];
|
|
247
|
+
}
|
|
248
|
+
#syncFromEmbla(id) {
|
|
249
|
+
const instance = this.#instances[id];
|
|
250
|
+
const embla = instance?.embla;
|
|
251
|
+
if (!(instance && embla)) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const previousIndex = instance.currentIndex;
|
|
255
|
+
instance.currentIndex = embla.selectedScrollSnap();
|
|
256
|
+
instance.totalSlides = embla.scrollSnapList().length;
|
|
257
|
+
instance.progress = embla.scrollProgress();
|
|
258
|
+
instance.canNext = embla.canScrollNext();
|
|
259
|
+
instance.canPrevious = embla.canScrollPrev();
|
|
260
|
+
instance.slidesInView = embla.slidesInView();
|
|
261
|
+
instance.isFirst = instance.currentIndex === 0;
|
|
262
|
+
instance.isLast = instance.totalSlides > 0 && instance.currentIndex === instance.totalSlides - 1;
|
|
93
263
|
instance.isPlaying = instance.autoplay?.isPlaying() ?? false;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
function setupResizeObserver(viewport, store, id, cleanups) {
|
|
103
|
-
if (typeof ResizeObserver === "undefined") {
|
|
104
|
-
return;
|
|
264
|
+
if (previousIndex !== instance.currentIndex) {
|
|
265
|
+
this.emit("slideChange", {
|
|
266
|
+
carouselId: id,
|
|
267
|
+
index: instance.currentIndex,
|
|
268
|
+
totalSlides: instance.totalSlides
|
|
269
|
+
});
|
|
270
|
+
instance.options.onChange?.(instance.currentIndex);
|
|
271
|
+
}
|
|
105
272
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
} else if (options) {
|
|
117
|
-
store.instances[id].options = { ...store.instances[id].options, ...options };
|
|
118
|
-
store.instances[id].ariaLive = options.ariaLive ?? store.instances[id].ariaLive;
|
|
273
|
+
#destroyEmbla(id) {
|
|
274
|
+
this.#cleanups.get(id)?.();
|
|
275
|
+
this.#cleanups.delete(id);
|
|
276
|
+
const instance = this.#instances[id];
|
|
277
|
+
instance?.embla?.destroy();
|
|
278
|
+
if (instance) {
|
|
279
|
+
instance.embla = null;
|
|
280
|
+
instance.autoplay = null;
|
|
281
|
+
instance.viewport = null;
|
|
282
|
+
}
|
|
119
283
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
284
|
+
#addCleanup(id, cleanup) {
|
|
285
|
+
const previous = this.#cleanups.get(id);
|
|
286
|
+
this.#cleanups.set(id, () => {
|
|
287
|
+
previous?.();
|
|
288
|
+
cleanup();
|
|
289
|
+
});
|
|
126
290
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
291
|
+
#setupVisibilityAutoplay(id, instance) {
|
|
292
|
+
const stopWhenHidden = instance.options.autoplayOptions?.stopWhenHidden ?? true;
|
|
293
|
+
if (!(instance.options.autoplay && stopWhenHidden)) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const onVisibilityChange = () => {
|
|
297
|
+
if (document.hidden) {
|
|
298
|
+
instance.autoplay?.stop();
|
|
299
|
+
} else {
|
|
300
|
+
instance.autoplay?.play();
|
|
301
|
+
}
|
|
302
|
+
instance.isPlaying = instance.autoplay?.isPlaying() ?? false;
|
|
303
|
+
};
|
|
304
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
305
|
+
this.#addCleanup(
|
|
306
|
+
id,
|
|
307
|
+
() => document.removeEventListener("visibilitychange", onVisibilityChange)
|
|
308
|
+
);
|
|
134
309
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
embla.on("autoplay:stop", sync);
|
|
143
|
-
sync();
|
|
144
|
-
setupResizeObserver(viewport, store, id, cleanups);
|
|
145
|
-
setupVisibilityAutoplay(id, instance, cleanups);
|
|
146
|
-
if (typeof requestAnimationFrame !== "undefined") {
|
|
147
|
-
requestAnimationFrame(() => {
|
|
148
|
-
instance.embla?.reInit();
|
|
149
|
-
syncFromEmbla(store, id);
|
|
310
|
+
#setupResizeObserver(id, viewport) {
|
|
311
|
+
if (typeof ResizeObserver === "undefined") {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const observer = new ResizeObserver(() => {
|
|
315
|
+
this.#instances[id]?.embla?.reInit();
|
|
316
|
+
this.#syncFromEmbla(id);
|
|
150
317
|
});
|
|
318
|
+
observer.observe(viewport);
|
|
319
|
+
this.#addCleanup(id, () => observer.disconnect());
|
|
151
320
|
}
|
|
321
|
+
#initEmbla(id, viewport) {
|
|
322
|
+
const instance = this.#instances[id];
|
|
323
|
+
if (!instance) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
this.#destroyEmbla(id);
|
|
327
|
+
instance.viewport = viewport;
|
|
328
|
+
const plugins = [];
|
|
329
|
+
const autoplay = createAutoplayPlugin(instance.options);
|
|
330
|
+
if (autoplay) {
|
|
331
|
+
plugins.push(autoplay);
|
|
332
|
+
instance.autoplay = autoplay;
|
|
333
|
+
}
|
|
334
|
+
const embla = EmblaCarousel(viewport, toEmblaOptions(instance.options), plugins);
|
|
335
|
+
instance.embla = embla;
|
|
336
|
+
const sync = () => this.#syncFromEmbla(id);
|
|
337
|
+
embla.on("select", sync);
|
|
338
|
+
embla.on("scroll", sync);
|
|
339
|
+
embla.on("reInit", sync);
|
|
340
|
+
embla.on("autoplay:play", sync);
|
|
341
|
+
embla.on("autoplay:stop", sync);
|
|
342
|
+
sync();
|
|
343
|
+
this.#setupResizeObserver(id, viewport);
|
|
344
|
+
this.#setupVisibilityAutoplay(id, instance);
|
|
345
|
+
if (typeof requestAnimationFrame !== "undefined") {
|
|
346
|
+
requestAnimationFrame(() => {
|
|
347
|
+
instance.embla?.reInit();
|
|
348
|
+
this.#syncFromEmbla(id);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
function createCarouselController(id) {
|
|
354
|
+
return new CarouselController(id);
|
|
152
355
|
}
|
|
153
356
|
function createCarouselStore() {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
return this.instances[id]?.currentIndex ?? 0;
|
|
193
|
-
},
|
|
194
|
-
count(id) {
|
|
195
|
-
return this.instances[id]?.totalSlides ?? 0;
|
|
196
|
-
},
|
|
197
|
-
canNext(id) {
|
|
198
|
-
return this.instances[id]?.canNext ?? false;
|
|
199
|
-
},
|
|
200
|
-
canPrevious(id) {
|
|
201
|
-
return this.instances[id]?.canPrevious ?? false;
|
|
202
|
-
},
|
|
203
|
-
play(id) {
|
|
204
|
-
this.instances[id]?.autoplay?.play();
|
|
205
|
-
const instance = this.instances[id];
|
|
206
|
-
if (instance) {
|
|
207
|
-
instance.isPlaying = instance.autoplay?.isPlaying() ?? false;
|
|
208
|
-
}
|
|
209
|
-
},
|
|
210
|
-
pause(id) {
|
|
211
|
-
this.instances[id]?.autoplay?.stop();
|
|
212
|
-
const instance = this.instances[id];
|
|
213
|
-
if (instance) {
|
|
214
|
-
instance.isPlaying = instance.autoplay?.isPlaying() ?? false;
|
|
215
|
-
}
|
|
216
|
-
},
|
|
217
|
-
isPlaying(id) {
|
|
218
|
-
return this.instances[id]?.isPlaying ?? false;
|
|
219
|
-
},
|
|
220
|
-
instance(id) {
|
|
221
|
-
return this.instances[id]?.embla ?? null;
|
|
222
|
-
},
|
|
223
|
-
handleKeydown(id, event) {
|
|
224
|
-
const instance = this.instances[id];
|
|
225
|
-
if (!instance?.embla) {
|
|
226
|
-
return;
|
|
357
|
+
return new CarouselController().toStore();
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// src/plugin.ts
|
|
361
|
+
var CAROUSEL_STORE_KEY = "carousel";
|
|
362
|
+
function carouselPlugin(options = {}) {
|
|
363
|
+
return function registerCarousel(alpine) {
|
|
364
|
+
const Alpine = alpine;
|
|
365
|
+
const controller = new CarouselController(options.id);
|
|
366
|
+
const store = {
|
|
367
|
+
instances: controller.instances,
|
|
368
|
+
create: (id, opts) => controller.create(id, opts),
|
|
369
|
+
destroy: (id) => controller.destroy(id),
|
|
370
|
+
bindViewport: (id, viewport) => controller.bindViewport(id, viewport),
|
|
371
|
+
next: (id) => controller.next(id),
|
|
372
|
+
previous: (id) => controller.previous(id),
|
|
373
|
+
goTo: (id, index) => controller.goTo(id, index),
|
|
374
|
+
current: (id) => controller.current(id),
|
|
375
|
+
count: (id) => controller.count(id),
|
|
376
|
+
canNext: (id) => controller.canNext(id),
|
|
377
|
+
canPrevious: (id) => controller.canPrevious(id),
|
|
378
|
+
play: (id) => controller.play(id),
|
|
379
|
+
pause: (id) => controller.pause(id),
|
|
380
|
+
isPlaying: (id) => controller.isPlaying(id),
|
|
381
|
+
instance: (id) => controller.instance(id),
|
|
382
|
+
handleKeydown: (id, event) => controller.handleKeydown(id, event),
|
|
383
|
+
carouselProps: (id, opts) => controller.carouselProps(id, opts),
|
|
384
|
+
viewportProps: (id, opts) => controller.viewportProps(id, opts),
|
|
385
|
+
slideProps: (id, index) => controller.slideProps(id, index),
|
|
386
|
+
indicatorProps: (id, index) => controller.indicatorProps(id, index),
|
|
387
|
+
destroyAll: () => controller.destroyAll()
|
|
388
|
+
};
|
|
389
|
+
Alpine.store(CAROUSEL_STORE_KEY, store);
|
|
390
|
+
const reactiveStore = Alpine.store(CAROUSEL_STORE_KEY);
|
|
391
|
+
controller.on("slideChange", () => {
|
|
392
|
+
const controllerInstances = controller.instances;
|
|
393
|
+
for (const key of Object.keys(controllerInstances)) {
|
|
394
|
+
reactiveStore.instances[key] = controllerInstances[key];
|
|
227
395
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
event.preventDefault();
|
|
232
|
-
this.previous(id);
|
|
233
|
-
break;
|
|
234
|
-
case "ArrowRight":
|
|
235
|
-
case "ArrowDown":
|
|
236
|
-
event.preventDefault();
|
|
237
|
-
this.next(id);
|
|
238
|
-
break;
|
|
239
|
-
case "Home":
|
|
240
|
-
event.preventDefault();
|
|
241
|
-
this.goTo(id, 0);
|
|
242
|
-
break;
|
|
243
|
-
case "End": {
|
|
244
|
-
event.preventDefault();
|
|
245
|
-
const last = this.count(id) - 1;
|
|
246
|
-
if (last >= 0) {
|
|
247
|
-
this.goTo(id, last);
|
|
248
|
-
}
|
|
249
|
-
break;
|
|
396
|
+
for (const key of Object.keys(reactiveStore.instances)) {
|
|
397
|
+
if (!(key in controllerInstances)) {
|
|
398
|
+
delete reactiveStore.instances[key];
|
|
250
399
|
}
|
|
251
|
-
default:
|
|
252
|
-
break;
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
carouselProps(id, options = {}) {
|
|
256
|
-
const instance = this.instances[id];
|
|
257
|
-
return {
|
|
258
|
-
role: "region",
|
|
259
|
-
"aria-roledescription": "carousel",
|
|
260
|
-
"aria-live": instance?.ariaLive ?? "polite",
|
|
261
|
-
"aria-label": options.label
|
|
262
|
-
};
|
|
263
|
-
},
|
|
264
|
-
viewportProps(_id, options = {}) {
|
|
265
|
-
const props = {
|
|
266
|
-
tabindex: 0
|
|
267
|
-
};
|
|
268
|
-
if (options.slideSize !== false) {
|
|
269
|
-
props.style = `--slide-size: ${options.slideSize ?? "100%"}`;
|
|
270
|
-
}
|
|
271
|
-
return props;
|
|
272
|
-
},
|
|
273
|
-
slideProps(id, index) {
|
|
274
|
-
const current = this.current(id);
|
|
275
|
-
const total = this.count(id);
|
|
276
|
-
return {
|
|
277
|
-
role: "group",
|
|
278
|
-
"aria-roledescription": "slide",
|
|
279
|
-
"aria-label": `${index + 1} of ${total}`,
|
|
280
|
-
"aria-hidden": current !== index ? true : void 0
|
|
281
|
-
};
|
|
282
|
-
},
|
|
283
|
-
indicatorProps(id, index) {
|
|
284
|
-
const selected = this.current(id) === index;
|
|
285
|
-
return {
|
|
286
|
-
type: "button",
|
|
287
|
-
"aria-label": `Go to slide ${index + 1}`,
|
|
288
|
-
"aria-current": selected ? "true" : void 0
|
|
289
|
-
};
|
|
290
|
-
},
|
|
291
|
-
destroyAll() {
|
|
292
|
-
for (const id of Object.keys(this.instances)) {
|
|
293
|
-
this.destroy(id);
|
|
294
400
|
}
|
|
401
|
+
});
|
|
402
|
+
Alpine.magic(CAROUSEL_STORE_KEY, () => reactiveStore);
|
|
403
|
+
if (typeof Alpine.cleanup === "function") {
|
|
404
|
+
Alpine.cleanup(() => controller.destroy());
|
|
295
405
|
}
|
|
296
406
|
};
|
|
297
|
-
return store;
|
|
298
407
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
function carouselPlugin() {
|
|
302
|
-
return function registerCarousel(Alpine) {
|
|
303
|
-
const store = createCarouselStore();
|
|
304
|
-
Alpine.store("carousel", store);
|
|
305
|
-
Alpine.magic("carousel", () => Alpine.store("carousel"));
|
|
306
|
-
};
|
|
408
|
+
function carouselOptions(options) {
|
|
409
|
+
return options;
|
|
307
410
|
}
|
|
308
411
|
export {
|
|
412
|
+
CarouselController,
|
|
413
|
+
carouselOptions,
|
|
414
|
+
carouselPlugin,
|
|
415
|
+
createCarouselController,
|
|
309
416
|
createCarouselStore,
|
|
310
417
|
carouselPlugin as default
|
|
311
418
|
};
|
|
312
|
-
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ailuracode/alpine-carousel",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Alpine.js headless accessible carousel store powered by Embla Carousel",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,7 +35,11 @@
|
|
|
35
35
|
"embla-carousel": "^8.6.0",
|
|
36
36
|
"embla-carousel-autoplay": "^8.6.0"
|
|
37
37
|
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@ailuracode/alpine-core": "0.2.0"
|
|
40
|
+
},
|
|
38
41
|
"peerDependencies": {
|
|
42
|
+
"@ailuracode/alpine-core": "^0.2.0",
|
|
39
43
|
"alpinejs": "^3.0.0",
|
|
40
44
|
"@types/alpinejs": "^3.13.11"
|
|
41
45
|
},
|
|
@@ -55,7 +59,7 @@
|
|
|
55
59
|
"headless"
|
|
56
60
|
],
|
|
57
61
|
"scripts": {
|
|
58
|
-
"build": "tsup src/index.ts --format esm --dts --clean --
|
|
62
|
+
"build": "tsup src/index.ts --format esm --dts --clean --out-dir dist && cp src/global.d.ts dist/global.d.ts",
|
|
59
63
|
"test": "vitest run --config ../../vitest.config.ts test"
|
|
60
64
|
}
|
|
61
65
|
}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store.ts","../src/index.ts"],"sourcesContent":["import EmblaCarousel, { type EmblaCarouselType, type EmblaOptionsType } from \"embla-carousel\";\nimport Autoplay, { type AutoplayType } from \"embla-carousel-autoplay\";\n\nexport type CarouselAutoplayOptions = {\n delay?: number;\n stopOnInteraction?: boolean;\n stopOnMouseEnter?: boolean;\n stopOnFocusIn?: boolean;\n stopWhenHidden?: boolean;\n};\n\nexport type CarouselOptions = {\n loop?: boolean;\n autoplay?: boolean;\n autoplayOptions?: CarouselAutoplayOptions;\n axis?: \"x\" | \"y\";\n align?: EmblaOptionsType[\"align\"];\n containScroll?: EmblaOptionsType[\"containScroll\"];\n dragFree?: boolean;\n duration?: number;\n ariaLive?: \"off\" | \"polite\" | \"assertive\";\n onChange?: (index: number) => void;\n};\n\nexport type CarouselInstance = {\n currentIndex: number;\n totalSlides: number;\n progress: number;\n isFirst: boolean;\n isLast: boolean;\n isPlaying: boolean;\n canNext: boolean;\n canPrevious: boolean;\n slidesInView: number[];\n options: CarouselOptions;\n ariaLive: \"off\" | \"polite\" | \"assertive\";\n viewport: HTMLElement | null;\n embla: EmblaCarouselType | null;\n autoplay: AutoplayType | null;\n};\n\nexport type CarouselStore = {\n instances: Record<string, CarouselInstance>;\n create(id: string, options?: CarouselOptions): void;\n destroy(id: string): void;\n bindViewport(id: string, viewport: HTMLElement | null): void;\n next(id: string): void;\n previous(id: string): void;\n goTo(id: string, index: number): void;\n current(id: string): number;\n count(id: string): number;\n canNext(id: string): boolean;\n canPrevious(id: string): boolean;\n play(id: string): void;\n pause(id: string): void;\n isPlaying(id: string): boolean;\n instance(id: string): EmblaCarouselType | null;\n handleKeydown(id: string, event: KeyboardEvent): void;\n carouselProps(\n id: string,\n options?: { label?: string }\n ): Record<string, string | boolean | undefined>;\n viewportProps(\n id: string,\n options?: { slideSize?: string | false }\n ): Record<string, string | number | boolean | undefined>;\n slideProps(id: string, index: number): Record<string, string | boolean | undefined>;\n indicatorProps(id: string, index: number): Record<string, string | boolean | undefined>;\n destroyAll(): void;\n};\n\nfunction createInstance(options: CarouselOptions = {}): CarouselInstance {\n return {\n currentIndex: 0,\n totalSlides: 0,\n progress: 0,\n isFirst: true,\n isLast: false,\n isPlaying: false,\n canNext: false,\n canPrevious: false,\n slidesInView: [],\n options,\n ariaLive: options.ariaLive ?? \"polite\",\n viewport: null,\n embla: null,\n autoplay: null,\n };\n}\n\nfunction toEmblaOptions(options: CarouselOptions): EmblaOptionsType {\n return {\n loop: options.loop ?? false,\n axis: options.axis ?? \"x\",\n align: options.align ?? \"start\",\n containScroll: options.containScroll ?? \"trimSnaps\",\n dragFree: options.dragFree ?? false,\n duration: options.duration ?? 25,\n };\n}\n\nfunction createAutoplayPlugin(options: CarouselOptions): AutoplayType | null {\n if (!options.autoplay) {\n return null;\n }\n\n const autoplayOptions = options.autoplayOptions ?? {};\n const stopOnMouseEnter = autoplayOptions.stopOnMouseEnter ?? false;\n const stopOnInteraction = autoplayOptions.stopOnInteraction ?? !stopOnMouseEnter;\n\n return Autoplay({\n delay: autoplayOptions.delay ?? 4000,\n stopOnInteraction,\n stopOnMouseEnter,\n stopOnFocusIn: autoplayOptions.stopOnFocusIn ?? true,\n playOnInit: true,\n });\n}\n\nfunction syncFromEmbla(store: CarouselStore, id: string): void {\n const instance = store.instances[id];\n const embla = instance?.embla;\n if (!(instance && embla)) {\n return;\n }\n\n const previousIndex = instance.currentIndex;\n instance.currentIndex = embla.selectedScrollSnap();\n instance.totalSlides = embla.scrollSnapList().length;\n instance.progress = embla.scrollProgress();\n instance.canNext = embla.canScrollNext();\n instance.canPrevious = embla.canScrollPrev();\n instance.slidesInView = embla.slidesInView();\n instance.isFirst = instance.currentIndex === 0;\n instance.isLast = instance.totalSlides > 0 && instance.currentIndex === instance.totalSlides - 1;\n instance.isPlaying = instance.autoplay?.isPlaying() ?? false;\n\n if (previousIndex !== instance.currentIndex) {\n instance.options.onChange?.(instance.currentIndex);\n }\n}\n\nfunction destroyEmbla(\n instance: CarouselInstance,\n id: string,\n cleanups: Map<string, () => void>\n): void {\n cleanups.get(id)?.();\n cleanups.delete(id);\n instance.embla?.destroy();\n instance.embla = null;\n instance.autoplay = null;\n instance.viewport = null;\n}\n\nfunction addCleanup(id: string, cleanups: Map<string, () => void>, cleanup: () => void): void {\n const previous = cleanups.get(id);\n cleanups.set(id, () => {\n previous?.();\n cleanup();\n });\n}\n\nfunction setupVisibilityAutoplay(\n id: string,\n instance: CarouselInstance,\n cleanups: Map<string, () => void>\n): void {\n const stopWhenHidden = instance.options.autoplayOptions?.stopWhenHidden ?? true;\n if (!(instance.options.autoplay && stopWhenHidden)) {\n return;\n }\n\n const onVisibilityChange = () => {\n if (document.hidden) {\n instance.autoplay?.stop();\n } else {\n instance.autoplay?.play();\n }\n instance.isPlaying = instance.autoplay?.isPlaying() ?? false;\n };\n\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n addCleanup(id, cleanups, () =>\n document.removeEventListener(\"visibilitychange\", onVisibilityChange)\n );\n}\n\nfunction setupResizeObserver(\n viewport: HTMLElement,\n store: CarouselStore,\n id: string,\n cleanups: Map<string, () => void>\n): void {\n if (typeof ResizeObserver === \"undefined\") {\n return;\n }\n\n const observer = new ResizeObserver(() => {\n store.instances[id]?.embla?.reInit();\n syncFromEmbla(store, id);\n });\n observer.observe(viewport);\n addCleanup(id, cleanups, () => observer.disconnect());\n}\n\nfunction getOrCreate(\n store: CarouselStore,\n id: string,\n options?: CarouselOptions\n): CarouselInstance {\n if (!store.instances[id]) {\n store.instances[id] = createInstance(options);\n } else if (options) {\n store.instances[id].options = { ...store.instances[id].options, ...options };\n store.instances[id].ariaLive = options.ariaLive ?? store.instances[id].ariaLive;\n }\n return store.instances[id];\n}\n\nfunction initEmbla(\n store: CarouselStore,\n id: string,\n viewport: HTMLElement,\n cleanups: Map<string, () => void>\n): void {\n const instance = store.instances[id];\n if (!instance) {\n return;\n }\n\n destroyEmbla(instance, id, cleanups);\n instance.viewport = viewport;\n\n const plugins = [];\n const autoplay = createAutoplayPlugin(instance.options);\n if (autoplay) {\n plugins.push(autoplay);\n instance.autoplay = autoplay;\n }\n\n const embla = EmblaCarousel(viewport, toEmblaOptions(instance.options), plugins);\n instance.embla = embla;\n\n const sync = () => syncFromEmbla(store, id);\n embla.on(\"select\", sync);\n embla.on(\"scroll\", sync);\n embla.on(\"reInit\", sync);\n embla.on(\"autoplay:play\", sync);\n embla.on(\"autoplay:stop\", sync);\n sync();\n setupResizeObserver(viewport, store, id, cleanups);\n setupVisibilityAutoplay(id, instance, cleanups);\n\n if (typeof requestAnimationFrame !== \"undefined\") {\n requestAnimationFrame(() => {\n instance.embla?.reInit();\n syncFromEmbla(store, id);\n });\n }\n}\n\n/** Creates the headless carousel store powered by Embla Carousel. */\nexport function createCarouselStore(): CarouselStore {\n const cleanups = new Map<string, () => void>();\n\n const store: CarouselStore = {\n instances: {},\n\n create(id, options = {}) {\n getOrCreate(this, id, options);\n },\n\n destroy(id) {\n const instance = this.instances[id];\n if (!instance) {\n return;\n }\n destroyEmbla(instance, id, cleanups);\n delete this.instances[id];\n },\n\n bindViewport(id, viewport) {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return;\n }\n\n if (!viewport) {\n const instance = this.instances[id];\n if (instance) {\n destroyEmbla(instance, id, cleanups);\n }\n return;\n }\n\n getOrCreate(this, id);\n initEmbla(this, id, viewport, cleanups);\n },\n\n next(id) {\n this.instances[id]?.embla?.scrollNext();\n },\n\n previous(id) {\n this.instances[id]?.embla?.scrollPrev();\n },\n\n goTo(id, index) {\n this.instances[id]?.embla?.scrollTo(index);\n },\n\n current(id) {\n return this.instances[id]?.currentIndex ?? 0;\n },\n\n count(id) {\n return this.instances[id]?.totalSlides ?? 0;\n },\n\n canNext(id) {\n return this.instances[id]?.canNext ?? false;\n },\n\n canPrevious(id) {\n return this.instances[id]?.canPrevious ?? false;\n },\n\n play(id) {\n this.instances[id]?.autoplay?.play();\n const instance = this.instances[id];\n if (instance) {\n instance.isPlaying = instance.autoplay?.isPlaying() ?? false;\n }\n },\n\n pause(id) {\n this.instances[id]?.autoplay?.stop();\n const instance = this.instances[id];\n if (instance) {\n instance.isPlaying = instance.autoplay?.isPlaying() ?? false;\n }\n },\n\n isPlaying(id) {\n return this.instances[id]?.isPlaying ?? false;\n },\n\n instance(id) {\n return this.instances[id]?.embla ?? null;\n },\n\n handleKeydown(id, event) {\n const instance = this.instances[id];\n if (!instance?.embla) {\n return;\n }\n\n switch (event.key) {\n case \"ArrowLeft\":\n case \"ArrowUp\":\n event.preventDefault();\n this.previous(id);\n break;\n case \"ArrowRight\":\n case \"ArrowDown\":\n event.preventDefault();\n this.next(id);\n break;\n case \"Home\":\n event.preventDefault();\n this.goTo(id, 0);\n break;\n case \"End\": {\n event.preventDefault();\n const last = this.count(id) - 1;\n if (last >= 0) {\n this.goTo(id, last);\n }\n break;\n }\n default:\n break;\n }\n },\n\n carouselProps(id, options = {}) {\n const instance = this.instances[id];\n return {\n role: \"region\",\n \"aria-roledescription\": \"carousel\",\n \"aria-live\": instance?.ariaLive ?? \"polite\",\n \"aria-label\": options.label,\n };\n },\n\n viewportProps(_id, options = {}) {\n const props: Record<string, string | number | boolean | undefined> = {\n tabindex: 0,\n };\n\n if (options.slideSize !== false) {\n props.style = `--slide-size: ${options.slideSize ?? \"100%\"}`;\n }\n\n return props;\n },\n\n slideProps(id, index) {\n const current = this.current(id);\n const total = this.count(id);\n return {\n role: \"group\",\n \"aria-roledescription\": \"slide\",\n \"aria-label\": `${index + 1} of ${total}`,\n \"aria-hidden\": current !== index ? true : undefined,\n };\n },\n\n indicatorProps(id, index) {\n const selected = this.current(id) === index;\n return {\n type: \"button\",\n \"aria-label\": `Go to slide ${index + 1}`,\n \"aria-current\": selected ? \"true\" : undefined,\n };\n },\n\n destroyAll() {\n for (const id of Object.keys(this.instances)) {\n this.destroy(id);\n }\n },\n };\n\n return store;\n}\n","import type AlpineType from \"alpinejs\";\nimport { type CarouselStore, createCarouselStore } from \"./store.js\";\n\nexport {\n type CarouselAutoplayOptions,\n type CarouselInstance,\n type CarouselOptions,\n type CarouselStore,\n createCarouselStore,\n} from \"./store.js\";\n\n/** Alpine.js carousel plugin. Registers `$store.carousel`. */\nexport default function carouselPlugin(): AlpineType.PluginCallback {\n return function registerCarousel(Alpine) {\n const store = createCarouselStore();\n Alpine.store(\"carousel\", store);\n Alpine.magic(\"carousel\", () => Alpine.store(\"carousel\"));\n };\n}\n\ndeclare global {\n namespace Alpine {\n interface Stores {\n carousel: CarouselStore;\n }\n interface Magics<T> {\n $carousel: CarouselStore;\n }\n }\n}\n"],"mappings":"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","names":[]}
|