ngx-edge-slider 2.2.4 → 2.2.5
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.txt +655 -0
- package/package.json +2 -2
package/README.txt
ADDED
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
# WingmanColt Angular Slider
|
|
2
|
+
|
|
3
|
+
A modern, fully reactive, plugin‑driven **Angular slider / carousel library** built for **Angular 18+**.
|
|
4
|
+
|
|
5
|
+
Main Slider (3 rendere sliders on homepage):
|
|
6
|
+
https://obscene.me/
|
|
7
|
+
|
|
8
|
+
Main Slider + Thumb Slider :
|
|
9
|
+
https://obscene.me/shop/product/the-devil
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
Stackblitz:
|
|
14
|
+
https://stackblitz.com/edit/angular-rx83vkhm?file=src%2Fmain.ts
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
WingmanColt is designed for **production‑grade UI systems** where flexibility, performance, and clean architecture matter. It supports **dragging**, **pagination**, **navigation**, **autoplay**, **responsive breakpoints**, and **synced thumbnail sliders**, all powered by a small, predictable core engine.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## ✨ Features
|
|
22
|
+
|
|
23
|
+
- ✅ Angular **18+** compatible (Standalone components)
|
|
24
|
+
- ⚡ **RxJS‑driven state** (predictable & debuggable)
|
|
25
|
+
- 🧩 **Plugin architecture** (enable only what you need)
|
|
26
|
+
- 🖱️ Pointer‑based dragging (mouse + touch)
|
|
27
|
+
- 📱 Responsive breakpoints (container‑aware)
|
|
28
|
+
- 🧭 Navigation arrows
|
|
29
|
+
- 🔘 Pagination (dots)
|
|
30
|
+
- ▶️ Autoplay (configurable)
|
|
31
|
+
- 🖼️ **Main + Thumbs slider syncing**
|
|
32
|
+
- 📐 Vertical & horizontal modes
|
|
33
|
+
- 🎯 Click‑to‑select slides
|
|
34
|
+
- ♻️ Safe re‑initialization on data changes
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 📦 Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install ngx-edge-slider
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
> Angular **18 or newer** is required.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 🚀 Quick Start
|
|
49
|
+
|
|
50
|
+
### 1️⃣ Import the module
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { NgxEdgeSliderModule } from "ngx-edge-slider";
|
|
54
|
+
|
|
55
|
+
@Component({
|
|
56
|
+
standalone: true,
|
|
57
|
+
imports: [NgxEdgeSliderModule],
|
|
58
|
+
})
|
|
59
|
+
export class AppComponent {}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Or use as standalone
|
|
63
|
+
```ts
|
|
64
|
+
@Component({
|
|
65
|
+
selector: 'app-root',
|
|
66
|
+
imports: [CommonModule, NgxEdgeSliderModule],
|
|
67
|
+
templateUrl: './app.html',
|
|
68
|
+
styleUrl: './app.scss',
|
|
69
|
+
encapsulation: ViewEncapsulation.None,
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
### Import Style
|
|
76
|
+
|
|
77
|
+
```css
|
|
78
|
+
@import 'ngx-edge-slider/assets/ngx-simple-slider.scss';
|
|
79
|
+
OR
|
|
80
|
+
@import 'ngx-edge-slider/assets/ngx-simple-slider.css';
|
|
81
|
+
|
|
82
|
+
OR USE CDN
|
|
83
|
+
|
|
84
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/WingmanColt/ngx-edge-slider@master/projects/ngx-edge-slider/assets/ngx-simple-slider.scss"/>
|
|
85
|
+
|
|
86
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/WingmanColt/ngx-edge-slider@master/projects/ngx-edge-slider/assets/ngx-simple-slider.css"/>
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### 2️⃣ Basic Slider Usage
|
|
94
|
+
|
|
95
|
+
```html
|
|
96
|
+
<app-simple-slider #MainSlider [config]="sliderConfig" [slideTemplate]="mainSlideTemplate" (slideChange)="onSlideChange($event)"></app-simple-slider>
|
|
97
|
+
|
|
98
|
+
<ng-template #mainSlideTemplate let-slide="slide" let-index="index">
|
|
99
|
+
<div class="slide-content">
|
|
100
|
+
<img [src]="slide.image" alt="Slide {{ index + 1 }}" />
|
|
101
|
+
</div>
|
|
102
|
+
</ng-template>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
sliderConfig: SliderConfig = {
|
|
107
|
+
slides: this.slides,
|
|
108
|
+
slidesPerView: 1,
|
|
109
|
+
plugins: {
|
|
110
|
+
draggable: true,
|
|
111
|
+
pagination: true,
|
|
112
|
+
navigation: true,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 🧱 Slider Templates
|
|
120
|
+
|
|
121
|
+
Slider is **template‑driven**. You fully control slide markup.
|
|
122
|
+
|
|
123
|
+
```html
|
|
124
|
+
<app-simple-slider [config]="sliderConfig" [slideTemplate]="slideTpl"></app-simple-slider>
|
|
125
|
+
|
|
126
|
+
<ng-template #slideTpl let-slide let-index="index">
|
|
127
|
+
<img [src]="slide.image" />
|
|
128
|
+
</ng-template>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Full Component with MainSlider, ThumbSlider Pagination, Navigation
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
```html
|
|
137
|
+
|
|
138
|
+
<ng-container *ngIf="this.slides.length">
|
|
139
|
+
<div class="slider-container" style="position: relative">
|
|
140
|
+
<app-simple-slider
|
|
141
|
+
#MainSlider
|
|
142
|
+
[config]="sliderConfig"
|
|
143
|
+
class="slider-main-product"
|
|
144
|
+
[slideTemplate]="mainSlideTemplate"
|
|
145
|
+
(slideChange)="onSlideChangeMain($event)"
|
|
146
|
+
>
|
|
147
|
+
</app-simple-slider>
|
|
148
|
+
|
|
149
|
+
<ng-template #mainSlideTemplate let-slide="slide" let-index="index">
|
|
150
|
+
<div class="slide-content">
|
|
151
|
+
<img [src]="slide.image" alt="Slide {{ index + 1 }}" />
|
|
152
|
+
</div>
|
|
153
|
+
</ng-template>
|
|
154
|
+
|
|
155
|
+
<!-- NAVIGATION -->
|
|
156
|
+
<div class="slider-nav" [ngClass]="'nav--' + navPosition">
|
|
157
|
+
<button
|
|
158
|
+
type="button"
|
|
159
|
+
class="nav-btn nav-btn--prev"
|
|
160
|
+
[class.is-hidden]="!(canPrev$ | async)"
|
|
161
|
+
(click)="onPrevClick($event)"
|
|
162
|
+
aria-label="Previous"
|
|
163
|
+
>
|
|
164
|
+
<
|
|
165
|
+
</button>
|
|
166
|
+
|
|
167
|
+
<button
|
|
168
|
+
type="button"
|
|
169
|
+
class="nav-btn nav-btn--next"
|
|
170
|
+
[class.is-hidden]="!(canNext$ | async)"
|
|
171
|
+
(click)="onNextClick($event)"
|
|
172
|
+
aria-label="Next"
|
|
173
|
+
>
|
|
174
|
+
>
|
|
175
|
+
</button>
|
|
176
|
+
</div>
|
|
177
|
+
<ng-container *ngIf="pager$ | async as pager">
|
|
178
|
+
<div class="slider-pagination" *ngIf="pager">
|
|
179
|
+
<div class="thumb-dots-wrapper">
|
|
180
|
+
<div
|
|
181
|
+
*ngFor="let slideIndex of pager.visibleDots; let i = index"
|
|
182
|
+
(click)="goToSlide(slideIndex)"
|
|
183
|
+
>
|
|
184
|
+
<span
|
|
185
|
+
class="thumb-dot"
|
|
186
|
+
[class.active]="slideIndex === pager.visibleDots[pager.activeDotIndex]"
|
|
187
|
+
[class.inactive]="slideIndex !== pager.visibleDots[pager.activeDotIndex]"
|
|
188
|
+
></span>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
</ng-container>
|
|
193
|
+
</div>
|
|
194
|
+
</ng-container>
|
|
195
|
+
|
|
196
|
+
<!-- Thumbs Slider -->
|
|
197
|
+
<ng-container *ngIf="sliderConfigThumbs?.slides?.length">
|
|
198
|
+
<div class="thumbs-wrapper">
|
|
199
|
+
<div
|
|
200
|
+
class="thumb-nav thumb-nav--left"
|
|
201
|
+
[class.activeArrow]="(thumbsState$ | async)?.canPrev"
|
|
202
|
+
(click)="ThumbsSlider?.prev()"
|
|
203
|
+
></div>
|
|
204
|
+
<app-simple-slider
|
|
205
|
+
#ThumbsSlider
|
|
206
|
+
[config]="sliderConfigThumbs"
|
|
207
|
+
[slideTemplate]="ThumbsSlideTemplate"
|
|
208
|
+
></app-simple-slider>
|
|
209
|
+
|
|
210
|
+
<ng-template #ThumbsSlideTemplate let-slide="slide" let-index="index">
|
|
211
|
+
<div
|
|
212
|
+
class="slide-content"
|
|
213
|
+
[class.slide--current]="(thumbsState$ | async)?.selectedSlide === index"
|
|
214
|
+
(click)="onThumbClick(index)"
|
|
215
|
+
>
|
|
216
|
+
<img [src]="slide.image" alt="Slide {{ index + 1 }}" />
|
|
217
|
+
</div>
|
|
218
|
+
</ng-template>
|
|
219
|
+
|
|
220
|
+
<div
|
|
221
|
+
class="thumb-nav thumb-nav--right"
|
|
222
|
+
[class.activeArrow]="(thumbsState$ | async)?.canNext"
|
|
223
|
+
(click)="ThumbsSlider?.next()"
|
|
224
|
+
></div>
|
|
225
|
+
</div>
|
|
226
|
+
</ng-container>
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Full TS File
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
import { CommonModule } from '@angular/common';
|
|
239
|
+
import { Component, OnInit, signal, ViewChild, ViewEncapsulation } from '@angular/core';
|
|
240
|
+
import {
|
|
241
|
+
NavPosition,
|
|
242
|
+
NgxEdgeSliderModule,
|
|
243
|
+
Pager,
|
|
244
|
+
SimpleSliderComponent,
|
|
245
|
+
SliderConfig,
|
|
246
|
+
} from 'ngx-edge-slider';
|
|
247
|
+
import { Observable, take } from 'rxjs';
|
|
248
|
+
|
|
249
|
+
@Component({
|
|
250
|
+
selector: 'app-root',
|
|
251
|
+
imports: [CommonModule, NgxEdgeSliderModule],
|
|
252
|
+
templateUrl: './app.html',
|
|
253
|
+
styleUrl: './app.scss',
|
|
254
|
+
encapsulation: ViewEncapsulation.None,
|
|
255
|
+
})
|
|
256
|
+
export class App implements OnInit {
|
|
257
|
+
slidesArray = [
|
|
258
|
+
{
|
|
259
|
+
image: 'https://obscene.me/assets/images/products/27/image-1/image-1.webp',
|
|
260
|
+
caption: 'First Slide',
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
image: 'https://obscene.me/assets/images/products/27/image-2/image-2.webp',
|
|
264
|
+
caption: 'Second Slide',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
image: 'https://obscene.me/assets/images/products/27/image-3/image-3.webp',
|
|
268
|
+
caption: 'Third Slide',
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
image: 'https://obscene.me/assets/images/products/27/image-4/image-4.webp',
|
|
272
|
+
caption: 'Fourth Slide',
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
image: 'https://obscene.me/assets/images/products/27/image-3/image-3.webp',
|
|
276
|
+
caption: 'Third Slide',
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
image: 'https://obscene.me/assets/images/products/27/image-1/image-1.webp',
|
|
280
|
+
caption: 'First Slide',
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
image: 'https://obscene.me/assets/images/products/27/image-2/image-2.webp',
|
|
284
|
+
caption: 'Second Slide',
|
|
285
|
+
},
|
|
286
|
+
];
|
|
287
|
+
|
|
288
|
+
private isSyncing = false;
|
|
289
|
+
slides: any[] = [...this.slidesArray];
|
|
290
|
+
|
|
291
|
+
sliderConfig!: SliderConfig;
|
|
292
|
+
sliderConfigThumbs!: SliderConfig;
|
|
293
|
+
|
|
294
|
+
navPosition: NavPosition = 'top-right'; // change this to switch layouts "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right" | "center-sides";
|
|
295
|
+
|
|
296
|
+
@ViewChild('MainSlider') MainSlider!: SimpleSliderComponent;
|
|
297
|
+
@ViewChild('ThumbsSlider') ThumbsSlider?: SimpleSliderComponent;
|
|
298
|
+
|
|
299
|
+
constructor() {}
|
|
300
|
+
|
|
301
|
+
ngOnInit(): void {
|
|
302
|
+
this.sliderConfig = {
|
|
303
|
+
slides: this.slides,
|
|
304
|
+
slidesPerView: 1,
|
|
305
|
+
slidesToSlide: 1,
|
|
306
|
+
isThumbs: false,
|
|
307
|
+
plugins: {
|
|
308
|
+
draggable: true,
|
|
309
|
+
pagination: true,
|
|
310
|
+
navigation: true,
|
|
311
|
+
autoplay: undefined,
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
this.sliderConfigThumbs = {
|
|
316
|
+
slides: this.slides,
|
|
317
|
+
slidesPerView: 4, // desktop default
|
|
318
|
+
slidesToSlide: 1,
|
|
319
|
+
isThumbs: true,
|
|
320
|
+
plugins: {
|
|
321
|
+
pagination: true, // enable pagination
|
|
322
|
+
navigation: true, // optional
|
|
323
|
+
draggable: true, // optional
|
|
324
|
+
autoplay: undefined,
|
|
325
|
+
},
|
|
326
|
+
showOn: { mobile: false, tablet: true, desktop: true },
|
|
327
|
+
breakpoints: {
|
|
328
|
+
mobile: { slidesPerView: 0 }, // not used because showOn.mobile=false
|
|
329
|
+
tablet: { slidesPerView: 4 },
|
|
330
|
+
desktop: { slidesPerView: 5 },
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Sync sliders */
|
|
336
|
+
onSlideChangeMain(index: number) {
|
|
337
|
+
this.syncSliders(index);
|
|
338
|
+
}
|
|
339
|
+
onSlideChangeThumbs(index: number) {
|
|
340
|
+
this.syncSliders(index);
|
|
341
|
+
}
|
|
342
|
+
onThumbClick(index: number) {
|
|
343
|
+
this.syncSliders(index);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private syncSliders(index: number) {
|
|
347
|
+
if (this.isSyncing) return;
|
|
348
|
+
this.isSyncing = true;
|
|
349
|
+
|
|
350
|
+
this.MainSlider?.goTo(index);
|
|
351
|
+
this.ThumbsSlider?.goTo(index);
|
|
352
|
+
|
|
353
|
+
this.isSyncing = false;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// GET SLIDER STATES
|
|
357
|
+
get mainState$() {
|
|
358
|
+
return this.MainSlider?.state$;
|
|
359
|
+
}
|
|
360
|
+
get thumbsState$() {
|
|
361
|
+
return this.ThumbsSlider?.state$;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// NAVIGATION
|
|
365
|
+
get canPrev$(): Observable<boolean> | null {
|
|
366
|
+
if (!this.MainSlider || !this.MainSlider.canPrev$) {
|
|
367
|
+
console.warn('Navigation plugin is not enabled for this slider.');
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
return this.MainSlider.canPrev$;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
get canNext$(): Observable<boolean> | null {
|
|
374
|
+
if (!this.MainSlider || !this.MainSlider.canNext$) {
|
|
375
|
+
console.warn('Navigation plugin is not enabled for this slider.');
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
return this.MainSlider.canNext$;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
onPrevClick(event?: MouseEvent) {
|
|
382
|
+
event?.stopPropagation();
|
|
383
|
+
event?.preventDefault();
|
|
384
|
+
|
|
385
|
+
this.MainSlider?.canPrev$?.pipe(take(1)).subscribe((can) => {
|
|
386
|
+
if (can) this.MainSlider.prev?.();
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
onNextClick(event?: MouseEvent) {
|
|
391
|
+
event?.stopPropagation();
|
|
392
|
+
event?.preventDefault();
|
|
393
|
+
|
|
394
|
+
this.MainSlider?.canNext$?.pipe(take(1)).subscribe((can) => {
|
|
395
|
+
if (can) this.MainSlider.next?.();
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// PAGINATION
|
|
400
|
+
get pager$(): Observable<Pager | null> | null {
|
|
401
|
+
if (!this.MainSlider?.pager$) {
|
|
402
|
+
console.warn('Pagination plugin is not enabled for this slider.');
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
return this.MainSlider.pager$;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
goToSlide(index: number) {
|
|
409
|
+
this.MainSlider?.goToSlide?.(index);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
protected readonly title = signal('slider-test');
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
```
|
|
416
|
+
----
|
|
417
|
+
|
|
418
|
+
## 🔧 SliderConfig Reference
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
export interface SliderConfig {
|
|
422
|
+
slides: any[];
|
|
423
|
+
slidesPerView: number;
|
|
424
|
+
slidesToSlide?: number;
|
|
425
|
+
loop?: 0 | 1 | 2;
|
|
426
|
+
vertical?: boolean;
|
|
427
|
+
changeToClickedSlide?: boolean;
|
|
428
|
+
isThumbs?: boolean;
|
|
429
|
+
gap?: number;
|
|
430
|
+
|
|
431
|
+
breakpoints?: {
|
|
432
|
+
mobile?: Partial<SliderConfig>;
|
|
433
|
+
tablet?: Partial<SliderConfig>;
|
|
434
|
+
desktop?: Partial<SliderConfig>;
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
plugins?: {
|
|
438
|
+
draggable?: boolean;
|
|
439
|
+
autoplay?: { delay?: number };
|
|
440
|
+
navigation?: boolean;
|
|
441
|
+
pagination?: boolean;
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
showOn?: {
|
|
445
|
+
mobile?: boolean;
|
|
446
|
+
tablet?: boolean;
|
|
447
|
+
desktop?: boolean;
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## 🔌 Plugins
|
|
455
|
+
|
|
456
|
+
Plugins are **opt‑in**. Only enabled plugins are initialized.
|
|
457
|
+
|
|
458
|
+
### Draggable
|
|
459
|
+
|
|
460
|
+
```ts
|
|
461
|
+
plugins: {
|
|
462
|
+
draggable: true;
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
- Mouse + touch dragging
|
|
467
|
+
- Pointer capture outside slider bounds
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
### Navigation
|
|
472
|
+
|
|
473
|
+
```ts
|
|
474
|
+
plugins: {
|
|
475
|
+
navigation: true;
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
```html
|
|
480
|
+
<button (click)="slider.prev()">Prev</button>
|
|
481
|
+
<button (click)="slider.next()">Next</button>
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Reactive state:
|
|
485
|
+
|
|
486
|
+
```ts
|
|
487
|
+
slider.canPrev$;
|
|
488
|
+
slider.canNext$;
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
### Pagination
|
|
494
|
+
|
|
495
|
+
```ts
|
|
496
|
+
plugins: {
|
|
497
|
+
pagination: true;
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
```ts
|
|
502
|
+
slider.pager$; // Observable<Pager>
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Pager structure:
|
|
506
|
+
|
|
507
|
+
```ts
|
|
508
|
+
interface Pager {
|
|
509
|
+
currentPage: number;
|
|
510
|
+
totalPages: number;
|
|
511
|
+
visibleDots: number[];
|
|
512
|
+
activeDotIndex: number;
|
|
513
|
+
}
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
### Autoplay
|
|
519
|
+
|
|
520
|
+
```ts
|
|
521
|
+
plugins: {
|
|
522
|
+
autoplay: {
|
|
523
|
+
delay: 3000;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
- Automatically pauses during dragging
|
|
529
|
+
- Resumes safely
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
## 🖼️ Thumbnails Slider (Main + Thumbs)
|
|
534
|
+
|
|
535
|
+
WingmanColt supports **fully synced sliders**.
|
|
536
|
+
|
|
537
|
+
```html
|
|
538
|
+
<app-simple-slider #MainSlider [config]="mainConfig" (slideChange)="onMainChange($event)"></app-simple-slider>
|
|
539
|
+
|
|
540
|
+
<app-simple-slider #ThumbsSlider [config]="thumbsConfig"></app-simple-slider>
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
```ts
|
|
544
|
+
onMainChange(index: number) {
|
|
545
|
+
this.MainSlider.goTo(index);
|
|
546
|
+
this.ThumbsSlider.goTo(index);
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
Thumbs config example:
|
|
551
|
+
|
|
552
|
+
```ts
|
|
553
|
+
thumbsConfig = {
|
|
554
|
+
slides,
|
|
555
|
+
slidesPerView: 5,
|
|
556
|
+
isThumbs: true,
|
|
557
|
+
plugins: { draggable: true, navigation: true },
|
|
558
|
+
breakpoints: {
|
|
559
|
+
tablet: { slidesPerView: 4 },
|
|
560
|
+
desktop: { slidesPerView: 5 },
|
|
561
|
+
},
|
|
562
|
+
};
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## 📐 Responsive Breakpoints
|
|
568
|
+
|
|
569
|
+
Breakpoints are **container‑aware**, not just viewport‑based.
|
|
570
|
+
|
|
571
|
+
```ts
|
|
572
|
+
breakpoints: {
|
|
573
|
+
mobile: { slidesPerView: 1 },
|
|
574
|
+
tablet: { slidesPerView: 2 },
|
|
575
|
+
desktop: { slidesPerView: 4 }
|
|
576
|
+
}
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
Visibility control:
|
|
580
|
+
|
|
581
|
+
```ts
|
|
582
|
+
showOn: {
|
|
583
|
+
mobile: false,
|
|
584
|
+
tablet: true,
|
|
585
|
+
desktop: true
|
|
586
|
+
}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
## 🧠 Architecture Overview
|
|
592
|
+
|
|
593
|
+
- **SliderEngine** – core logic, movement, breakpoints
|
|
594
|
+
- **SliderStore** – RxJS state container
|
|
595
|
+
- **Plugins** – isolated feature modules
|
|
596
|
+
- **SimpleSliderComponent** – UI wrapper
|
|
597
|
+
|
|
598
|
+
This separation allows:
|
|
599
|
+
|
|
600
|
+
- Easy feature expansion
|
|
601
|
+
- Predictable state transitions
|
|
602
|
+
- Minimal DOM coupling
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
606
|
+
## ♻️ Lifecycle & Reinitialization
|
|
607
|
+
|
|
608
|
+
The slider safely re‑initializes when:
|
|
609
|
+
|
|
610
|
+
- Slides array reference changes
|
|
611
|
+
- Breakpoints change
|
|
612
|
+
- Container size changes
|
|
613
|
+
|
|
614
|
+
```ts
|
|
615
|
+
this.engine.destroy();
|
|
616
|
+
this.engine.init(newConfig);
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
## 🛠️ Requirements
|
|
622
|
+
|
|
623
|
+
- Angular **18+**
|
|
624
|
+
- RxJS **7+**
|
|
625
|
+
- Browser support for `ResizeObserver`
|
|
626
|
+
|
|
627
|
+
---
|
|
628
|
+
|
|
629
|
+
## 🧪 Status
|
|
630
|
+
|
|
631
|
+
WingmanColt is **production‑ready**, actively evolving, and designed for real‑world applications.
|
|
632
|
+
|
|
633
|
+
Planned enhancements:
|
|
634
|
+
|
|
635
|
+
- ⏩ Loop modes
|
|
636
|
+
- 🎞️ Animation presets
|
|
637
|
+
- ♿ Accessibility helpers
|
|
638
|
+
- 🔄 Virtual slides
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
642
|
+
## 📄 License
|
|
643
|
+
|
|
644
|
+
MIT
|
|
645
|
+
|
|
646
|
+
---
|
|
647
|
+
|
|
648
|
+
## 👤 Author
|
|
649
|
+
|
|
650
|
+
**WingmanColt**
|
|
651
|
+
Angular & Full‑Stack Engineer
|
|
652
|
+
|
|
653
|
+
---
|
|
654
|
+
|
|
655
|
+
If you find this library useful, ⭐️ the repository and feel free to contribute.
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ngx-edge-slider",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.5",
|
|
4
4
|
"description": "A simple slider library for Angular",
|
|
5
|
-
"author": "
|
|
5
|
+
"author": "WingmanColt",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"peerDependencies": {
|
|
8
8
|
"@angular/common": ">=18.0.0",
|