wally-ui 1.6.0 → 1.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wally-ui",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "About Where’s Wally? Right here — bringing you ready-to-use Angular components with Wally-UI. Stop searching, start building.",
5
5
  "bin": {
6
6
  "wally": "dist/cli.js"
@@ -44,4 +44,12 @@
44
44
  <priority>0.8</priority>
45
45
  </url>
46
46
 
47
+ <!-- Carousel Documentation -->
48
+ <url>
49
+ <loc>https://wally-ui.com/documentation/components/carousel</loc>
50
+ <lastmod>2025-09-27</lastmod>
51
+ <changefreq>monthly</changefreq>
52
+ <priority>0.8</priority>
53
+ </url>
54
+
47
55
  </urlset>
@@ -21,6 +21,10 @@ export const serverRoutes: ServerRoute[] = [
21
21
  path: 'documentation/components/breadcrumb',
22
22
  renderMode: RenderMode.Prerender,
23
23
  },
24
+ {
25
+ path: 'documentation/components/carousel',
26
+ renderMode: RenderMode.Prerender,
27
+ },
24
28
  {
25
29
  path: 'components',
26
30
  renderMode: RenderMode.Prerender,
@@ -0,0 +1,32 @@
1
+ <div class="w-full flex flex-col items-center gap-4">
2
+ <div class="relative w-96 h-96 overflow-hidden shadow rounded-lg" #carouselContainer>
3
+ <ng-content></ng-content>
4
+ </div>
5
+
6
+ <div class="flex items-center gap-3">
7
+ @for (navigationDot of navigationDotsArray; track $index; let dotIndex = $index) {
8
+ <button
9
+ class="size-2 rounded-full transition-all duration-500 ease-in-out cursor-pointer"
10
+ [ngClass]="{
11
+ 'bg-blue-500 border scale-150': dotIndex === currentVisibleItemIndex(),
12
+ 'bg-white hover:bg-gray-400 hover:scale-125': dotIndex !== currentVisibleItemIndex()
13
+ }"
14
+ (click)="navigateToSpecificItem(dotIndex)"
15
+ [attr.aria-label]="'Navigate to slide ' + (dotIndex + 1)">
16
+ </button>
17
+ }
18
+ </div>
19
+
20
+ <div class="flex gap-4">
21
+ <button class="px-4 py-2 bg-gray-700 text-white rounded hover:bg-gray-600 transition-colors duration-200"
22
+ (click)="navigateToPreviousItem()"
23
+ aria-label="Go to previous slide">
24
+ Previous
25
+ </button>
26
+ <button class="px-4 py-2 bg-gray-700 text-white rounded hover:bg-gray-600 transition-colors duration-200"
27
+ (click)="navigateToNextItem()"
28
+ aria-label="Go to next slide">
29
+ Next
30
+ </button>
31
+ </div>
32
+ </div>
@@ -0,0 +1,257 @@
1
+ import { Component, input, OnInit, signal, ViewChild, ElementRef, AfterViewInit, Renderer2, OnDestroy, HostListener } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ @Component({
5
+ selector: 'wally-carousel',
6
+ imports: [CommonModule],
7
+ templateUrl: './carousel.html',
8
+ })
9
+ export class Carousel implements OnInit, AfterViewInit, OnDestroy {
10
+ @ViewChild('carouselContainer', { static: false }) carouselContainer!: ElementRef;
11
+
12
+ totalItemsCount = signal<number>(0);
13
+ currentVisibleItemIndex = signal<number>(0);
14
+ carouselItemElements: HTMLElement[] = [];
15
+
16
+ // Touch and swipe gesture state
17
+ private gestureState = {
18
+ startPositionX: 0,
19
+ startPositionY: 0,
20
+ currentPositionX: 0,
21
+ currentPositionY: 0,
22
+ isCurrentlyDragging: false,
23
+ gestureStartTime: 0
24
+ };
25
+
26
+ // Configuration constants for touch and swipe behavior
27
+ private readonly MINIMUM_SWIPE_DISTANCE = 50; // minimum pixels to register as swipe
28
+ private readonly MINIMUM_SWIPE_VELOCITY = 0.3; // minimum velocity to register as swipe
29
+ private readonly MAXIMUM_SWIPE_DURATION = 300; // maximum milliseconds for swipe gesture
30
+
31
+ // Event listener cleanup functions
32
+ private eventListenerCleanupFunctions: (() => void)[] = [];
33
+
34
+ get navigationDotsArray() {
35
+ return Array(this.totalItemsCount()).fill(0);
36
+ }
37
+
38
+ constructor(private renderer: Renderer2) { }
39
+
40
+ ngOnInit(): void {
41
+ }
42
+
43
+ ngAfterViewInit(): void {
44
+ if (this.carouselContainer) {
45
+ this.initializeCarouselItems();
46
+ this.setupTouchAndMouseEvents();
47
+ this.setupAccessibilityAttributes();
48
+ }
49
+ }
50
+
51
+ ngOnDestroy(): void {
52
+ // Clean up all event listeners to prevent memory leaks
53
+ this.eventListenerCleanupFunctions.forEach(cleanupFunction => cleanupFunction());
54
+ }
55
+
56
+ private initializeCarouselItems(): void {
57
+ // Get all direct children elements of the carousel container
58
+ this.carouselItemElements = Array.from(this.carouselContainer.nativeElement.children);
59
+ this.totalItemsCount.set(this.carouselItemElements.length);
60
+
61
+ // Apply positioning and styling to each carousel item using Renderer2
62
+ this.carouselItemElements.forEach((itemElement, itemIndex) => {
63
+ this.renderer.setStyle(itemElement, 'position', 'absolute');
64
+ this.renderer.setStyle(itemElement, 'inset', '0');
65
+ this.renderer.setStyle(itemElement, 'transition', 'transform 700ms ease-out');
66
+ this.renderer.setStyle(itemElement, 'display', 'flex');
67
+ this.renderer.setStyle(itemElement, 'align-items', 'center');
68
+ this.renderer.setStyle(itemElement, 'justify-content', 'center');
69
+ this.updateItemElementPosition(itemElement, itemIndex);
70
+ });
71
+ }
72
+
73
+ private setupTouchAndMouseEvents(): void {
74
+ const carouselContainerElement = this.carouselContainer.nativeElement;
75
+
76
+ // Touch events for mobile devices
77
+ const touchStartListener = this.renderer.listen(carouselContainerElement, 'touchstart', (event) => this.handleTouchStart(event));
78
+ const touchMoveListener = this.renderer.listen(carouselContainerElement, 'touchmove', (event) => this.handleTouchMove(event));
79
+ const touchEndListener = this.renderer.listen(carouselContainerElement, 'touchend', () => this.handleTouchEnd());
80
+
81
+ // Mouse events for desktop devices
82
+ const mouseDownListener = this.renderer.listen(carouselContainerElement, 'mousedown', (event) => this.handleMouseDown(event));
83
+ const mouseMoveListener = this.renderer.listen(carouselContainerElement, 'mousemove', (event) => this.handleMouseMove(event));
84
+ const mouseUpListener = this.renderer.listen(carouselContainerElement, 'mouseup', () => this.handleMouseUp());
85
+ const mouseLeaveListener = this.renderer.listen(carouselContainerElement, 'mouseleave', () => this.handleMouseUp());
86
+
87
+ // Store all cleanup functions for proper memory management
88
+ this.eventListenerCleanupFunctions.push(
89
+ touchStartListener,
90
+ touchMoveListener,
91
+ touchEndListener,
92
+ mouseDownListener,
93
+ mouseMoveListener,
94
+ mouseUpListener,
95
+ mouseLeaveListener
96
+ );
97
+
98
+ // Optimize touch performance by restricting to horizontal panning only
99
+ this.renderer.setStyle(carouselContainerElement, 'touch-action', 'pan-x');
100
+ }
101
+
102
+ private setupAccessibilityAttributes(): void {
103
+ const carouselContainerElement = this.carouselContainer.nativeElement;
104
+
105
+ // Set ARIA attributes for screen readers and accessibility
106
+ this.renderer.setAttribute(carouselContainerElement, 'role', 'region');
107
+ this.renderer.setAttribute(carouselContainerElement, 'aria-label', 'Carousel');
108
+ this.renderer.setAttribute(carouselContainerElement, 'tabindex', '0');
109
+ }
110
+
111
+ updateItemElementPosition(carouselItemElement: HTMLElement, itemIndex: number): void {
112
+ const currentVisibleIndex = this.currentVisibleItemIndex();
113
+
114
+ if (itemIndex === currentVisibleIndex) {
115
+ // Position the currently visible item at center (0% translation)
116
+ this.renderer.setStyle(carouselItemElement, 'transform', 'translateX(0)');
117
+ } else if (itemIndex > currentVisibleIndex) {
118
+ // Position items to the right of current item (110% translation to the right)
119
+ this.renderer.setStyle(carouselItemElement, 'transform', 'translateX(110%)');
120
+ } else {
121
+ // Position items to the left of current item (110% translation to the left)
122
+ this.renderer.setStyle(carouselItemElement, 'transform', 'translateX(-110%)');
123
+ }
124
+ }
125
+
126
+ updateAllItemElementPositions(): void {
127
+ this.carouselItemElements.forEach((itemElement, itemIndex) => {
128
+ this.updateItemElementPosition(itemElement, itemIndex);
129
+ });
130
+ }
131
+
132
+ // Touch event handlers for mobile devices
133
+ private handleTouchStart(touchEvent: TouchEvent): void {
134
+ this.initializeGesture(touchEvent.touches[0].clientX, touchEvent.touches[0].clientY);
135
+ }
136
+
137
+ private handleTouchMove(touchEvent: TouchEvent): void {
138
+ if (this.gestureState.isCurrentlyDragging) {
139
+ touchEvent.preventDefault(); // Prevent page scrolling during swipe
140
+ this.updateGesturePosition(touchEvent.touches[0].clientX, touchEvent.touches[0].clientY);
141
+ }
142
+ }
143
+
144
+ private handleTouchEnd(): void {
145
+ this.finalizeGestureAndNavigate();
146
+ }
147
+
148
+ // Mouse event handlers for desktop devices
149
+ private handleMouseDown(mouseEvent: MouseEvent): void {
150
+ this.initializeGesture(mouseEvent.clientX, mouseEvent.clientY);
151
+ }
152
+
153
+ private handleMouseMove(mouseEvent: MouseEvent): void {
154
+ if (this.gestureState.isCurrentlyDragging) {
155
+ mouseEvent.preventDefault(); // Prevent text selection during drag
156
+ this.updateGesturePosition(mouseEvent.clientX, mouseEvent.clientY);
157
+ }
158
+ }
159
+
160
+ private handleMouseUp(): void {
161
+ this.finalizeGestureAndNavigate();
162
+ }
163
+
164
+ // Unified gesture handling methods
165
+ private initializeGesture(xPosition: number, yPosition: number): void {
166
+ this.gestureState = {
167
+ startPositionX: xPosition,
168
+ startPositionY: yPosition,
169
+ currentPositionX: xPosition,
170
+ currentPositionY: yPosition,
171
+ isCurrentlyDragging: true,
172
+ gestureStartTime: Date.now()
173
+ };
174
+ }
175
+
176
+ private updateGesturePosition(xPosition: number, yPosition: number): void {
177
+ if (!this.gestureState.isCurrentlyDragging) return;
178
+
179
+ this.gestureState.currentPositionX = xPosition;
180
+ this.gestureState.currentPositionY = yPosition;
181
+ }
182
+
183
+ private finalizeGestureAndNavigate(): void {
184
+ if (!this.gestureState.isCurrentlyDragging) return;
185
+
186
+ const horizontalDistanceMoved = this.gestureState.currentPositionX - this.gestureState.startPositionX;
187
+ const verticalDistanceMoved = this.gestureState.currentPositionY - this.gestureState.startPositionY;
188
+ const gestureDuration = Date.now() - this.gestureState.gestureStartTime;
189
+ const gestureVelocity = Math.abs(horizontalDistanceMoved) / gestureDuration;
190
+
191
+ // Only process horizontal swipes (ignore vertical movements)
192
+ if (Math.abs(horizontalDistanceMoved) > Math.abs(verticalDistanceMoved)) {
193
+ // Check if gesture meets minimum requirements for navigation
194
+ const isValidSwipeGesture = Math.abs(horizontalDistanceMoved) > this.MINIMUM_SWIPE_DISTANCE ||
195
+ (gestureVelocity > this.MINIMUM_SWIPE_VELOCITY && gestureDuration < this.MAXIMUM_SWIPE_DURATION);
196
+
197
+ if (isValidSwipeGesture) {
198
+ if (horizontalDistanceMoved > 0) {
199
+ this.navigateToPreviousItem(); // Swipe right = go to previous item
200
+ } else {
201
+ this.navigateToNextItem(); // Swipe left = go to next item
202
+ }
203
+ }
204
+ }
205
+
206
+ // Reset gesture state after processing
207
+ this.gestureState.isCurrentlyDragging = false;
208
+ }
209
+
210
+ // Keyboard navigation handler
211
+ @HostListener('keydown', ['$event'])
212
+ handleKeyboardNavigation(keyboardEvent: KeyboardEvent): void {
213
+ switch (keyboardEvent.key) {
214
+ case 'ArrowLeft':
215
+ keyboardEvent.preventDefault();
216
+ this.navigateToPreviousItem();
217
+ break;
218
+ case 'ArrowRight':
219
+ keyboardEvent.preventDefault();
220
+ this.navigateToNextItem();
221
+ break;
222
+ case 'Home':
223
+ keyboardEvent.preventDefault();
224
+ this.navigateToSpecificItem(0);
225
+ break;
226
+ case 'End':
227
+ keyboardEvent.preventDefault();
228
+ this.navigateToSpecificItem(this.totalItemsCount() - 1);
229
+ break;
230
+ }
231
+ }
232
+
233
+ // Circular buffer algorithm implementation for infinite carousel navigation
234
+ calculateNextItemIndex(currentItemIndex: number): number {
235
+ return (currentItemIndex + 1) % this.totalItemsCount();
236
+ }
237
+
238
+ calculatePreviousItemIndex(currentItemIndex: number): number {
239
+ return (currentItemIndex - 1 + this.totalItemsCount()) % this.totalItemsCount();
240
+ }
241
+
242
+ navigateToNextItem(): void {
243
+ this.currentVisibleItemIndex.set(this.calculateNextItemIndex(this.currentVisibleItemIndex()));
244
+ this.updateAllItemElementPositions();
245
+ }
246
+
247
+ navigateToPreviousItem(): void {
248
+ this.currentVisibleItemIndex.set(this.calculatePreviousItemIndex(this.currentVisibleItemIndex()));
249
+ this.updateAllItemElementPositions();
250
+ }
251
+
252
+ navigateToSpecificItem(targetItemIndex: number): void {
253
+ this.currentVisibleItemIndex.set(targetItemIndex);
254
+ this.updateAllItemElementPositions();
255
+ }
256
+ // end test Circular Buffer ---
257
+ }
@@ -0,0 +1,48 @@
1
+ // Carousel documentation code examples
2
+ export const CarouselCodeExamples = {
3
+ // Installation
4
+ installation: `npx wally-ui add carousel`,
5
+
6
+ // Import examples
7
+ import: `import { Carousel } from './components/wally-ui/carousel/carousel';`,
8
+ componentImport: `@Component({
9
+ selector: 'app-example',
10
+ imports: [Carousel],
11
+ templateUrl: './example.html',
12
+ styleUrl: './example.css'
13
+ })`,
14
+
15
+ // Basic usage
16
+ basicUsage: `<wally-carousel>
17
+ <div>Item 1</div>
18
+ <div>Item 2</div>
19
+ </wally-carousel>`,
20
+
21
+ // Custom content example
22
+ customContent: `<wally-carousel>
23
+ <div class="w-full h-full bg-gradient-to-r from-blue-500 to-purple-600 text-white flex flex-col items-center justify-center p-6">
24
+ <h3 class="text-xl font-bold mb-2">Feature 1</h3>
25
+ <p class="text-center text-sm">Custom slide with rich content</p>
26
+ </div>
27
+ <div class="w-full h-full bg-gradient-to-r from-green-500 to-blue-500 text-white flex flex-col items-center justify-center p-6">
28
+ <h3 class="text-xl font-bold mb-2">Feature 2</h3>
29
+ <p class="text-center text-sm">Another slide with different styling</p>
30
+ </div>
31
+ </wally-carousel>`,
32
+
33
+ // Properties
34
+ properties: `// Component properties
35
+ // The carousel automatically detects child elements and creates navigation
36
+ // Currently no input properties - content projection only
37
+
38
+ // Available interactions:
39
+ // • Click navigation buttons (Previous/Next)
40
+ // • Click dot indicators for direct navigation
41
+ // • Touch/swipe gestures on mobile and desktop
42
+ // • Keyboard navigation (Arrow keys, Home, End)
43
+
44
+ // Auto-detected features:
45
+ totalItemsCount: number; // Automatically set based on child elements
46
+ currentVisibleItemIndex: number; // Currently visible slide index (0-based)
47
+ navigationDotsArray: any[]; // Auto-generated dot indicators`
48
+ };
@@ -0,0 +1,156 @@
1
+ <div class="font-mono">
2
+ <div class="max-w-4xl mx-auto p-6">
3
+ <div class="mb-4">
4
+ <wally-breadcrumb [items]="breadcrumbItems"></wally-breadcrumb>
5
+ </div>
6
+
7
+ <h1 class="text-2xl font-bold mb-4 text-[#0a0a0a] dark:text-white">
8
+ Carousel
9
+ </h1>
10
+ <p class="text-gray-700 dark:text-gray-400 mb-4">
11
+ A carousel component with navigation controls, touch gestures, and smooth transitions.
12
+ </p>
13
+
14
+ <!-- Under Construction Badge -->
15
+ <div class="mb-6">
16
+ <span class="text-xs bg-yellow-500 text-black px-3 py-1 rounded-full font-medium">Under Construction</span>
17
+ <p class="text-sm text-gray-600 dark:text-gray-400 mt-2">
18
+ This component is actively being developed. Touch gestures, accessibility, and customization options are coming soon.
19
+ </p>
20
+ </div>
21
+
22
+ <!-- AI Prompts -->
23
+ <div class="flex flex-wrap gap-2 mb-6">
24
+ <a [href]="claudeUrl" target="_blank"
25
+ class="inline-flex items-center gap-2 px-3 py-1.5 text-xs bg-gray-200 dark:bg-[#121212] text-gray-600 dark:text-gray-300 rounded-lg hover:bg-gray-300 dark:hover:bg-[#1a1a1a] hover:text-[#0a0a0a] dark:hover:text-white transition-colors">
26
+ <svg xmlns="http://www.w3.org/2000/svg" class="size-6" viewBox="0 0 24 24">
27
+ <path fill="currentColor"
28
+ d="m4.714 15.956l4.718-2.648l.079-.23l-.08-.128h-.23l-.79-.048l-2.695-.073l-2.337-.097l-2.265-.122l-.57-.121l-.535-.704l.055-.353l.48-.321l.685.06l1.518.104l2.277.157l1.651.098l2.447.255h.389l.054-.158l-.133-.097l-.103-.098l-2.356-1.596l-2.55-1.688l-1.336-.972l-.722-.491L2 6.223l-.158-1.008l.656-.722l.88.06l.224.061l.893.686l1.906 1.476l2.49 1.833l.364.304l.146-.104l.018-.072l-.164-.274l-1.354-2.446l-1.445-2.49l-.644-1.032l-.17-.619a3 3 0 0 1-.103-.729L6.287.133L6.7 0l.995.134l.42.364l.619 1.415L9.735 4.14l1.555 3.03l.455.898l.243.832l.09.255h.159V9.01l.127-1.706l.237-2.095l.23-2.695l.08-.76l.376-.91l.747-.492l.583.28l.48.685l-.067.444l-.286 1.851l-.558 2.903l-.365 1.942h.213l.243-.242l.983-1.306l1.652-2.064l.728-.82l.85-.904l.547-.431h1.032l.759 1.129l-.34 1.166l-1.063 1.347l-.88 1.142l-1.263 1.7l-.79 1.36l.074.11l.188-.02l2.853-.606l1.542-.28l1.84-.315l.832.388l.09.395l-.327.807l-1.967.486l-2.307.462l-3.436.813l-.043.03l.049.061l1.548.146l.662.036h1.62l3.018.225l.79.522l.473.638l-.08.485l-1.213.62l-1.64-.389l-3.825-.91l-1.31-.329h-.183v.11l1.093 1.068l2.003 1.81l2.508 2.33l.127.578l-.321.455l-.34-.049l-2.204-1.657l-.85-.747l-1.925-1.62h-.127v.17l.443.649l2.343 3.521l.122 1.08l-.17.353l-.607.213l-.668-.122l-1.372-1.924l-1.415-2.168l-1.141-1.943l-.14.08l-.674 7.254l-.316.37l-.728.28l-.607-.461l-.322-.747l.322-1.476l.388-1.924l.316-1.53l.285-1.9l.17-.632l-.012-.042l-.14.018l-1.432 1.967l-2.18 2.945l-1.724 1.845l-.413.164l-.716-.37l.066-.662l.401-.589l2.386-3.036l1.439-1.882l.929-1.086l-.006-.158h-.055L4.138 18.56l-1.13.146l-.485-.456l.06-.746l.231-.243l1.907-1.312Z" />
29
+ </svg>
30
+ Open in Claude
31
+ </a>
32
+
33
+ <a [href]="chatGptUrl" target="_blank"
34
+ class="inline-flex items-center gap-2 px-3 py-1.5 text-xs bg-gray-200 dark:bg-[#121212] text-gray-600 dark:text-gray-300 rounded-lg hover:bg-gray-300 dark:hover:bg-[#1a1a1a] hover:text-[#0a0a0a] dark:hover:text-white transition-colors">
35
+ <svg xmlns="http://www.w3.org/2000/svg" class="size-6" viewBox="0 0 48 48">
36
+ <path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
37
+ d="M18.38 27.94v-14.4l11.19-6.46c6.2-3.58 17.3 5.25 12.64 13.33" />
38
+ <path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
39
+ d="m18.38 20.94l12.47-7.2l11.19 6.46c6.2 3.58 4.1 17.61-5.23 17.61" />
40
+ <path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
41
+ d="m24.44 17.44l12.47 7.2v12.93c0 7.16-13.2 12.36-17.86 4.28" />
42
+ <path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
43
+ d="M30.5 21.2v14.14L19.31 41.8c-6.2 3.58-17.3-5.25-12.64-13.33" />
44
+ <path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
45
+ d="m30.5 27.94l-12.47 7.2l-11.19-6.46c-6.21-3.59-4.11-17.61 5.22-17.61" />
46
+ <path fill="none" stroke="currentColor" stroke-width="3" stroke-linejoin="round"
47
+ d="m24.44 31.44l-12.47-7.2V11.31c0-7.16 13.2-12.36 17.86-4.28" />
48
+ </svg>
49
+ Open in ChatGPT
50
+ </a>
51
+ </div>
52
+
53
+ <!-- Installation -->
54
+ <section class="mb-8">
55
+ <h2 class="text-lg font-semibold mb-4 text-[#0a0a0a] dark:text-white">Installation</h2>
56
+ <div class="bg-gray-200 dark:bg-[#121212] p-4 rounded-lg">
57
+ <pre><code [innerHTML]="installationCode" class="text-sm text-[#0a0a0a] dark:text-white"></code></pre>
58
+ </div>
59
+ </section>
60
+
61
+ <!-- Preview -->
62
+ <section class="mb-8">
63
+ <h2 class="text-lg font-semibold mb-4 text-[#0a0a0a] dark:text-white">Preview</h2>
64
+ <div class="p-6 border rounded-lg bg-white dark:bg-[#121212]">
65
+ <div class="flex justify-center">
66
+ <wally-carousel>
67
+ <div class="w-full h-full bg-blue-500 text-white flex items-center justify-center text-lg font-semibold">
68
+ Slide 1
69
+ </div>
70
+ <div class="w-full h-full bg-green-500 text-white flex items-center justify-center text-lg font-semibold">
71
+ Slide 2
72
+ </div>
73
+ <div class="w-full h-full bg-purple-500 text-white flex items-center justify-center text-lg font-semibold">
74
+ Slide 3
75
+ </div>
76
+ </wally-carousel>
77
+ </div>
78
+ </div>
79
+ </section>
80
+
81
+ <!-- Import -->
82
+ <section class="mb-8">
83
+ <h2 class="text-lg font-semibold mb-4 text-[#0a0a0a] dark:text-white">Import</h2>
84
+ <div class="space-y-4">
85
+ <div class="bg-gray-200 dark:bg-[#121212] p-4 rounded-lg">
86
+ <pre><code [innerHTML]="importCode" class="text-sm text-[#0a0a0a] dark:text-white"></code></pre>
87
+ </div>
88
+
89
+ <div class="bg-gray-200 dark:bg-[#121212] p-4 rounded-lg">
90
+ <pre><code [innerHTML]="componentImportCode" class="text-sm text-[#0a0a0a] dark:text-white"></code></pre>
91
+ </div>
92
+ </div>
93
+ </section>
94
+
95
+ <!-- Basic Usage -->
96
+ <section class="mb-8">
97
+ <h2 class="text-lg font-semibold mb-4 text-[#0a0a0a] dark:text-white">Basic Usage</h2>
98
+ <div class="bg-gray-200 dark:bg-[#121212] p-4 rounded-lg mb-4">
99
+ <pre><code [innerHTML]="basicUsageCode" class="text-sm text-[#0a0a0a] dark:text-white"></code></pre>
100
+ </div>
101
+ <div class="p-6 border rounded-lg bg-white dark:bg-[#121212]">
102
+ <div class="flex justify-center">
103
+ <wally-carousel>
104
+ <div class="w-full h-full bg-blue-400 text-white flex items-center justify-center">
105
+ Item 1
106
+ </div>
107
+ <div class="w-full h-full bg-green-400 text-white flex items-center justify-center">
108
+ Item 2
109
+ </div>
110
+ </wally-carousel>
111
+ </div>
112
+ </div>
113
+ </section>
114
+
115
+ <!-- Advanced Examples -->
116
+ <section class="mb-8">
117
+ <h2 class="text-lg font-semibold mb-4 text-[#0a0a0a] dark:text-white">Advanced Examples</h2>
118
+
119
+ <!-- Custom Content Example -->
120
+ <div class="mb-6">
121
+ <h3 class="text-md font-medium mb-3 text-[#0a0a0a] dark:text-white">Custom Content</h3>
122
+ <div class="bg-gray-200 dark:bg-[#121212] p-4 rounded-lg mb-4">
123
+ <pre><code [innerHTML]="customContentCode" class="text-sm text-[#0a0a0a] dark:text-white"></code></pre>
124
+ </div>
125
+ <div class="p-6 border rounded-lg bg-white dark:bg-[#121212]">
126
+ <div class="flex justify-center">
127
+ <wally-carousel>
128
+ <div class="w-full h-full bg-gradient-to-r from-blue-500 to-purple-600 text-white flex flex-col items-center justify-center p-6">
129
+ <h3 class="text-xl font-bold mb-2">Feature 1</h3>
130
+ <p class="text-center text-sm">This is a custom slide with rich content</p>
131
+ </div>
132
+ <div class="w-full h-full bg-gradient-to-r from-green-500 to-blue-500 text-white flex flex-col items-center justify-center p-6">
133
+ <h3 class="text-xl font-bold mb-2">Feature 2</h3>
134
+ <p class="text-center text-sm">Another slide with different styling</p>
135
+ </div>
136
+ <div class="w-full h-full bg-gradient-to-r from-purple-500 to-pink-500 text-white flex flex-col items-center justify-center p-6">
137
+ <h3 class="text-xl font-bold mb-2">Feature 3</h3>
138
+ <p class="text-center text-sm">Beautiful gradients and typography</p>
139
+ </div>
140
+ </wally-carousel>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ </section>
145
+
146
+ <!-- Properties -->
147
+ <section class="mb-8">
148
+ <h2 class="text-lg font-semibold mb-4 text-[#0a0a0a] dark:text-white">Properties</h2>
149
+ <div class="bg-gray-200 dark:bg-[#121212] p-4 rounded-lg">
150
+ <pre><code [innerHTML]="propertiesCode" class="text-sm text-[#0a0a0a] dark:text-white"></code></pre>
151
+ </div>
152
+ </section>
153
+
154
+
155
+ </div>
156
+ </div>
@@ -0,0 +1,23 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { CarouselDocs } from './carousel-docs';
4
+
5
+ describe('CarouselDocs', () => {
6
+ let component: CarouselDocs;
7
+ let fixture: ComponentFixture<CarouselDocs>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ imports: [CarouselDocs]
12
+ })
13
+ .compileComponents();
14
+
15
+ fixture = TestBed.createComponent(CarouselDocs);
16
+ component = fixture.componentInstance;
17
+ fixture.detectChanges();
18
+ });
19
+
20
+ it('should create', () => {
21
+ expect(component).toBeTruthy();
22
+ });
23
+ });
@@ -0,0 +1,46 @@
1
+ import { Component } from '@angular/core';
2
+
3
+ import { Breadcrumb, BreadcrumbItem } from '../../../../components/breadcrumb/breadcrumb';
4
+ import { Carousel } from '../../../../components/carousel/carousel';
5
+
6
+ import { AiPromptService } from '../../../../core/services/ai-prompt.service';
7
+ import { CarouselCodeExamples } from './carousel-docs.examples';
8
+ import { getFormattedCode } from '../../../../core/utils/prism';
9
+
10
+ @Component({
11
+ selector: 'wally-carousel-docs',
12
+ imports: [
13
+ Carousel,
14
+ Breadcrumb
15
+ ],
16
+ templateUrl: './carousel-docs.html',
17
+ styleUrl: './carousel-docs.css'
18
+ })
19
+ export class CarouselDocs {
20
+ breadcrumbItems: BreadcrumbItem[] = [
21
+ { label: 'Home', url: '/' },
22
+ { label: 'Documentation', url: '/documentation' },
23
+ { label: 'Components', url: '/documentation/components' },
24
+ { label: 'Carousel' }
25
+ ];
26
+
27
+ // Code examples with highlighting
28
+ installationCode = getFormattedCode(CarouselCodeExamples.installation, 'bash');
29
+ importCode = getFormattedCode(CarouselCodeExamples.import, 'typescript');
30
+ componentImportCode = getFormattedCode(CarouselCodeExamples.componentImport, 'typescript');
31
+ basicUsageCode = getFormattedCode(CarouselCodeExamples.basicUsage, 'html');
32
+ customContentCode = getFormattedCode(CarouselCodeExamples.customContent, 'html');
33
+ propertiesCode = getFormattedCode(CarouselCodeExamples.properties, 'typescript');
34
+
35
+ constructor(
36
+ private aiPromptService: AiPromptService
37
+ ) { }
38
+
39
+ get claudeUrl(): string {
40
+ return this.aiPromptService.generateClaudeUrl();
41
+ }
42
+
43
+ get chatGptUrl(): string {
44
+ return this.aiPromptService.generateChatGptUrl();
45
+ }
46
+ }
@@ -60,6 +60,19 @@
60
60
  </a>
