@qwanyx/carousel 0.1.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 ADDED
@@ -0,0 +1,138 @@
1
+ # @qwanyx/carousel
2
+
3
+ Universal slide/presentation engine for the Qwanyx ecosystem.
4
+
5
+ ## Features
6
+
7
+ - **Polymorphic slides**: Images, videos, documents, HTML, or custom React components
8
+ - **Multiple transitions**: Fade, slide, zoom, flip, cube
9
+ - **Rich navigation**: Arrows, dots, thumbnails, keyboard, touch/swipe
10
+ - **Presentation mode**: Fullscreen support with keyboard shortcuts
11
+ - **Autoplay**: With configurable intervals and pause-on-interaction
12
+ - **Responsive**: Adapts to any container size
13
+ - **TypeScript**: Full type safety
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @qwanyx/carousel
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ```tsx
24
+ import { Carousel, createSlide } from '@qwanyx/carousel';
25
+
26
+ const slides = [
27
+ createSlide.image('1', '/photo1.jpg', { title: 'Beautiful sunset' }),
28
+ createSlide.image('2', '/photo2.jpg', { title: 'Mountain view' }),
29
+ createSlide.video('3', '/video.mp4', { poster: '/poster.jpg' }),
30
+ ];
31
+
32
+ function MyGallery() {
33
+ return (
34
+ <Carousel
35
+ slides={slides}
36
+ transition="fade"
37
+ navigation={{ arrows: true, dots: true }}
38
+ allowFullscreen
39
+ />
40
+ );
41
+ }
42
+ ```
43
+
44
+ ## Slide Types
45
+
46
+ ### Image Slide
47
+
48
+ ```tsx
49
+ createSlide.image('id', '/image.jpg', {
50
+ title: 'Image title',
51
+ description: 'Description',
52
+ alt: 'Alt text',
53
+ objectFit: 'cover', // 'contain' | 'cover' | 'fill' | 'none'
54
+ });
55
+ ```
56
+
57
+ ### Video Slide
58
+
59
+ ```tsx
60
+ createSlide.video('id', '/video.mp4', {
61
+ poster: '/poster.jpg',
62
+ autoplay: true,
63
+ muted: true,
64
+ loop: true,
65
+ controls: true,
66
+ });
67
+ ```
68
+
69
+ ### Document Slide (PDF)
70
+
71
+ ```tsx
72
+ createSlide.document('id', '/document.pdf', {
73
+ title: 'My Document',
74
+ });
75
+ ```
76
+
77
+ ### HTML Slide
78
+
79
+ ```tsx
80
+ createSlide.html('id', '<div>Custom HTML content</div>', {
81
+ title: 'HTML Slide',
82
+ });
83
+ ```
84
+
85
+ ### Component Slide
86
+
87
+ ```tsx
88
+ createSlide.component('id', () => <MyCustomComponent />, {
89
+ title: 'Custom Component',
90
+ });
91
+ ```
92
+
93
+ ## Props
94
+
95
+ | Prop | Type | Default | Description |
96
+ |------|------|---------|-------------|
97
+ | `slides` | `Slide[]` | required | Array of slides |
98
+ | `initialIndex` | `number` | `0` | Starting slide index |
99
+ | `transition` | `TransitionType` | `'fade'` | Transition animation |
100
+ | `transitionDuration` | `number` | `300` | Duration in ms |
101
+ | `navigation` | `NavigationOptions \| boolean` | `true` | Navigation controls |
102
+ | `autoplay` | `AutoplayOptions \| boolean` | `false` | Autoplay settings |
103
+ | `loop` | `boolean` | `false` | Enable infinite loop |
104
+ | `aspectRatio` | `string` | `'16/9'` | Container aspect ratio |
105
+ | `allowFullscreen` | `boolean` | `true` | Enable fullscreen mode |
106
+ | `theme` | `'light' \| 'dark'` | `'dark'` | Color theme |
107
+ | `onSlideChange` | `function` | - | Callback on slide change |
108
+
109
+ ## Keyboard Shortcuts
110
+
111
+ | Key | Action |
112
+ |-----|--------|
113
+ | `←` `↑` | Previous slide |
114
+ | `→` `↓` | Next slide |
115
+ | `Space` | Play/pause autoplay |
116
+ | `F` | Toggle fullscreen |
117
+ | `Esc` | Exit fullscreen |
118
+
119
+ ## Ref Methods
120
+
121
+ ```tsx
122
+ const carouselRef = useRef<CarouselRef>(null);
123
+
124
+ // Available methods:
125
+ carouselRef.current?.next();
126
+ carouselRef.current?.prev();
127
+ carouselRef.current?.goTo(2);
128
+ carouselRef.current?.getCurrentIndex();
129
+ carouselRef.current?.enterFullscreen();
130
+ carouselRef.current?.exitFullscreen();
131
+ carouselRef.current?.toggleFullscreen();
132
+ carouselRef.current?.play();
133
+ carouselRef.current?.pause();
134
+ ```
135
+
136
+ ## License
137
+
138
+ MIT
@@ -0,0 +1,372 @@
1
+ import React, { CSSProperties, ReactNode } from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+
4
+ /**
5
+ * Position in the slide (percentage-based for responsiveness)
6
+ */
7
+ interface Position {
8
+ x: number;
9
+ y: number;
10
+ }
11
+ /**
12
+ * Size of an object
13
+ */
14
+ interface Size {
15
+ width: number;
16
+ height: number;
17
+ unit?: 'percent' | 'px';
18
+ }
19
+ /**
20
+ * Transform properties
21
+ */
22
+ interface Transform {
23
+ rotation?: number;
24
+ scaleX?: number;
25
+ scaleY?: number;
26
+ skewX?: number;
27
+ skewY?: number;
28
+ originX?: number;
29
+ originY?: number;
30
+ }
31
+ /**
32
+ * Animation keyframe
33
+ */
34
+ interface AnimationKeyframe {
35
+ offset: number;
36
+ properties: Partial<{
37
+ opacity: number;
38
+ x: number;
39
+ y: number;
40
+ scale: number;
41
+ rotation: number;
42
+ }>;
43
+ }
44
+ /**
45
+ * Animation definition
46
+ */
47
+ interface Animation {
48
+ name?: string;
49
+ duration: number;
50
+ delay?: number;
51
+ easing?: string;
52
+ iterations?: number;
53
+ direction?: 'normal' | 'reverse' | 'alternate';
54
+ fillMode?: 'none' | 'forwards' | 'backwards' | 'both';
55
+ keyframes?: AnimationKeyframe[];
56
+ preset?: 'fadeIn' | 'fadeOut' | 'slideInLeft' | 'slideInRight' | 'slideInUp' | 'slideInDown' | 'zoomIn' | 'zoomOut' | 'bounce' | 'pulse';
57
+ }
58
+ /**
59
+ * Object types that can be placed on a slide
60
+ */
61
+ type ObjectType = 'text' | 'image' | 'video' | 'audio' | 'shape' | 'component' | 'group';
62
+ /**
63
+ * Base properties for all objects
64
+ */
65
+ interface BaseObject {
66
+ id: string;
67
+ type: ObjectType;
68
+ name?: string;
69
+ position: Position;
70
+ size?: Size;
71
+ transform?: Transform;
72
+ opacity?: number;
73
+ visible?: boolean;
74
+ locked?: boolean;
75
+ animation?: Animation;
76
+ style?: CSSProperties;
77
+ className?: string;
78
+ }
79
+ /**
80
+ * Text object
81
+ */
82
+ interface TextObject extends BaseObject {
83
+ type: 'text';
84
+ content: string;
85
+ fontSize?: number;
86
+ fontFamily?: string;
87
+ fontWeight?: string | number;
88
+ fontStyle?: 'normal' | 'italic';
89
+ textAlign?: 'left' | 'center' | 'right' | 'justify';
90
+ verticalAlign?: 'top' | 'middle' | 'bottom';
91
+ color?: string;
92
+ backgroundColor?: string;
93
+ lineHeight?: number;
94
+ letterSpacing?: number;
95
+ textShadow?: string;
96
+ padding?: number | string;
97
+ }
98
+ /**
99
+ * Image object
100
+ */
101
+ interface ImageObject extends BaseObject {
102
+ type: 'image';
103
+ src: string;
104
+ alt?: string;
105
+ objectFit?: 'contain' | 'cover' | 'fill' | 'none';
106
+ objectPosition?: string;
107
+ borderRadius?: number | string;
108
+ border?: string;
109
+ shadow?: string;
110
+ filter?: string;
111
+ }
112
+ /**
113
+ * Video object
114
+ */
115
+ interface VideoObject extends BaseObject {
116
+ type: 'video';
117
+ src: string;
118
+ poster?: string;
119
+ autoplay?: boolean;
120
+ muted?: boolean;
121
+ loop?: boolean;
122
+ controls?: boolean;
123
+ objectFit?: 'contain' | 'cover' | 'fill';
124
+ }
125
+ /**
126
+ * Audio object (invisible, but plays sound)
127
+ */
128
+ interface AudioObject extends BaseObject {
129
+ type: 'audio';
130
+ src: string;
131
+ autoplay?: boolean;
132
+ loop?: boolean;
133
+ volume?: number;
134
+ showWaveform?: boolean;
135
+ showControls?: boolean;
136
+ }
137
+ /**
138
+ * Shape object (rectangle, circle, etc.)
139
+ */
140
+ interface ShapeObject extends BaseObject {
141
+ type: 'shape';
142
+ shape: 'rectangle' | 'circle' | 'ellipse' | 'triangle' | 'polygon' | 'line' | 'arrow';
143
+ fill?: string;
144
+ stroke?: string;
145
+ strokeWidth?: number;
146
+ borderRadius?: number | string;
147
+ points?: number;
148
+ startPoint?: Position;
149
+ endPoint?: Position;
150
+ }
151
+ /**
152
+ * Custom React component object
153
+ */
154
+ interface ComponentObject extends BaseObject {
155
+ type: 'component';
156
+ render: (props: {
157
+ isActive: boolean;
158
+ slideIndex: number;
159
+ }) => ReactNode;
160
+ props?: Record<string, unknown>;
161
+ }
162
+ /**
163
+ * Group of objects (can be transformed together)
164
+ */
165
+ interface GroupObject extends BaseObject {
166
+ type: 'group';
167
+ children: SlideObject[];
168
+ }
169
+ /**
170
+ * Union of all object types
171
+ */
172
+ type SlideObject = TextObject | ImageObject | VideoObject | AudioObject | ShapeObject | ComponentObject | GroupObject;
173
+ /**
174
+ * Layer containing objects
175
+ */
176
+ interface Layer {
177
+ id: string;
178
+ name: string;
179
+ visible: boolean;
180
+ locked: boolean;
181
+ opacity: number;
182
+ blendMode?: 'normal' | 'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten';
183
+ objects: SlideObject[];
184
+ }
185
+ /**
186
+ * Background configuration
187
+ */
188
+ interface SlideBackground {
189
+ type: 'color' | 'gradient' | 'image' | 'video';
190
+ value: string;
191
+ opacity?: number;
192
+ blur?: number;
193
+ overlay?: string;
194
+ }
195
+ /**
196
+ * Slide transition
197
+ */
198
+ type TransitionType = 'none' | 'fade' | 'slide' | 'slideLeft' | 'slideRight' | 'slideUp' | 'slideDown' | 'zoom' | 'zoomIn' | 'zoomOut' | 'flip' | 'cube' | 'rotate';
199
+ interface SlideTransition {
200
+ type: TransitionType;
201
+ duration?: number;
202
+ easing?: string;
203
+ }
204
+ /**
205
+ * Complete slide definition
206
+ */
207
+ interface Slide {
208
+ id: string;
209
+ name?: string;
210
+ layers: Layer[];
211
+ background?: SlideBackground;
212
+ duration?: number;
213
+ enterTransition?: SlideTransition;
214
+ exitTransition?: SlideTransition;
215
+ notes?: string;
216
+ thumbnail?: string;
217
+ tags?: string[];
218
+ }
219
+ /**
220
+ * Navigation options
221
+ */
222
+ interface NavigationOptions {
223
+ arrows?: boolean;
224
+ dots?: boolean;
225
+ thumbnails?: boolean;
226
+ keyboard?: boolean;
227
+ touch?: boolean;
228
+ mouseWheel?: boolean;
229
+ progress?: boolean;
230
+ }
231
+ /**
232
+ * Autoplay options
233
+ */
234
+ interface AutoplayOptions {
235
+ enabled: boolean;
236
+ interval?: number;
237
+ pauseOnHover?: boolean;
238
+ pauseOnInteraction?: boolean;
239
+ }
240
+ /**
241
+ * Main carousel/presentation props
242
+ */
243
+ interface CarouselProps {
244
+ slides: Slide[];
245
+ initialIndex?: number;
246
+ transition?: TransitionType;
247
+ transitionDuration?: number;
248
+ navigation?: NavigationOptions | boolean;
249
+ autoplay?: AutoplayOptions | boolean;
250
+ loop?: boolean;
251
+ aspectRatio?: string;
252
+ allowFullscreen?: boolean;
253
+ theme?: 'light' | 'dark' | 'auto';
254
+ className?: string;
255
+ style?: CSSProperties;
256
+ onSlideChange?: (index: number, slide: Slide) => void;
257
+ onFullscreenChange?: (isFullscreen: boolean) => void;
258
+ onObjectClick?: (object: SlideObject, slide: Slide) => void;
259
+ }
260
+ /**
261
+ * Carousel ref methods
262
+ */
263
+ interface CarouselRef {
264
+ next: () => void;
265
+ prev: () => void;
266
+ goTo: (index: number) => void;
267
+ getCurrentIndex: () => number;
268
+ getCurrentSlide: () => Slide;
269
+ enterFullscreen: () => void;
270
+ exitFullscreen: () => void;
271
+ toggleFullscreen: () => void;
272
+ play: () => void;
273
+ pause: () => void;
274
+ isPlaying: () => boolean;
275
+ }
276
+ /**
277
+ * Create a simple slide with just objects (default layer)
278
+ */
279
+ declare function createSimpleSlide(id: string, objects: SlideObject[], options?: Partial<Omit<Slide, 'id' | 'layers'>>): Slide;
280
+ /**
281
+ * Create a single-image slide (convenience method)
282
+ */
283
+ declare function createImageSlide(id: string, src: string, options?: {
284
+ title?: string;
285
+ objectFit?: ImageObject['objectFit'];
286
+ background?: SlideBackground;
287
+ }): Slide;
288
+
289
+ /**
290
+ * Qwanyx Carousel - Universal slide/presentation engine
291
+ */
292
+ declare const Carousel: React.ForwardRefExoticComponent<CarouselProps & React.RefAttributes<CarouselRef>>;
293
+
294
+ interface SlideRendererProps {
295
+ slide: Slide;
296
+ isActive: boolean;
297
+ slideIndex: number;
298
+ }
299
+ /**
300
+ * Main slide renderer - renders background, layers, and all objects
301
+ */
302
+ declare function SlideRenderer({ slide, isActive, slideIndex }: SlideRendererProps): react_jsx_runtime.JSX.Element;
303
+
304
+ interface ThumbnailsProps {
305
+ slides: Slide[];
306
+ currentIndex: number;
307
+ onSelect: (index: number) => void;
308
+ position?: 'bottom' | 'left' | 'right';
309
+ size?: 'small' | 'medium' | 'large';
310
+ theme?: 'light' | 'dark';
311
+ }
312
+ /**
313
+ * Thumbnails navigation component
314
+ */
315
+ declare function Thumbnails({ slides, currentIndex, onSelect, position, size, theme, }: ThumbnailsProps): react_jsx_runtime.JSX.Element;
316
+
317
+ interface UseCarouselOptions {
318
+ slides: Slide[];
319
+ initialIndex?: number;
320
+ loop?: boolean;
321
+ autoplay?: AutoplayOptions | boolean;
322
+ onSlideChange?: (index: number, slide: Slide) => void;
323
+ }
324
+ declare function useCarousel({ slides, initialIndex, loop, autoplay, onSlideChange, }: UseCarouselOptions): {
325
+ currentIndex: number;
326
+ currentSlide: Slide;
327
+ totalSlides: number;
328
+ direction: "next" | "prev";
329
+ isPlaying: boolean;
330
+ canGoNext: boolean;
331
+ canGoPrev: boolean;
332
+ next: () => void;
333
+ prev: () => void;
334
+ goTo: (index: number) => void;
335
+ play: () => void;
336
+ pause: () => void;
337
+ handleInteraction: () => void;
338
+ };
339
+
340
+ declare const createSlide: {
341
+ /**
342
+ * Create a simple image slide (single image, full slide)
343
+ */
344
+ image: (id: string, src: string, options?: {
345
+ title?: string;
346
+ objectFit?: "contain" | "cover" | "fill" | "none";
347
+ background?: SlideBackground;
348
+ }) => Slide;
349
+ /**
350
+ * Create a video slide
351
+ */
352
+ video: (id: string, src: string, options?: {
353
+ poster?: string;
354
+ autoplay?: boolean;
355
+ muted?: boolean;
356
+ loop?: boolean;
357
+ controls?: boolean;
358
+ }) => Slide;
359
+ /**
360
+ * Create a slide with custom objects
361
+ */
362
+ custom: (id: string, objects: SlideObject[], options?: Partial<Omit<Slide, "id" | "layers">>) => Slide;
363
+ /**
364
+ * Create a slide with a React component
365
+ */
366
+ component: (id: string, render: (props: {
367
+ isActive: boolean;
368
+ slideIndex: number;
369
+ }) => React.ReactNode, options?: Partial<Omit<Slide, "id" | "layers">>) => Slide;
370
+ };
371
+
372
+ export { type Animation, type AnimationKeyframe, type AudioObject, type AutoplayOptions, type BaseObject, Carousel, type CarouselProps, type CarouselRef, type ComponentObject, type GroupObject, type ImageObject, type Layer, type NavigationOptions, type ObjectType, type Position, type ShapeObject, type Size, type Slide, type SlideBackground, type SlideObject, SlideRenderer, type SlideTransition, type TextObject, Thumbnails, type Transform, type TransitionType, type VideoObject, createImageSlide, createSimpleSlide, createSlide, useCarousel };