awesome-ring-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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 AwesomeRingCarousel Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [2026-27] [Pravinkumar Dabade]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,555 @@
1
+ # AwesomeRingCarousel
2
+
3
+ A beautiful, interactive 3D ring carousel component for React with smooth animations, multiple themes, and flexible data handling. Perfect for showcasing products, portfolios, or any collection of items in an eye-catching carousel format.
4
+
5
+ ## Features
6
+
7
+ ✨ **3D Ring Carousel** - Visually stunning 3D perspective effects
8
+ 🎨 **4 Built-in Themes** - Dark, Light, Neon, and Glass themes
9
+ ⌨️ **Multiple Control Methods** - Mouse drag, keyboard arrows, touch & programmatic
10
+ 🎯 **Auto-Rotate** - Automatic carousel rotation with customizable speed
11
+ 📱 **Responsive** - Works seamlessly on different screen sizes
12
+ ⚡ **Smooth Physics** - Realistic inertia and friction animations
13
+ ✨ **Glow Effects** - Beautiful focus glow for the active item
14
+ 🔧 **Highly Customizable** - Full control over speed, friction, perspective, and more
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install awesome-ring-carousel
20
+ ```
21
+
22
+ Or with pnpm:
23
+
24
+ ```bash
25
+ pnpm add awesome-ring-carousel
26
+ ```
27
+
28
+ Or with yarn:
29
+
30
+ ```bash
31
+ yarn add awesome-ring-carousel
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```jsx
37
+ import { AwesomeRingCarousel } from 'awesome-ring-carousel';
38
+ import 'awesome-ring-carousel/dist/style.css';
39
+
40
+ const slides = [
41
+ { title: 'Slide 1', description: 'First slide', image: 'url1.jpg' },
42
+ { title: 'Slide 2', description: 'Second slide', image: 'url2.jpg' },
43
+ ];
44
+
45
+ export default function App() {
46
+ return (
47
+ <div style={{ width: '100%', height: '100vh' }}>
48
+ <AwesomeRingCarousel data={slides} />
49
+ </div>
50
+ );
51
+ }
52
+ ```
53
+
54
+ ## Data Format
55
+
56
+ Each item in the carousel requires the following structure:
57
+
58
+ ```js
59
+ const slide = {
60
+ title: 'Slide 1', // Required: Title displayed on the card
61
+ description: 'First slide', // Optional: Subtitle or description
62
+ image: 'url1.jpg', // Optional: Image URL
63
+ tag: 'Featured', // Optional: Tag label
64
+ color: '#ff6b6b', // Optional: Background color (hex)
65
+ onClick: () => {}, // Optional: Click handler
66
+ };
67
+ ```
68
+
69
+ For TypeScript users, this object shape maps directly to the `SlideData` type exported by the package.
70
+
71
+ ## Component Props
72
+
73
+ | Prop | Type | Default | Description |
74
+ |------|------|---------|-------------|
75
+ | `data` | `SlideData[]` | **Required** | Array of slide objects to display in the carousel |
76
+ | `autoRotate` | `boolean` | `true` | Enable automatic carousel rotation |
77
+ | `rotationSpeed` | `number` | `2800` | Time in milliseconds between auto-rotations |
78
+ | `friction` | `number` | `0.88` | Friction coefficient for inertia animations (0-1). Higher = more damping |
79
+ | `focusGlow` | `boolean` | `true` | Enable glow effect on the focused card |
80
+ | `theme` | `'dark' \| 'light' \| 'neon' \| 'glass'` | `'dark'` | Visual theme for the carousel |
81
+ | `perspective` | `number` | `1100` | 3D perspective depth in pixels |
82
+ | `onFocusChange` | `(index: number) => void` | `undefined` | Callback fired when the focused item changes |
83
+ | `className` | `string` | `undefined` | Custom CSS class for the carousel container |
84
+
85
+ ## Available Themes
86
+
87
+ ### Dark Theme
88
+ Modern dark theme with cyan accents. Perfect for tech products and modern designs.
89
+
90
+ ```jsx
91
+ <AwesomeRingCarousel data={slides} theme="dark" />
92
+ ```
93
+
94
+ ### Light Theme
95
+ Clean light theme with violet accents. Great for portfolios and creative showcases.
96
+
97
+ ```jsx
98
+ <AwesomeRingCarousel data={slides} theme="light" />
99
+ ```
100
+
101
+ ### Neon Theme
102
+ Vibrant neon magenta and cyan theme. Bold and eye-catching for entertainment and gaming.
103
+
104
+ ```jsx
105
+ <AwesomeRingCarousel data={slides} theme="neon" />
106
+ ```
107
+
108
+ ### Glass Theme
109
+ Frosted glass effect with semi-transparent cards. Modern and elegant for premium brands.
110
+
111
+ ```jsx
112
+ <AwesomeRingCarousel data={slides} theme="glass" />
113
+ ```
114
+
115
+ ## Usage Examples
116
+
117
+ ### Example 1: Static Data Source
118
+
119
+ Use hardcoded data directly in your component:
120
+
121
+ ```jsx
122
+ import { AwesomeRingCarousel } from 'awesome-ring-carousel';
123
+ import 'awesome-ring-carousel/dist/style.css';
124
+
125
+ export default function StaticDataExample() {
126
+ const staticSlides = [
127
+ {
128
+ title: 'Product A',
129
+ description: 'Premium quality product',
130
+ image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=500&h=300&fit=crop',
131
+ tag: 'Featured',
132
+ onClick: () => console.log('Product A clicked')
133
+ },
134
+ {
135
+ title: 'Product B',
136
+ description: 'Best seller item',
137
+ image: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=500&h=300&fit=crop',
138
+ tag: 'Sale',
139
+ onClick: () => console.log('Product B clicked')
140
+ },
141
+ {
142
+ title: 'Product C',
143
+ description: 'New arrival',
144
+ image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=500&h=300&fit=crop',
145
+ tag: 'New',
146
+ onClick: () => console.log('Product C clicked')
147
+ },
148
+ {
149
+ title: 'Product D',
150
+ description: 'Limited edition',
151
+ image: 'https://images.unsplash.com/photo-1517457373614-b7152f800fd1?w=500&h=300&fit=crop',
152
+ tag: 'Limited',
153
+ onClick: () => console.log('Product D clicked')
154
+ },
155
+ ];
156
+
157
+ return (
158
+ <div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
159
+ <h1 style={{ textAlign: 'center', padding: '20px' }}>Static Data Example</h1>
160
+ <div style={{ flex: 1 }}>
161
+ <AwesomeRingCarousel
162
+ data={staticSlides}
163
+ theme="dark"
164
+ onFocusChange={(index) => console.log('Focused item:', index)}
165
+ />
166
+ </div>
167
+ </div>
168
+ );
169
+ }
170
+ ```
171
+
172
+ ### Example 2: JSON File Data Source
173
+
174
+ Load carousel data from a JSON file:
175
+
176
+ ```jsx
177
+ import { useEffect, useState } from 'react';
178
+ import { AwesomeRingCarousel } from 'awesome-ring-carousel';
179
+ import 'awesome-ring-carousel/dist/style.css';
180
+
181
+ export default function JSONDataExample() {
182
+ const [slides, setSlides] = useState([]);
183
+ const [loading, setLoading] = useState(true);
184
+ const [error, setError] = useState(null);
185
+
186
+ useEffect(() => {
187
+ // Load slides from JSON file in public folder
188
+ fetch('/carousel-data.json')
189
+ .then((response) => {
190
+ if (!response.ok) throw new Error('Failed to load data');
191
+ return response.json();
192
+ })
193
+ .then((data) => {
194
+ setSlides(data);
195
+ setLoading(false);
196
+ })
197
+ .catch((err) => {
198
+ setError(err.message);
199
+ setLoading(false);
200
+ });
201
+ }, []);
202
+
203
+ if (loading) return <div style={{ textAlign: 'center', padding: '20px' }}>Loading...</div>;
204
+ if (error) return <div style={{ textAlign: 'center', padding: '20px', color: 'red' }}>Error: {error}</div>;
205
+
206
+ return (
207
+ <div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
208
+ <h1 style={{ textAlign: 'center', padding: '20px' }}>JSON File Data Example</h1>
209
+ <div style={{ flex: 1 }}>
210
+ <AwesomeRingCarousel
211
+ data={slides}
212
+ theme="light"
213
+ autoRotate={true}
214
+ rotationSpeed={3000}
215
+ onFocusChange={(index) => console.log('Focused item:', index)}
216
+ />
217
+ </div>
218
+ </div>
219
+ );
220
+ }
221
+ ```
222
+
223
+ **Sample `carousel-data.json` (place in `public` folder):**
224
+
225
+ ```json
226
+ [
227
+ {
228
+ "title": "Portfolio Project 1",
229
+ "description": "Award-winning design system",
230
+ "image": "https://images.unsplash.com/photo-1561070791-2526d30994b5?w=500&h=300&fit=crop",
231
+ "tag": "Design",
232
+ "color": "#1e3a8a"
233
+ },
234
+ {
235
+ "title": "Portfolio Project 2",
236
+ "description": "Enterprise web application",
237
+ "image": "https://images.unsplash.com/photo-1517694712202-14dd9538aa97?w=500&h=300&fit=crop",
238
+ "tag": "Development",
239
+ "color": "#064e3b"
240
+ },
241
+ {
242
+ "title": "Portfolio Project 3",
243
+ "description": "Mobile app with 10M+ downloads",
244
+ "image": "https://images.unsplash.com/photo-1560807707-38cc612d91b3?w=500&h=300&fit=crop",
245
+ "tag": "Mobile",
246
+ "color": "#5b1f39"
247
+ },
248
+ {
249
+ "title": "Portfolio Project 4",
250
+ "description": "Real-time data visualization",
251
+ "image": "https://images.unsplash.com/photo-1516321295223-4f2d3f2cf1e1?w=500&h=300&fit=crop",
252
+ "tag": "Analytics",
253
+ "color": "#78350f"
254
+ }
255
+ ]
256
+ ```
257
+
258
+ ### Example 3: REST API Data Source
259
+
260
+ Fetch carousel data from a REST API:
261
+
262
+ ```jsx
263
+ import { useEffect, useState } from 'react';
264
+ import { AwesomeRingCarousel } from 'awesome-ring-carousel';
265
+ import 'awesome-ring-carousel/dist/style.css';
266
+
267
+ export default function RESTAPIExample() {
268
+ const [slides, setSlides] = useState([]);
269
+ const [loading, setLoading] = useState(true);
270
+ const [error, setError] = useState(null);
271
+ const [focusedItem, setFocusedItem] = useState(null);
272
+
273
+ useEffect(() => {
274
+ // Fetch from a REST API endpoint
275
+ fetch('https://api.example.com/carousel-items')
276
+ .then((response) => {
277
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
278
+ return response.json();
279
+ })
280
+ .then((data) => {
281
+ // Transform API response to match SlideData format
282
+ const transformedData = data.map((item) => ({
283
+ title: item.name || item.title,
284
+ description: item.summary || item.description,
285
+ image: item.thumbnail || item.image,
286
+ tag: item.category || item.type,
287
+ color: item.accentColor,
288
+ onClick: () => console.log(`Clicked: ${item.id}`)
289
+ }));
290
+ setSlides(transformedData);
291
+ setLoading(false);
292
+ })
293
+ .catch((err) => {
294
+ setError(err.message);
295
+ setLoading(false);
296
+ });
297
+ }, []);
298
+
299
+ const handleFocusChange = (index) => {
300
+ setFocusedItem(slides[index] || null);
301
+ console.log(`Focused on: ${slides[index]?.title}`);
302
+ };
303
+
304
+ if (loading) {
305
+ return (
306
+ <div style={{ textAlign: 'center', padding: '20px', color: '#999' }}>
307
+ Loading carousel data...
308
+ </div>
309
+ );
310
+ }
311
+
312
+ if (error) {
313
+ return (
314
+ <div style={{ textAlign: 'center', padding: '20px', color: 'red' }}>
315
+ Error loading data: {error}
316
+ </div>
317
+ );
318
+ }
319
+
320
+ return (
321
+ <div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
322
+ <h1 style={{ textAlign: 'center', padding: '20px' }}>REST API Data Example</h1>
323
+
324
+ {focusedItem && (
325
+ <div style={{
326
+ textAlign: 'center',
327
+ padding: '10px 20px',
328
+ backgroundColor: 'rgba(0, 245, 255, 0.1)',
329
+ borderBottom: '1px solid rgba(0, 245, 255, 0.2)',
330
+ }}>
331
+ <p>Currently viewing: <strong>{focusedItem.title}</strong></p>
332
+ {focusedItem.description && <p style={{ fontSize: '0.9em', color: '#aaa' }}>{focusedItem.description}</p>}
333
+ </div>
334
+ )}
335
+
336
+ <div style={{ flex: 1 }}>
337
+ <AwesomeRingCarousel
338
+ data={slides}
339
+ theme="neon"
340
+ autoRotate={false}
341
+ friction={0.85}
342
+ perspective={1200}
343
+ focusGlow={true}
344
+ onFocusChange={handleFocusChange}
345
+ />
346
+ </div>
347
+
348
+ <div style={{
349
+ textAlign: 'center',
350
+ padding: '15px',
351
+ fontSize: '0.85em',
352
+ color: '#888',
353
+ borderTop: '1px solid rgba(255, 255, 255, 0.1)',
354
+ }}>
355
+ Use arrow keys or drag to navigate • Current count: {slides.length} items
356
+ </div>
357
+ </div>
358
+ );
359
+ }
360
+ ```
361
+
362
+ ## Customization Examples
363
+
364
+ ### Custom Styling with Themes
365
+
366
+ ```jsx
367
+ import { AwesomeRingCarousel } from 'awesome-ring-carousel';
368
+ import 'awesome-ring-carousel/dist/style.css';
369
+
370
+ export default function CustomizedCarousel() {
371
+ const carouselContainer = {
372
+ width: '100%',
373
+ height: '100vh',
374
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
375
+ };
376
+
377
+ const slides = [/* your slides */];
378
+
379
+ return (
380
+ <div style={carouselContainer}>
381
+ <AwesomeRingCarousel
382
+ data={slides}
383
+ theme="glass"
384
+ autoRotate={true}
385
+ rotationSpeed={3500}
386
+ friction={0.90}
387
+ perspective={1300}
388
+ focusGlow={true}
389
+ onFocusChange={(index) => {
390
+ // Custom handler
391
+ }}
392
+ />
393
+ </div>
394
+ );
395
+ }
396
+ ```
397
+
398
+ ### Controlled Carousel with External Controls
399
+
400
+ ```jsx
401
+ import { useRef, useState } from 'react';
402
+ import { AwesomeRingCarousel } from 'awesome-ring-carousel';
403
+ import 'awesome-ring-carousel/dist/style.css';
404
+
405
+ export default function ControlledCarousel() {
406
+ const carouselRef = useRef(null);
407
+ const [focusedIndex, setFocusedIndex] = useState(0);
408
+
409
+ const slides = [
410
+ { title: 'Slide 1', description: 'First', image: 'url1.jpg' },
411
+ { title: 'Slide 2', description: 'Second', image: 'url2.jpg' },
412
+ { title: 'Slide 3', description: 'Third', image: 'url3.jpg' },
413
+ ];
414
+
415
+ return (
416
+ <div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
417
+ <div style={{ flex: 1 }}>
418
+ <AwesomeRingCarousel
419
+ ref={carouselRef}
420
+ data={slides}
421
+ theme="dark"
422
+ onFocusChange={setFocusedIndex}
423
+ />
424
+ </div>
425
+
426
+ <div style={{ display: 'flex', gap: '10px', justifyContent: 'center', padding: '20px' }}>
427
+ <button onClick={() => {/* prev */}}>← Previous</button>
428
+ <span>{focusedIndex + 1} / {slides.length}</span>
429
+ <button onClick={() => {/* next */}}>Next →</button>
430
+ </div>
431
+ </div>
432
+ );
433
+ }
434
+ ```
435
+
436
+ ## Interaction Methods
437
+
438
+ ### Mouse Interactions
439
+ - **Drag**: Click and drag left/right to rotate the carousel
440
+ - **Click**: Click on a side item to bring it to center
441
+
442
+ ### Keyboard Interactions
443
+ - **Arrow Left**: Rotate to previous item
444
+ - **Arrow Right**: Rotate to next item
445
+
446
+ ### Mouse Events
447
+ - **Hover**: Pauses auto-rotation when hovering over the carousel
448
+ - **Leave**: Resumes auto-rotation when mouse leaves
449
+
450
+ ## Performance Tips
451
+
452
+ 1. **Optimize Images**: Use optimized image URLs for better performance
453
+ 2. **Lazy Loading**: Load large datasets progressively
454
+ 3. **Memoize Callbacks**: Use `useCallback` for `onFocusChange` handlers
455
+ 4. **Responsive Container**: Set appropriate dimensions for different screen sizes
456
+
457
+ ```jsx
458
+ // Example with responsive sizing
459
+ <div style={{
460
+ width: '100%',
461
+ height: window.innerWidth < 768 ? '60vh' : '100vh'
462
+ }}>
463
+ <AwesomeRingCarousel data={slides} />
464
+ </div>
465
+ ```
466
+
467
+ ## Styling
468
+
469
+ The component comes with built-in styles. Import the CSS file:
470
+
471
+ ```jsx
472
+ import 'awesome-ring-carousel/dist/style.css';
473
+ ```
474
+
475
+ All theme colors and styles are defined in the included CSS. You can override default styles with custom CSS classes:
476
+
477
+ ```css
478
+ /* Override carousel styles */
479
+ .custom-carousel {
480
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
481
+ }
482
+ ```
483
+
484
+ ```jsx
485
+ <AwesomeRingCarousel
486
+ data={slides}
487
+ className="custom-carousel"
488
+ />
489
+ ```
490
+
491
+ ## Browser Support
492
+
493
+ - Chrome/Edge (latest)
494
+ - Firefox (latest)
495
+ - Safari (latest)
496
+ - Mobile browsers (iOS Safari, Chrome Mobile)
497
+
498
+ ## API Reference
499
+
500
+ ### Component Props (Detailed)
501
+
502
+ #### `data: SlideData[]` (Required)
503
+ Array of slide objects. Each object must have at least a `title` property. See [Data Format](#data-format) above.
504
+
505
+ #### `autoRotate: boolean` (Default: `true`)
506
+ When enabled, the carousel automatically rotates to the next item after `rotationSpeed` milliseconds.
507
+
508
+ #### `rotationSpeed: number` (Default: `2800`)
509
+ Time in milliseconds between auto-rotations. Only applies if `autoRotate` is `true`.
510
+
511
+ #### `friction: number` (Default: `0.88`)
512
+ Controls how quickly animations decelerate. Range: 0-1
513
+ - `0.8` - More responsive, snappier animations
514
+ - `0.88` - Default, balanced feel
515
+ - `0.95` - Slower, smoother animations
516
+
517
+ #### `focusGlow: boolean` (Default: `true`)
518
+ Displays a glowing effect around the currently focused card. The glow color matches the selected theme.
519
+
520
+ #### `theme: 'dark' | 'light' | 'neon' | 'glass'` (Default: `'dark'`)
521
+ Selects the visual appearance of the carousel. See [Available Themes](#available-themes).
522
+
523
+ #### `perspective: number` (Default: `1100`)
524
+ Controls the 3D perspective depth in pixels. Higher values create less dramatic 3D effects.
525
+ - `800` - Strong 3D effect, cards spread wider
526
+ - `1100` - Default, balanced 3D
527
+ - `1500` - Subtle 3D, cards closer together
528
+
529
+ #### `onFocusChange: (index: number) => void`
530
+ Callback function fired whenever the focused item changes. Receives the index of the newly focused item.
531
+
532
+ #### `className: string`
533
+ Custom CSS class applied to the carousel container for additional customization.
534
+
535
+ ## Troubleshooting
536
+
537
+ ### Carousel not rotating
538
+ - Check that `autoRotate` is set to `true`
539
+ - Verify `rotationSpeed` is not too high (should be in milliseconds)
540
+ - Ensure the data array has at least 2 items
541
+
542
+ ### Images not displaying
543
+ - Verify image URLs are accessible and correct
544
+ - Check browser console for CORS errors
545
+ - Use absolute URLs for remote images
546
+
547
+ ### 3D effects not visible
548
+ - Ensure CSS is imported: `import 'awesome-ring-carousel/dist/style.css'`
549
+ - Check that `perspective` prop is not set too high
550
+ - Verify browser supports 3D transforms (all modern browsers)
551
+
552
+ ### Performance issues on mobile
553
+ - Reduce the number of images or use smaller dimensions
554
+ - Set `autoRotate={false}` if performance is critical
555
+ - Use optimized/compressed images
@@ -0,0 +1,37 @@
1
+ [
2
+ {
3
+ "title": "Portfolio Project 1",
4
+ "description": "Award-winning design system",
5
+ "image": "https://images.unsplash.com/photo-1561070791-2526d30994b5?w=500&h=300&fit=crop",
6
+ "tag": "Design",
7
+ "color": "#1e3a8a"
8
+ },
9
+ {
10
+ "title": "Portfolio Project 2",
11
+ "description": "Enterprise web application",
12
+ "image": "https://images.unsplash.com/photo-1517694712202-14dd9538aa97?w=500&h=300&fit=crop",
13
+ "tag": "Development",
14
+ "color": "#064e3b"
15
+ },
16
+ {
17
+ "title": "Portfolio Project 3",
18
+ "description": "Mobile app with 10M+ downloads",
19
+ "image": "https://images.unsplash.com/photo-1560807707-38cc612d91b3?w=500&h=300&fit=crop",
20
+ "tag": "Mobile",
21
+ "color": "#5b1f39"
22
+ },
23
+ {
24
+ "title": "Portfolio Project 4",
25
+ "description": "Real-time data visualization",
26
+ "image": "https://images.unsplash.com/photo-1516321295223-4f2d3f2cf1e1?w=500&h=300&fit=crop",
27
+ "tag": "Analytics",
28
+ "color": "#78350f"
29
+ },
30
+ {
31
+ "title": "E-Commerce Platform",
32
+ "description": "Full-stack shopping experience",
33
+ "image": "https://images.unsplash.com/photo-1460925895917-adf4e565e6c1?w=500&h=300&fit=crop",
34
+ "tag": "Commerce",
35
+ "color": "#4c0519"
36
+ }
37
+ ]
package/dist/index.js ADDED
@@ -0,0 +1,310 @@
1
+ import { jsx as c, jsxs as w } from "react/jsx-runtime";
2
+ import { useRef as d, useState as tt, useCallback as v, useEffect as z, useMemo as rt } from "react";
3
+ const et = {
4
+ name: "dark",
5
+ background: "transparent",
6
+ cardBg: "rgba(10, 15, 30, 0.85)",
7
+ cardBorder: "rgba(0, 245, 255, 0.2)",
8
+ cardText: "#f1f5f9",
9
+ cardSubtext: "#94a3b8",
10
+ focusGlow: "0 0 40px rgba(0, 245, 255, 0.5), 0 0 80px rgba(0, 245, 255, 0.2)",
11
+ navBg: "rgba(0, 245, 255, 0.1)",
12
+ navText: "#00f5ff",
13
+ indicatorActive: "#00f5ff",
14
+ indicatorInactive: "rgba(0, 245, 255, 0.25)"
15
+ }, nt = {
16
+ name: "light",
17
+ background: "transparent",
18
+ cardBg: "rgba(255, 255, 255, 0.92)",
19
+ cardBorder: "rgba(124, 58, 237, 0.25)",
20
+ cardText: "#1e293b",
21
+ cardSubtext: "#64748b",
22
+ focusGlow: "0 0 40px rgba(124, 58, 237, 0.35), 0 8px 32px rgba(0,0,0,0.15)",
23
+ navBg: "rgba(124, 58, 237, 0.1)",
24
+ navText: "#7c3aed",
25
+ indicatorActive: "#7c3aed",
26
+ indicatorInactive: "rgba(124, 58, 237, 0.2)"
27
+ }, at = {
28
+ name: "neon",
29
+ background: "transparent",
30
+ cardBg: "rgba(5, 0, 20, 0.9)",
31
+ cardBorder: "rgba(255, 0, 255, 0.4)",
32
+ cardText: "#fff",
33
+ cardSubtext: "#f0abfc",
34
+ focusGlow: "0 0 50px rgba(255,0,255,0.6), 0 0 100px rgba(0,255,255,0.3)",
35
+ navBg: "rgba(255, 0, 255, 0.1)",
36
+ navText: "#ff00ff",
37
+ indicatorActive: "#ff00ff",
38
+ indicatorInactive: "rgba(255,0,255,0.2)"
39
+ }, ot = {
40
+ name: "glass",
41
+ background: "transparent",
42
+ cardBg: "rgba(255, 255, 255, 0.08)",
43
+ cardBorder: "rgba(255, 255, 255, 0.2)",
44
+ cardText: "#ffffff",
45
+ cardSubtext: "rgba(255,255,255,0.65)",
46
+ focusGlow: "0 8px 32px rgba(255,255,255,0.15), inset 0 1px 0 rgba(255,255,255,0.3)",
47
+ navBg: "rgba(255,255,255,0.1)",
48
+ navText: "#ffffff",
49
+ indicatorActive: "#ffffff",
50
+ indicatorInactive: "rgba(255,255,255,0.3)"
51
+ }, W = {
52
+ dark: et,
53
+ light: nt,
54
+ neon: at,
55
+ glass: ot
56
+ }, j = 220, H = 300, X = 155, it = 55, Y = 0.8, ct = 0.64, st = 0.72, dt = 0.4, lt = 3.4;
57
+ function bt({
58
+ data: e,
59
+ autoRotate: s = !0,
60
+ rotationSpeed: l = 2800,
61
+ friction: n = 0.88,
62
+ focusGlow: h = !0,
63
+ theme: q = "dark",
64
+ perspective: F = 1100,
65
+ onFocusChange: f,
66
+ className: U
67
+ }) {
68
+ const o = e.length, Z = W[q] || W.dark, p = d(0), g = d(0), b = d(0), m = d(0), u = d(!1), G = d(0), L = d(0), k = d(null), S = d(!1), [T, I] = tt(0), R = v(() => {
69
+ if (u.current) return;
70
+ const t = p.current, a = g.current, i = a - t;
71
+ b.current = b.current * n + i * (1 - n) * 0.28;
72
+ const r = t + b.current;
73
+ if (p.current = r, I(r), Math.abs(i) < 8e-4 && Math.abs(b.current) < 8e-4) {
74
+ p.current = a, I(a);
75
+ return;
76
+ }
77
+ m.current = requestAnimationFrame(R);
78
+ }, [n]), y = v(() => {
79
+ cancelAnimationFrame(m.current), m.current = requestAnimationFrame(R);
80
+ }, [R]), x = v((t) => {
81
+ g.current += t, b.current = 0, y();
82
+ const a = (Math.round(g.current) % o + o) % o;
83
+ f == null || f(a);
84
+ }, [o, y, f]);
85
+ z(() => {
86
+ if (!s) return;
87
+ const t = () => {
88
+ k.current = setTimeout(() => {
89
+ !u.current && !S.current && x(1), t();
90
+ }, l);
91
+ };
92
+ return t(), () => {
93
+ k.current && clearTimeout(k.current);
94
+ };
95
+ }, [s, l, x]);
96
+ const K = v((t) => {
97
+ u.current = !0, G.current = t.clientX, L.current = p.current, b.current = 0, cancelAnimationFrame(m.current), t.target.setPointerCapture(t.pointerId);
98
+ }, []), N = v((t) => {
99
+ if (!u.current) return;
100
+ const i = -((t.clientX - G.current) / X), r = L.current + i;
101
+ p.current = r, g.current = r, I(r);
102
+ }, []), V = v((t) => {
103
+ if (!u.current) return;
104
+ u.current = !1, g.current = Math.round(p.current), y();
105
+ const a = (Math.round(g.current) % o + o) % o;
106
+ f == null || f(a);
107
+ }, [o, y, f]);
108
+ z(() => {
109
+ const t = (a) => {
110
+ a.key === "ArrowLeft" && x(-1), a.key === "ArrowRight" && x(1);
111
+ };
112
+ return window.addEventListener("keydown", t), () => window.removeEventListener("keydown", t);
113
+ }, [x]), z(() => () => cancelAnimationFrame(m.current), []);
114
+ const J = (Math.round(T) % o + o) % o, Q = rt(() => e.map((t, a) => {
115
+ let i = a - T;
116
+ i = i - Math.round(i / o) * o;
117
+ const r = Math.abs(i), M = i >= 0 ? 1 : -1, B = r < lt, E = M * Math.min(it + r * 8, 85), $ = i * X, D = Math.max(0, 100 - r * 70), A = r < 0.05 ? 1 : r < 1.5 ? 1 - (1 - Y) * r : Math.max(ct, Y - (r - 1) * 0.1), C = r < 0.05 ? 1 : r < 1.5 ? 1 - (1 - st) * r : Math.max(0, dt - (r - 2) * 0.25), _ = Math.round(100 - r * 20);
118
+ return { slide: t, i: a, visible: B, rotY: r < 0.05 ? 0 : E, translateX: $, translateZ: D, scale: A, opacity: C, zIndex: _, dist: i };
119
+ }), [e, T, o]);
120
+ return /* @__PURE__ */ c(
121
+ "div",
122
+ {
123
+ className: U,
124
+ style: {
125
+ position: "relative",
126
+ width: "100%",
127
+ height: "100%",
128
+ cursor: u.current ? "grabbing" : "grab",
129
+ userSelect: "none",
130
+ outline: "none",
131
+ overflow: "hidden"
132
+ },
133
+ onPointerDown: K,
134
+ onPointerMove: N,
135
+ onPointerUp: V,
136
+ onMouseEnter: () => {
137
+ S.current = !0;
138
+ },
139
+ onMouseLeave: () => {
140
+ S.current = !1;
141
+ },
142
+ tabIndex: 0,
143
+ role: "region",
144
+ "aria-label": "Coverflow Carousel",
145
+ children: /* @__PURE__ */ c("div", { style: {
146
+ position: "absolute",
147
+ inset: 0,
148
+ perspective: `${F}px`,
149
+ perspectiveOrigin: "50% 50%",
150
+ display: "flex",
151
+ alignItems: "center",
152
+ justifyContent: "center"
153
+ }, children: /* @__PURE__ */ c("div", { style: { position: "relative", width: 0, height: 0, transformStyle: "preserve-3d" }, children: Q.map(({ slide: t, i: a, visible: i, rotY: r, translateX: M, translateZ: B, scale: E, opacity: $, zIndex: D, dist: A }) => {
154
+ if (!i) return null;
155
+ const C = a === J, _ = Math.abs(A);
156
+ return /* @__PURE__ */ c(
157
+ "div",
158
+ {
159
+ style: {
160
+ position: "absolute",
161
+ width: `${j}px`,
162
+ height: `${H}px`,
163
+ left: `${-j / 2}px`,
164
+ top: `${-H / 2}px`,
165
+ transform: `translateX(${M}px) translateZ(${B}px) rotateY(${r}deg) scale(${E})`,
166
+ opacity: $,
167
+ zIndex: D,
168
+ transition: u.current ? "none" : "transform 0.55s cubic-bezier(0.25,0.46,0.45,0.94), opacity 0.5s ease",
169
+ willChange: "transform, opacity",
170
+ cursor: "pointer"
171
+ },
172
+ onClick: () => {
173
+ var P, O;
174
+ _ > 0.3 ? x(Math.round(A)) : ((P = t.onClick) == null || P.call(t), (O = t.imageClick) == null || O.call(t));
175
+ },
176
+ children: /* @__PURE__ */ c(
177
+ ft,
178
+ {
179
+ slide: t,
180
+ isFocused: C,
181
+ focusGlow: h,
182
+ theme: Z
183
+ }
184
+ )
185
+ },
186
+ a
187
+ );
188
+ }) }) })
189
+ }
190
+ );
191
+ }
192
+ function ft({ slide: e, isFocused: s, focusGlow: l, theme: n }) {
193
+ const h = s && l ? n.focusGlow : void 0;
194
+ return /* @__PURE__ */ w("div", { style: {
195
+ width: "100%",
196
+ height: "100%",
197
+ borderRadius: "18px",
198
+ overflow: "hidden",
199
+ position: "relative",
200
+ background: n.cardBg,
201
+ border: `1.5px solid ${s ? n.indicatorActive : n.cardBorder}`,
202
+ boxShadow: h || (s ? "0 24px 64px rgba(0,0,0,0.65), 0 8px 24px rgba(0,0,0,0.4)" : "0 8px 32px rgba(0,0,0,0.4)"),
203
+ backdropFilter: "blur(12px)",
204
+ WebkitBackdropFilter: "blur(12px)",
205
+ transition: "border-color 0.4s, box-shadow 0.4s",
206
+ display: "flex",
207
+ flexDirection: "column"
208
+ }, children: [
209
+ /* @__PURE__ */ w("div", { style: { flex: "0 0 65%", position: "relative", overflow: "hidden" }, children: [
210
+ e.image ? /* @__PURE__ */ c(
211
+ "img",
212
+ {
213
+ src: e.image,
214
+ alt: e.title,
215
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
216
+ }
217
+ ) : /* @__PURE__ */ c(ut, { title: e.title, color: e.color, accent: n.indicatorActive }),
218
+ e.tag && /* @__PURE__ */ c("span", { style: {
219
+ position: "absolute",
220
+ top: 10,
221
+ right: 10,
222
+ background: n.indicatorActive,
223
+ color: "#000",
224
+ fontSize: "9px",
225
+ fontWeight: 800,
226
+ letterSpacing: "0.08em",
227
+ padding: "3px 9px",
228
+ borderRadius: "100px",
229
+ fontFamily: "var(--font-display)",
230
+ textTransform: "uppercase"
231
+ }, children: e.tag }),
232
+ /* @__PURE__ */ c("div", { style: {
233
+ position: "absolute",
234
+ bottom: 0,
235
+ left: 0,
236
+ right: 0,
237
+ height: "50%",
238
+ background: `linear-gradient(to bottom, transparent, ${n.cardBg})`
239
+ } })
240
+ ] }),
241
+ /* @__PURE__ */ w("div", { style: {
242
+ flex: 1,
243
+ padding: "12px 16px 16px",
244
+ display: "flex",
245
+ flexDirection: "column",
246
+ justifyContent: "center",
247
+ gap: "5px"
248
+ }, children: [
249
+ /* @__PURE__ */ c("div", { style: {
250
+ fontFamily: "var(--font-display)",
251
+ fontWeight: 800,
252
+ fontSize: "15px",
253
+ color: n.cardText,
254
+ lineHeight: 1.25,
255
+ letterSpacing: "-0.02em"
256
+ }, children: e.title }),
257
+ e.description && /* @__PURE__ */ c("div", { style: {
258
+ fontFamily: "var(--font-body)",
259
+ fontSize: "11px",
260
+ color: n.cardSubtext,
261
+ lineHeight: 1.5,
262
+ fontWeight: 300
263
+ }, children: e.description }),
264
+ s && /* @__PURE__ */ c("div", { style: {
265
+ marginTop: "6px",
266
+ padding: "7px 16px",
267
+ borderRadius: "100px",
268
+ background: n.indicatorActive,
269
+ color: "#000",
270
+ fontSize: "10px",
271
+ fontWeight: 800,
272
+ fontFamily: "var(--font-display)",
273
+ letterSpacing: "0.05em",
274
+ textTransform: "uppercase",
275
+ cursor: "pointer",
276
+ alignSelf: "flex-start"
277
+ }, children: "Learn More →" })
278
+ ] })
279
+ ] });
280
+ }
281
+ function ut({ title: e, color: s, accent: l }) {
282
+ const n = ["#020d2e", "#1e1b4b", "#0c1445", "#1a0533", "#0d2137", "#1f1235", "#0a1400", "#1a1000", "#001820", "#160028"], h = s || n[e.charCodeAt(0) % n.length];
283
+ return /* @__PURE__ */ w("div", { style: {
284
+ width: "100%",
285
+ height: "100%",
286
+ background: `linear-gradient(145deg, ${h} 0%, ${l}22 100%)`,
287
+ display: "flex",
288
+ alignItems: "center",
289
+ justifyContent: "center",
290
+ position: "relative"
291
+ }, children: [
292
+ /* @__PURE__ */ c("div", { style: {
293
+ position: "absolute",
294
+ inset: 0,
295
+ background: `radial-gradient(circle at 60% 40%, ${l}38, transparent 65%)`
296
+ } }),
297
+ /* @__PURE__ */ c("span", { style: {
298
+ fontFamily: "var(--font-display)",
299
+ fontWeight: 900,
300
+ fontSize: "52px",
301
+ color: l,
302
+ opacity: 0.88,
303
+ position: "relative",
304
+ zIndex: 1
305
+ }, children: e.slice(0, 2).toUpperCase() })
306
+ ] });
307
+ }
308
+ export {
309
+ bt as AwesomeRingCarousel
310
+ };
@@ -0,0 +1 @@
1
+ (function(l,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],e):(l=typeof globalThis<"u"?globalThis:l||self,e(l.AwesomeRingCarousel={},l.jsxRuntime,l.React))})(this,(function(l,e,n){"use strict";const j={dark:{name:"dark",background:"transparent",cardBg:"rgba(10, 15, 30, 0.85)",cardBorder:"rgba(0, 245, 255, 0.2)",cardText:"#f1f5f9",cardSubtext:"#94a3b8",focusGlow:"0 0 40px rgba(0, 245, 255, 0.5), 0 0 80px rgba(0, 245, 255, 0.2)",navBg:"rgba(0, 245, 255, 0.1)",navText:"#00f5ff",indicatorActive:"#00f5ff",indicatorInactive:"rgba(0, 245, 255, 0.25)"},light:{name:"light",background:"transparent",cardBg:"rgba(255, 255, 255, 0.92)",cardBorder:"rgba(124, 58, 237, 0.25)",cardText:"#1e293b",cardSubtext:"#64748b",focusGlow:"0 0 40px rgba(124, 58, 237, 0.35), 0 8px 32px rgba(0,0,0,0.15)",navBg:"rgba(124, 58, 237, 0.1)",navText:"#7c3aed",indicatorActive:"#7c3aed",indicatorInactive:"rgba(124, 58, 237, 0.2)"},neon:{name:"neon",background:"transparent",cardBg:"rgba(5, 0, 20, 0.9)",cardBorder:"rgba(255, 0, 255, 0.4)",cardText:"#fff",cardSubtext:"#f0abfc",focusGlow:"0 0 50px rgba(255,0,255,0.6), 0 0 100px rgba(0,255,255,0.3)",navBg:"rgba(255, 0, 255, 0.1)",navText:"#ff00ff",indicatorActive:"#ff00ff",indicatorInactive:"rgba(255,0,255,0.2)"},glass:{name:"glass",background:"transparent",cardBg:"rgba(255, 255, 255, 0.08)",cardBorder:"rgba(255, 255, 255, 0.2)",cardText:"#ffffff",cardSubtext:"rgba(255,255,255,0.65)",focusGlow:"0 8px 32px rgba(255,255,255,0.15), inset 0 1px 0 rgba(255,255,255,0.3)",navBg:"rgba(255,255,255,0.1)",navText:"#ffffff",indicatorActive:"#ffffff",indicatorInactive:"rgba(255,255,255,0.3)"}},_=220,P=300,z=155,X=55,G=.8,Y=.64,q=.72,F=.4,U=3.4;function Z({data:o,autoRotate:d=!0,rotationSpeed:f=2800,friction:a=.88,focusGlow:v=!0,theme:V="dark",perspective:J=1100,onFocusChange:u,className:Q}){const c=o.length,tt=j[V]||j.dark,g=n.useRef(0),b=n.useRef(0),x=n.useRef(0),y=n.useRef(0),p=n.useRef(!1),L=n.useRef(0),O=n.useRef(0),A=n.useRef(null),T=n.useRef(!1),[w,S]=n.useState(0),I=n.useCallback(()=>{if(p.current)return;const t=g.current,i=b.current,s=i-t;x.current=x.current*a+s*(1-a)*.28;const r=t+x.current;if(g.current=r,S(r),Math.abs(s)<8e-4&&Math.abs(x.current)<8e-4){g.current=i,S(i);return}y.current=requestAnimationFrame(I)},[a]),m=n.useCallback(()=>{cancelAnimationFrame(y.current),y.current=requestAnimationFrame(I)},[I]),h=n.useCallback(t=>{b.current+=t,x.current=0,m();const i=(Math.round(b.current)%c+c)%c;u==null||u(i)},[c,m,u]);n.useEffect(()=>{if(!d)return;const t=()=>{A.current=setTimeout(()=>{!p.current&&!T.current&&h(1),t()},f)};return t(),()=>{A.current&&clearTimeout(A.current)}},[d,f,h]);const et=n.useCallback(t=>{p.current=!0,L.current=t.clientX,O.current=g.current,x.current=0,cancelAnimationFrame(y.current),t.target.setPointerCapture(t.pointerId)},[]),rt=n.useCallback(t=>{if(!p.current)return;const s=-((t.clientX-L.current)/z),r=O.current+s;g.current=r,b.current=r,S(r)},[]),nt=n.useCallback(t=>{if(!p.current)return;p.current=!1,b.current=Math.round(g.current),m();const i=(Math.round(b.current)%c+c)%c;u==null||u(i)},[c,m,u]);n.useEffect(()=>{const t=i=>{i.key==="ArrowLeft"&&h(-1),i.key==="ArrowRight"&&h(1)};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[h]),n.useEffect(()=>()=>cancelAnimationFrame(y.current),[]);const ot=(Math.round(w)%c+c)%c,at=n.useMemo(()=>o.map((t,i)=>{let s=i-w;s=s-Math.round(s/c)*c;const r=Math.abs(s),M=s>=0?1:-1,C=r<U,B=M*Math.min(X+r*8,85),E=s*z,R=Math.max(0,100-r*70),k=r<.05?1:r<1.5?1-(1-G)*r:Math.max(Y,G-(r-1)*.1),$=r<.05?1:r<1.5?1-(1-q)*r:Math.max(0,F-(r-2)*.25),D=Math.round(100-r*20);return{slide:t,i,visible:C,rotY:r<.05?0:B,translateX:E,translateZ:R,scale:k,opacity:$,zIndex:D,dist:s}}),[o,w,c]);return e.jsx("div",{className:Q,style:{position:"relative",width:"100%",height:"100%",cursor:p.current?"grabbing":"grab",userSelect:"none",outline:"none",overflow:"hidden"},onPointerDown:et,onPointerMove:rt,onPointerUp:nt,onMouseEnter:()=>{T.current=!0},onMouseLeave:()=>{T.current=!1},tabIndex:0,role:"region","aria-label":"Coverflow Carousel",children:e.jsx("div",{style:{position:"absolute",inset:0,perspective:`${J}px`,perspectiveOrigin:"50% 50%",display:"flex",alignItems:"center",justifyContent:"center"},children:e.jsx("div",{style:{position:"relative",width:0,height:0,transformStyle:"preserve-3d"},children:at.map(({slide:t,i,visible:s,rotY:r,translateX:M,translateZ:C,scale:B,opacity:E,zIndex:R,dist:k})=>{if(!s)return null;const $=i===ot,D=Math.abs(k);return e.jsx("div",{style:{position:"absolute",width:`${_}px`,height:`${P}px`,left:`${-_/2}px`,top:`${-P/2}px`,transform:`translateX(${M}px) translateZ(${C}px) rotateY(${r}deg) scale(${B})`,opacity:E,zIndex:R,transition:p.current?"none":"transform 0.55s cubic-bezier(0.25,0.46,0.45,0.94), opacity 0.5s ease",willChange:"transform, opacity",cursor:"pointer"},onClick:()=>{var W,H;D>.3?h(Math.round(k)):((W=t.onClick)==null||W.call(t),(H=t.imageClick)==null||H.call(t))},children:e.jsx(K,{slide:t,isFocused:$,focusGlow:v,theme:tt})},i)})})})})}function K({slide:o,isFocused:d,focusGlow:f,theme:a}){const v=d&&f?a.focusGlow:void 0;return e.jsxs("div",{style:{width:"100%",height:"100%",borderRadius:"18px",overflow:"hidden",position:"relative",background:a.cardBg,border:`1.5px solid ${d?a.indicatorActive:a.cardBorder}`,boxShadow:v||(d?"0 24px 64px rgba(0,0,0,0.65), 0 8px 24px rgba(0,0,0,0.4)":"0 8px 32px rgba(0,0,0,0.4)"),backdropFilter:"blur(12px)",WebkitBackdropFilter:"blur(12px)",transition:"border-color 0.4s, box-shadow 0.4s",display:"flex",flexDirection:"column"},children:[e.jsxs("div",{style:{flex:"0 0 65%",position:"relative",overflow:"hidden"},children:[o.image?e.jsx("img",{src:o.image,alt:o.title,style:{width:"100%",height:"100%",objectFit:"cover",display:"block"}}):e.jsx(N,{title:o.title,color:o.color,accent:a.indicatorActive}),o.tag&&e.jsx("span",{style:{position:"absolute",top:10,right:10,background:a.indicatorActive,color:"#000",fontSize:"9px",fontWeight:800,letterSpacing:"0.08em",padding:"3px 9px",borderRadius:"100px",fontFamily:"var(--font-display)",textTransform:"uppercase"},children:o.tag}),e.jsx("div",{style:{position:"absolute",bottom:0,left:0,right:0,height:"50%",background:`linear-gradient(to bottom, transparent, ${a.cardBg})`}})]}),e.jsxs("div",{style:{flex:1,padding:"12px 16px 16px",display:"flex",flexDirection:"column",justifyContent:"center",gap:"5px"},children:[e.jsx("div",{style:{fontFamily:"var(--font-display)",fontWeight:800,fontSize:"15px",color:a.cardText,lineHeight:1.25,letterSpacing:"-0.02em"},children:o.title}),o.description&&e.jsx("div",{style:{fontFamily:"var(--font-body)",fontSize:"11px",color:a.cardSubtext,lineHeight:1.5,fontWeight:300},children:o.description}),d&&e.jsx("div",{style:{marginTop:"6px",padding:"7px 16px",borderRadius:"100px",background:a.indicatorActive,color:"#000",fontSize:"10px",fontWeight:800,fontFamily:"var(--font-display)",letterSpacing:"0.05em",textTransform:"uppercase",cursor:"pointer",alignSelf:"flex-start"},children:"Learn More →"})]})]})}function N({title:o,color:d,accent:f}){const a=["#020d2e","#1e1b4b","#0c1445","#1a0533","#0d2137","#1f1235","#0a1400","#1a1000","#001820","#160028"],v=d||a[o.charCodeAt(0)%a.length];return e.jsxs("div",{style:{width:"100%",height:"100%",background:`linear-gradient(145deg, ${v} 0%, ${f}22 100%)`,display:"flex",alignItems:"center",justifyContent:"center",position:"relative"},children:[e.jsx("div",{style:{position:"absolute",inset:0,background:`radial-gradient(circle at 60% 40%, ${f}38, transparent 65%)`}}),e.jsx("span",{style:{fontFamily:"var(--font-display)",fontWeight:900,fontSize:"52px",color:f,opacity:.88,position:"relative",zIndex:1},children:o.slice(0,2).toUpperCase()})]})}l.AwesomeRingCarousel=Z,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}));
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ :root{--awesome-carousel-dark-bg: #030712;--awesome-carousel-light-text: #f1f5f9;--awesome-carousel-secondary-text: #94a3b8;--awesome-carousel-cyan: #00f5ff;--awesome-carousel-violet: #7c3aed;--awesome-carousel-amber: #f59e0b;--awesome-carousel-card-dark-bg: rgba(10, 15, 30, .85);--awesome-carousel-card-dark-border: rgba(0, 245, 255, .2);--awesome-carousel-glow-dark: 0 0 40px rgba(0, 245, 255, .5), 0 0 80px rgba(0, 245, 255, .2);--awesome-carousel-nav-dark-bg: rgba(0, 245, 255, .1);--awesome-carousel-nav-dark-text: #00f5ff;--awesome-carousel-indicator-active-dark: #00f5ff;--awesome-carousel-indicator-inactive-dark: rgba(0, 245, 255, .25)}@media(prefers-color-scheme:light){:root{--awesome-carousel-dark-bg: #f8fafc;--awesome-carousel-light-text: #1e293b;--awesome-carousel-secondary-text: #64748b}}.awesome-carousel-root *,.awesome-carousel-root *:before,.awesome-carousel-root *:after{box-sizing:border-box;margin:0;padding:0}.awesome-carousel-root{position:relative;width:100%;height:100%;font-family:DM Sans,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.awesome-ring-carousel{position:relative;width:100%;height:100%;overflow:hidden;background:transparent;cursor:grab;-webkit-user-select:none;user-select:none;outline:none}.awesome-ring-carousel:active{cursor:grabbing}.awesome-ring-carousel[role=region]{outline:none}.awesome-ring-carousel:focus-visible{outline:2px solid var(--awesome-carousel-cyan);outline-offset:4px}.awesome-carousel-stage{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;perspective:1100px;perspective-origin:50% 50%;transform-style:preserve-3d}.awesome-carousel-stage__inner{position:relative;width:0;height:0;transform-style:preserve-3d}.awesome-carousel-card{position:absolute;width:220px;height:300px;border-radius:18px;overflow:hidden;will-change:transform,opacity;transform-style:preserve-3d;backface-visibility:hidden}.awesome-carousel-card--transition{transition:transform .55s cubic-bezier(.25,.46,.45,.94),opacity .5s ease}.awesome-carousel-card--no-transition{transition:none}.awesome-carousel-card:hover{z-index:1000!important}.awesome-carousel-card__content{width:100%;height:100%;display:flex;flex-direction:column;border-radius:18px;overflow:hidden;position:relative;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);transition:border-color .4s,box-shadow .4s}.awesome-carousel-card__content--dark{background:var(--awesome-carousel-card-dark-bg);border:1.5px solid var(--awesome-carousel-card-dark-border);color:var(--awesome-carousel-light-text);box-shadow:0 8px 32px #0006}.awesome-carousel-card__content--dark.focused{border-color:var(--awesome-carousel-indicator-active-dark);box-shadow:var(--awesome-carousel-glow-dark)}.awesome-carousel-card__content--light{background:#ffffffeb;border:1.5px solid rgba(124,58,237,.25);color:#1e293b;box-shadow:0 8px 32px #00000026}.awesome-carousel-card__content--light.focused{border-color:var(--awesome-carousel-violet);box-shadow:0 0 40px #7c3aed59,0 8px 32px #00000026}.awesome-carousel-card__content--neon{background:#050014e6;border:1.5px solid rgba(255,0,255,.4);color:#fff;box-shadow:0 8px 32px #0006}.awesome-carousel-card__content--neon.focused{border-color:#f0f;box-shadow:0 0 50px #f0f9,0 0 100px #00ffff4d}.awesome-carousel-card__content--glass{background:#ffffff14;border:1.5px solid rgba(255,255,255,.2);color:#fff;box-shadow:0 8px 32px #ffffff26,inset 0 1px #ffffff4d}.awesome-carousel-card__content--glass.focused{border-color:#fff;box-shadow:0 8px 32px #fff3,inset 0 1px #fff6}.awesome-carousel-card__image-area{flex:0 0 65%;position:relative;overflow:hidden;background:#0000004d}.awesome-carousel-card__image{width:100%;height:100%;object-fit:cover;display:block;transition:transform .3s ease}.awesome-carousel-card:hover .awesome-carousel-card__image{transform:scale(1.05)}.awesome-carousel-card__image-placeholder{width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#7c3aed33,#00f5ff33);color:#ffffff4d;font-size:2em}.awesome-carousel-card__text-area{flex:1;padding:16px 14px;display:flex;flex-direction:column;gap:6px;overflow:hidden}.awesome-carousel-card__title{font-size:14px;font-weight:600;line-height:1.4;text-transform:uppercase;letter-spacing:.5px;margin:0}.awesome-carousel-card__description{font-size:12px;line-height:1.3;margin:0;opacity:.8}.awesome-carousel-card__tag{display:inline-block;padding:4px 8px;border-radius:4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.3px;width:fit-content;margin-top:auto}.awesome-carousel-card__content--dark .awesome-carousel-card__tag{background:var(--awesome-carousel-nav-dark-bg);color:var(--awesome-carousel-nav-dark-text)}.awesome-carousel-card__content--light .awesome-carousel-card__tag{background:#7c3aed1a;color:var(--awesome-carousel-violet)}.awesome-carousel-card__content--neon .awesome-carousel-card__tag{background:#ff00ff26;color:#f0f}.awesome-carousel-card__content--glass .awesome-carousel-card__tag{background:#ffffff26;color:#fff}.sphere-item{transition:transform .2s ease}.sphere-item--lift:hover{transform:translate(-50%,-52%)!important;filter:brightness(1.1)}.sphere-item--zoom:hover{transform:translate(-50%,-50%) scale(1.12)!important}.sphere-item--tilt:hover{transform:translate(-50%,-50%) rotateY(-5deg) rotateX(5deg)!important}.sphere-item:focus{outline:none}.sphere-item:focus-visible{outline:2px solid var(--awesome-carousel-cyan);outline-offset:4px;border-radius:16px}input[type=range]{-webkit-appearance:none;width:100%;height:4px;border-radius:2px;background:#ffffff1a;outline:none;cursor:pointer}input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:14px;height:14px;border-radius:50%;background:var(--awesome-carousel-cyan);cursor:pointer;transition:box-shadow .2s ease}input[type=range]::-webkit-slider-thumb:hover{box-shadow:0 0 10px var(--awesome-carousel-cyan)}input[type=range]::-moz-range-thumb{width:14px;height:14px;border-radius:50%;background:var(--awesome-carousel-cyan);cursor:pointer;border:none;transition:box-shadow .2s ease}input[type=range]::-moz-range-thumb:hover{box-shadow:0 0 10px var(--awesome-carousel-cyan)}input[type=range]::-moz-range-track{background:transparent;border:none}input[type=range]::-moz-range-progress{background:var(--awesome-carousel-cyan);height:4px;border-radius:2px}@keyframes starfield{0%{transform:translateY(0)}to{transform:translateY(-50%)}}@keyframes pulseGlow{0%,to{opacity:.4;transform:scale(1)}50%{opacity:.8;transform:scale(1.05)}}@keyframes fadeUp{0%{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)}}@keyframes rotate360{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes cardEntrance{0%{opacity:0;transform:scale(.9)}to{opacity:1;transform:scale(1)}}@keyframes slideIn{0%{transform:translate(-20px);opacity:0}to{transform:translate(0);opacity:1}}.awesome-carousel-nav{display:flex;gap:10px;justify-content:center;padding:20px;background:transparent}.awesome-carousel-nav__button{padding:8px 16px;border:1px solid currentColor;border-radius:6px;background:transparent;color:inherit;cursor:pointer;font-size:14px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;transition:all .3s ease;outline:none}.awesome-carousel-nav__button:hover{transform:translateY(-2px);box-shadow:0 4px 12px #0003}.awesome-carousel-nav__button:active{transform:translateY(0)}.awesome-carousel-nav__button:focus-visible{outline:2px solid var(--awesome-carousel-cyan);outline-offset:2px}.awesome-carousel-nav--dark .awesome-carousel-nav__button{color:var(--awesome-carousel-nav-dark-text);border-color:var(--awesome-carousel-nav-dark-text)}.awesome-carousel-nav--dark .awesome-carousel-nav__button:hover{background:var(--awesome-carousel-nav-dark-bg)}.awesome-carousel-nav--light .awesome-carousel-nav__button{color:var(--awesome-carousel-violet);border-color:var(--awesome-carousel-violet)}.awesome-carousel-nav--light .awesome-carousel-nav__button:hover{background:#7c3aed1a}.awesome-carousel-indicators{display:flex;gap:8px;justify-content:center;padding:16px;background:transparent}.awesome-carousel-indicator{width:8px;height:8px;border-radius:50%;cursor:pointer;transition:all .3s ease;border:none;padding:0}.awesome-carousel-indicator:hover{transform:scale(1.3)}.awesome-carousel-indicator:focus-visible{outline:2px solid var(--awesome-carousel-cyan);outline-offset:2px}.awesome-carousel-indicators--dark .awesome-carousel-indicator{background:var(--awesome-carousel-indicator-inactive-dark)}.awesome-carousel-indicators--dark .awesome-carousel-indicator.active{background:var(--awesome-carousel-indicator-active-dark);box-shadow:0 0 10px var(--awesome-carousel-indicator-active-dark)}.awesome-carousel-indicators--light .awesome-carousel-indicator{background:#7c3aed33}.awesome-carousel-indicators--light .awesome-carousel-indicator.active{background:var(--awesome-carousel-violet);box-shadow:0 0 10px #7c3aed80}.awesome-carousel-indicators--neon .awesome-carousel-indicator{background:#f0f3}.awesome-carousel-indicators--neon .awesome-carousel-indicator.active{background:#f0f;box-shadow:0 0 10px #f0f}.awesome-carousel-indicators--glass .awesome-carousel-indicator{background:#ffffff4d}.awesome-carousel-indicators--glass .awesome-carousel-indicator.active{background:#fff;box-shadow:0 0 10px #fff9}.awesome-carousel-fade-enter{animation:fadeUp .3s ease-out}.awesome-carousel-card-entrance{animation:cardEntrance .4s cubic-bezier(.34,1.56,.64,1)}.awesome-carousel-slide-in{animation:slideIn .3s ease-out}@media(prefers-reduced-motion:reduce){*{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}.awesome-carousel-card--transition{transition:none}}@media(max-width:768px){.awesome-carousel-card{width:180px;height:240px}.awesome-carousel-card__title{font-size:13px}.awesome-carousel-card__description{font-size:11px}.awesome-carousel-nav{padding:15px;gap:8px}.awesome-carousel-nav__button{padding:6px 12px;font-size:12px}.awesome-carousel-indicators{gap:6px;padding:12px}.awesome-carousel-indicator{width:6px;height:6px}}@media(max-width:480px){.awesome-carousel-card{width:150px;height:200px}.awesome-carousel-card__image-area{flex:0 0 60%}.awesome-carousel-card__text-area{padding:12px 10px;gap:4px}.awesome-carousel-card__title{font-size:12px}.awesome-carousel-card__description{font-size:10px}.awesome-carousel-card__tag{font-size:9px;padding:3px 6px}}.awesome-carousel-a11y-skip{position:absolute;left:-10000px;top:auto;width:1px;height:1px;overflow:hidden}.awesome-carousel-a11y-skip:focus{position:static;width:auto;height:auto;padding:10px;background:var(--awesome-carousel-cyan);color:var(--awesome-carousel-dark-bg);outline:2px solid var(--awesome-carousel-cyan)}@media(prefers-contrast:more){.awesome-carousel-card__content--dark{border-color:var(--awesome-carousel-indicator-active-dark)}.awesome-carousel-card__content--light{border-color:var(--awesome-carousel-violet)}.awesome-carousel-indicator{border:1px solid currentColor}}@media(forced-colors:active){.awesome-carousel-card__content,.awesome-carousel-nav__button,.awesome-carousel-indicator{border:1px solid}}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "awesome-ring-carousel",
3
+ "version": "0.1.0",
4
+ "description": "A beautiful, interactive 3D ring carousel component for React with smooth animations, multiple themes, and flexible data handling",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.es.js",
8
+ "types": "dist/index.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.es.js",
16
+ "require": "./dist/index.js"
17
+ },
18
+ "./dist/style.css": "./dist/style.css"
19
+ },
20
+ "scripts": {
21
+ "dev": "vite",
22
+ "build": "tsc -b && vite build",
23
+ "preview": "vite preview",
24
+ "prepublishOnly": "npm run build"
25
+ },
26
+ "keywords": [
27
+ "react",
28
+ "carousel",
29
+ "coverflow",
30
+ "3d",
31
+ "ring",
32
+ "component",
33
+ "interactive",
34
+ "animation",
35
+ "theme",
36
+ "ui"
37
+ ],
38
+ "author": "Pravinkumar Dabade",
39
+ "license": "MIT",
40
+ "peerDependenciesMeta": {
41
+ "react": {
42
+ "optional": false
43
+ },
44
+ "react-dom": {
45
+ "optional": false
46
+ }
47
+ },
48
+ "dependencies": {
49
+ "react": "^19.0.0",
50
+ "react-dom": "^19.0.0"
51
+ },
52
+ "devDependencies": {
53
+ "@types/react": "^19.0.0",
54
+ "@types/react-dom": "^19.0.0",
55
+ "@vitejs/plugin-react": "^4.3.4",
56
+ "typescript": "~5.6.2",
57
+ "vite": "^6.0.5"
58
+ },
59
+ "engines": {
60
+ "node": ">=14.0.0",
61
+ "npm": ">=6.0.0"
62
+ }
63
+ }