61
61
  </div>
62
62
 
63
+ <div class="bg-gray-200 dark:bg-[#121212] rounded-lg p-4">
64
+ <div class="flex items-center gap-2 mb-2">
65
+ <h4 class="text-md font-semibold text-[#0a0a0a] dark:text-white">Carousel</h4>
66
+ <span class="text-xs bg-yellow-500 text-black px-2 py-1 rounded">Under Construction</span>
67
+ </div>
68
+ <p class="text-sm text-gray-700 dark:text-gray-400 mb-3">
69
+ A smooth carousel component with navigation controls, dot indicators, and elegant transitions using circular buffer algorithm.
70
+ </p>
71
+ <a href="/documentation/components/carousel" class="text-blue-500 underline hover:text-blue-700 text-sm">
72
+ View Documentation
73
+ </a>
74
+ </div>
75
+
63
76
  <div class="w-auto h-auto">
64
77
  <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4944285767597175"
65
78
  crossorigin="anonymous"></script>
@@ -16,5 +16,9 @@ export const componentsRoutes: Routes = [
16
16
  {
17
17
  path: 'input',
18
18
  loadComponent: () => import('./input-docs/input-docs').then(m => m.InputDocs)
19
+ },
20
+ {
21
+ path: 'carousel',
22
+ loadComponent: () => import('./carousel-docs/carousel-docs').then(m => m.CarouselDocs)
19
23
  }
20
24
  ];