@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 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
- npm install @ailuracode/alpine-carousel alpinejs
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
- ```js
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 [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>
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
- export type CarouselAutoplayOptions = {
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 type CarouselOptions = {
12
- loop?: boolean;
13
- autoplay?: boolean;
14
- autoplayOptions?: CarouselAutoplayOptions;
15
- axis?: "x" | "y";
16
- align?: import("embla-carousel").EmblaOptionsType["align"];
17
- containScroll?: import("embla-carousel").EmblaOptionsType["containScroll"];
18
- dragFree?: boolean;
19
- duration?: number;
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 interface CarouselStore {
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 AlpineType from 'alpinejs';
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
- /** Creates the headless carousel store powered by Embla Carousel. */
68
- declare function createCarouselStore(): CarouselStore;
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
- /** Alpine.js carousel plugin. Registers `$store.carousel`. */
71
- declare function carouselPlugin(): AlpineType.PluginCallback;
72
- declare global {
73
- namespace Alpine {
74
- interface Stores {
75
- carousel: CarouselStore;
76
- }
77
- interface Magics<T> {
78
- $carousel: CarouselStore;
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/store.ts
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
- function syncFromEmbla(store, id) {
48
- const instance = store.instances[id];
49
- const embla = instance?.embla;
50
- if (!(instance && embla)) {
51
- return;
48
+ var CarouselController = class extends BaseController {
49
+ #instances = {};
50
+ #cleanups = /* @__PURE__ */ new Map();
51
+ constructor(id) {
52
+ super(id ?? generateId("carousel"));
52
53
  }
53
- const previousIndex = instance.currentIndex;
54
- instance.currentIndex = embla.selectedScrollSnap();
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
- function destroyEmbla(instance, id, cleanups) {
68
- cleanups.get(id)?.();
69
- cleanups.delete(id);
70
- instance.embla?.destroy();
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
- const onVisibilityChange = () => {
88
- if (document.hidden) {
89
- instance.autoplay?.stop();
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
- instance.autoplay?.play();
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
- document.addEventListener("visibilitychange", onVisibilityChange);
96
- addCleanup(
97
- id,
98
- cleanups,
99
- () => document.removeEventListener("visibilitychange", onVisibilityChange)
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
- const observer = new ResizeObserver(() => {
107
- store.instances[id]?.embla?.reInit();
108
- syncFromEmbla(store, id);
109
- });
110
- observer.observe(viewport);
111
- addCleanup(id, cleanups, () => observer.disconnect());
112
- }
113
- function getOrCreate(store, id, options) {
114
- if (!store.instances[id]) {
115
- store.instances[id] = createInstance(options);
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
- return store.instances[id];
121
- }
122
- function initEmbla(store, id, viewport, cleanups) {
123
- const instance = store.instances[id];
124
- if (!instance) {
125
- return;
284
+ #addCleanup(id, cleanup) {
285
+ const previous = this.#cleanups.get(id);
286
+ this.#cleanups.set(id, () => {
287
+ previous?.();
288
+ cleanup();
289
+ });
126
290
  }
127
- destroyEmbla(instance, id, cleanups);
128
- instance.viewport = viewport;
129
- const plugins = [];
130
- const autoplay = createAutoplayPlugin(instance.options);
131
- if (autoplay) {
132
- plugins.push(autoplay);
133
- instance.autoplay = autoplay;
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
- const embla = EmblaCarousel(viewport, toEmblaOptions(instance.options), plugins);
136
- instance.embla = embla;
137
- const sync = () => syncFromEmbla(store, id);
138
- embla.on("select", sync);
139
- embla.on("scroll", sync);
140
- embla.on("reInit", sync);
141
- embla.on("autoplay:play", sync);
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
- const cleanups = /* @__PURE__ */ new Map();
155
- const store = {
156
- instances: {},
157
- create(id, options = {}) {
158
- getOrCreate(this, id, options);
159
- },
160
- destroy(id) {
161
- const instance = this.instances[id];
162
- if (!instance) {
163
- return;
164
- }
165
- destroyEmbla(instance, id, cleanups);
166
- delete this.instances[id];
167
- },
168
- bindViewport(id, viewport) {
169
- if (typeof window === "undefined" || typeof document === "undefined") {
170
- return;
171
- }
172
- if (!viewport) {
173
- const instance = this.instances[id];
174
- if (instance) {
175
- destroyEmbla(instance, id, cleanups);
176
- }
177
- return;
178
- }
179
- getOrCreate(this, id);
180
- initEmbla(this, id, viewport, cleanups);
181
- },
182
- next(id) {
183
- this.instances[id]?.embla?.scrollNext();
184
- },
185
- previous(id) {
186
- this.instances[id]?.embla?.scrollPrev();
187
- },
188
- goTo(id, index) {
189
- this.instances[id]?.embla?.scrollTo(index);
190
- },
191
- current(id) {
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
- switch (event.key) {
229
- case "ArrowLeft":
230
- case "ArrowUp":
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
- // src/index.ts
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": "0.1.0",
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 --sourcemap --out-dir dist && cp src/global.d.ts dist/global.d.ts",
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":";AAAA,OAAO,mBAAsE;AAC7E,OAAO,cAAqC;AAsE5C,SAAS,eAAe,UAA2B,CAAC,GAAqB;AACvE,SAAO;AAAA,IACL,cAAc;AAAA,IACd,aAAa;AAAA,IACb,UAAU;AAAA,IACV,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,aAAa;AAAA,IACb,cAAc,CAAC;AAAA,IACf;AAAA,IACA,UAAU,QAAQ,YAAY;AAAA,IAC9B,UAAU;AAAA,IACV,OAAO;AAAA,IACP,UAAU;AAAA,EACZ;AACF;AAEA,SAAS,eAAe,SAA4C;AAClE,SAAO;AAAA,IACL,MAAM,QAAQ,QAAQ;AAAA,IACtB,MAAM,QAAQ,QAAQ;AAAA,IACtB,OAAO,QAAQ,SAAS;AAAA,IACxB,eAAe,QAAQ,iBAAiB;AAAA,IACxC,UAAU,QAAQ,YAAY;AAAA,IAC9B,UAAU,QAAQ,YAAY;AAAA,EAChC;AACF;AAEA,SAAS,qBAAqB,SAA+C;AAC3E,MAAI,CAAC,QAAQ,UAAU;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,kBAAkB,QAAQ,mBAAmB,CAAC;AACpD,QAAM,mBAAmB,gBAAgB,oBAAoB;AAC7D,QAAM,oBAAoB,gBAAgB,qBAAqB,CAAC;AAEhE,SAAO,SAAS;AAAA,IACd,OAAO,gBAAgB,SAAS;AAAA,IAChC;AAAA,IACA;AAAA,IACA,eAAe,gBAAgB,iBAAiB;AAAA,IAChD,YAAY;AAAA,EACd,CAAC;AACH;AAEA,SAAS,cAAc,OAAsB,IAAkB;AAC7D,QAAM,WAAW,MAAM,UAAU,EAAE;AACnC,QAAM,QAAQ,UAAU;AACxB,MAAI,EAAE,YAAY,QAAQ;AACxB;AAAA,EACF;AAEA,QAAM,gBAAgB,SAAS;AAC/B,WAAS,eAAe,MAAM,mBAAmB;AACjD,WAAS,cAAc,MAAM,eAAe,EAAE;AAC9C,WAAS,WAAW,MAAM,eAAe;AACzC,WAAS,UAAU,MAAM,cAAc;AACvC,WAAS,cAAc,MAAM,cAAc;AAC3C,WAAS,eAAe,MAAM,aAAa;AAC3C,WAAS,UAAU,SAAS,iBAAiB;AAC7C,WAAS,SAAS,SAAS,cAAc,KAAK,SAAS,iBAAiB,SAAS,cAAc;AAC/F,WAAS,YAAY,SAAS,UAAU,UAAU,KAAK;AAEvD,MAAI,kBAAkB,SAAS,cAAc;AAC3C,aAAS,QAAQ,WAAW,SAAS,YAAY;AAAA,EACnD;AACF;AAEA,SAAS,aACP,UACA,IACA,UACM;AACN,WAAS,IAAI,EAAE,IAAI;AACnB,WAAS,OAAO,EAAE;AAClB,WAAS,OAAO,QAAQ;AACxB,WAAS,QAAQ;AACjB,WAAS,WAAW;AACpB,WAAS,WAAW;AACtB;AAEA,SAAS,WAAW,IAAY,UAAmC,SAA2B;AAC5F,QAAM,WAAW,SAAS,IAAI,EAAE;AAChC,WAAS,IAAI,IAAI,MAAM;AACrB,eAAW;AACX,YAAQ;AAAA,EACV,CAAC;AACH;AAEA,SAAS,wBACP,IACA,UACA,UACM;AACN,QAAM,iBAAiB,SAAS,QAAQ,iBAAiB,kBAAkB;AAC3E,MAAI,EAAE,SAAS,QAAQ,YAAY,iBAAiB;AAClD;AAAA,EACF;AAEA,QAAM,qBAAqB,MAAM;AAC/B,QAAI,SAAS,QAAQ;AACnB,eAAS,UAAU,KAAK;AAAA,IAC1B,OAAO;AACL,eAAS,UAAU,KAAK;AAAA,IAC1B;AACA,aAAS,YAAY,SAAS,UAAU,UAAU,KAAK;AAAA,EACzD;AAEA,WAAS,iBAAiB,oBAAoB,kBAAkB;AAChE;AAAA,IAAW;AAAA,IAAI;AAAA,IAAU,MACvB,SAAS,oBAAoB,oBAAoB,kBAAkB;AAAA,EACrE;AACF;AAEA,SAAS,oBACP,UACA,OACA,IACA,UACM;AACN,MAAI,OAAO,mBAAmB,aAAa;AACzC;AAAA,EACF;AAEA,QAAM,WAAW,IAAI,eAAe,MAAM;AACxC,UAAM,UAAU,EAAE,GAAG,OAAO,OAAO;AACnC,kBAAc,OAAO,EAAE;AAAA,EACzB,CAAC;AACD,WAAS,QAAQ,QAAQ;AACzB,aAAW,IAAI,UAAU,MAAM,SAAS,WAAW,CAAC;AACtD;AAEA,SAAS,YACP,OACA,IACA,SACkB;AAClB,MAAI,CAAC,MAAM,UAAU,EAAE,GAAG;AACxB,UAAM,UAAU,EAAE,IAAI,eAAe,OAAO;AAAA,EAC9C,WAAW,SAAS;AAClB,UAAM,UAAU,EAAE,EAAE,UAAU,EAAE,GAAG,MAAM,UAAU,EAAE,EAAE,SAAS,GAAG,QAAQ;AAC3E,UAAM,UAAU,EAAE,EAAE,WAAW,QAAQ,YAAY,MAAM,UAAU,EAAE,EAAE;AAAA,EACzE;AACA,SAAO,MAAM,UAAU,EAAE;AAC3B;AAEA,SAAS,UACP,OACA,IACA,UACA,UACM;AACN,QAAM,WAAW,MAAM,UAAU,EAAE;AACnC,MAAI,CAAC,UAAU;AACb;AAAA,EACF;AAEA,eAAa,UAAU,IAAI,QAAQ;AACnC,WAAS,WAAW;AAEpB,QAAM,UAAU,CAAC;AACjB,QAAM,WAAW,qBAAqB,SAAS,OAAO;AACtD,MAAI,UAAU;AACZ,YAAQ,KAAK,QAAQ;AACrB,aAAS,WAAW;AAAA,EACtB;AAEA,QAAM,QAAQ,cAAc,UAAU,eAAe,SAAS,OAAO,GAAG,OAAO;AAC/E,WAAS,QAAQ;AAEjB,QAAM,OAAO,MAAM,cAAc,OAAO,EAAE;AAC1C,QAAM,GAAG,UAAU,IAAI;AACvB,QAAM,GAAG,UAAU,IAAI;AACvB,QAAM,GAAG,UAAU,IAAI;AACvB,QAAM,GAAG,iBAAiB,IAAI;AAC9B,QAAM,GAAG,iBAAiB,IAAI;AAC9B,OAAK;AACL,sBAAoB,UAAU,OAAO,IAAI,QAAQ;AACjD,0BAAwB,IAAI,UAAU,QAAQ;AAE9C,MAAI,OAAO,0BAA0B,aAAa;AAChD,0BAAsB,MAAM;AAC1B,eAAS,OAAO,OAAO;AACvB,oBAAc,OAAO,EAAE;AAAA,IACzB,CAAC;AAAA,EACH;AACF;AAGO,SAAS,sBAAqC;AACnD,QAAM,WAAW,oBAAI,IAAwB;AAE7C,QAAM,QAAuB;AAAA,IAC3B,WAAW,CAAC;AAAA,IAEZ,OAAO,IAAI,UAAU,CAAC,GAAG;AACvB,kBAAY,MAAM,IAAI,OAAO;AAAA,IAC/B;AAAA,IAEA,QAAQ,IAAI;AACV,YAAM,WAAW,KAAK,UAAU,EAAE;AAClC,UAAI,CAAC,UAAU;AACb;AAAA,MACF;AACA,mBAAa,UAAU,IAAI,QAAQ;AACnC,aAAO,KAAK,UAAU,EAAE;AAAA,IAC1B;AAAA,IAEA,aAAa,IAAI,UAAU;AACzB,UAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aAAa;AACpE;AAAA,MACF;AAEA,UAAI,CAAC,UAAU;AACb,cAAM,WAAW,KAAK,UAAU,EAAE;AAClC,YAAI,UAAU;AACZ,uBAAa,UAAU,IAAI,QAAQ;AAAA,QACrC;AACA;AAAA,MACF;AAEA,kBAAY,MAAM,EAAE;AACpB,gBAAU,MAAM,IAAI,UAAU,QAAQ;AAAA,IACxC;AAAA,IAEA,KAAK,IAAI;AACP,WAAK,UAAU,EAAE,GAAG,OAAO,WAAW;AAAA,IACxC;AAAA,IAEA,SAAS,IAAI;AACX,WAAK,UAAU,EAAE,GAAG,OAAO,WAAW;AAAA,IACxC;AAAA,IAEA,KAAK,IAAI,OAAO;AACd,WAAK,UAAU,EAAE,GAAG,OAAO,SAAS,KAAK;AAAA,IAC3C;AAAA,IAEA,QAAQ,IAAI;AACV,aAAO,KAAK,UAAU,EAAE,GAAG,gBAAgB;AAAA,IAC7C;AAAA,IAEA,MAAM,IAAI;AACR,aAAO,KAAK,UAAU,EAAE,GAAG,eAAe;AAAA,IAC5C;AAAA,IAEA,QAAQ,IAAI;AACV,aAAO,KAAK,UAAU,EAAE,GAAG,WAAW;AAAA,IACxC;AAAA,IAEA,YAAY,IAAI;AACd,aAAO,KAAK,UAAU,EAAE,GAAG,eAAe;AAAA,IAC5C;AAAA,IAEA,KAAK,IAAI;AACP,WAAK,UAAU,EAAE,GAAG,UAAU,KAAK;AACnC,YAAM,WAAW,KAAK,UAAU,EAAE;AAClC,UAAI,UAAU;AACZ,iBAAS,YAAY,SAAS,UAAU,UAAU,KAAK;AAAA,MACzD;AAAA,IACF;AAAA,IAEA,MAAM,IAAI;AACR,WAAK,UAAU,EAAE,GAAG,UAAU,KAAK;AACnC,YAAM,WAAW,KAAK,UAAU,EAAE;AAClC,UAAI,UAAU;AACZ,iBAAS,YAAY,SAAS,UAAU,UAAU,KAAK;AAAA,MACzD;AAAA,IACF;AAAA,IAEA,UAAU,IAAI;AACZ,aAAO,KAAK,UAAU,EAAE,GAAG,aAAa;AAAA,IAC1C;AAAA,IAEA,SAAS,IAAI;AACX,aAAO,KAAK,UAAU,EAAE,GAAG,SAAS;AAAA,IACtC;AAAA,IAEA,cAAc,IAAI,OAAO;AACvB,YAAM,WAAW,KAAK,UAAU,EAAE;AAClC,UAAI,CAAC,UAAU,OAAO;AACpB;AAAA,MACF;AAEA,cAAQ,MAAM,KAAK;AAAA,QACjB,KAAK;AAAA,QACL,KAAK;AACH,gBAAM,eAAe;AACrB,eAAK,SAAS,EAAE;AAChB;AAAA,QACF,KAAK;AAAA,QACL,KAAK;AACH,gBAAM,eAAe;AACrB,eAAK,KAAK,EAAE;AACZ;AAAA,QACF,KAAK;AACH,gBAAM,eAAe;AACrB,eAAK,KAAK,IAAI,CAAC;AACf;AAAA,QACF,KAAK,OAAO;AACV,gBAAM,eAAe;AACrB,gBAAM,OAAO,KAAK,MAAM,EAAE,IAAI;AAC9B,cAAI,QAAQ,GAAG;AACb,iBAAK,KAAK,IAAI,IAAI;AAAA,UACpB;AACA;AAAA,QACF;AAAA,QACA;AACE;AAAA,MACJ;AAAA,IACF;AAAA,IAEA,cAAc,IAAI,UAAU,CAAC,GAAG;AAC9B,YAAM,WAAW,KAAK,UAAU,EAAE;AAClC,aAAO;AAAA,QACL,MAAM;AAAA,QACN,wBAAwB;AAAA,QACxB,aAAa,UAAU,YAAY;AAAA,QACnC,cAAc,QAAQ;AAAA,MACxB;AAAA,IACF;AAAA,IAEA,cAAc,KAAK,UAAU,CAAC,GAAG;AAC/B,YAAM,QAA+D;AAAA,QACnE,UAAU;AAAA,MACZ;AAEA,UAAI,QAAQ,cAAc,OAAO;AAC/B,cAAM,QAAQ,iBAAiB,QAAQ,aAAa,MAAM;AAAA,MAC5D;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,WAAW,IAAI,OAAO;AACpB,YAAM,UAAU,KAAK,QAAQ,EAAE;AAC/B,YAAM,QAAQ,KAAK,MAAM,EAAE;AAC3B,aAAO;AAAA,QACL,MAAM;AAAA,QACN,wBAAwB;AAAA,QACxB,cAAc,GAAG,QAAQ,CAAC,OAAO,KAAK;AAAA,QACtC,eAAe,YAAY,QAAQ,OAAO;AAAA,MAC5C;AAAA,IACF;AAAA,IAEA,eAAe,IAAI,OAAO;AACxB,YAAM,WAAW,KAAK,QAAQ,EAAE,MAAM;AACtC,aAAO;AAAA,QACL,MAAM;AAAA,QACN,cAAc,eAAe,QAAQ,CAAC;AAAA,QACtC,gBAAgB,WAAW,SAAS;AAAA,MACtC;AAAA,IACF;AAAA,IAEA,aAAa;AACX,iBAAW,MAAM,OAAO,KAAK,KAAK,SAAS,GAAG;AAC5C,aAAK,QAAQ,EAAE;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ACvae,SAAR,iBAA6D;AAClE,SAAO,SAAS,iBAAiB,QAAQ;AACvC,UAAM,QAAQ,oBAAoB;AAClC,WAAO,MAAM,YAAY,KAAK;AAC9B,WAAO,MAAM,YAAY,MAAM,OAAO,MAAM,UAAU,CAAC;AAAA,EACzD;AACF;","names":[]}