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.
Files changed (2) hide show
  1. package/README.txt +655 -0
  2. 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.4",
3
+ "version": "2.2.5",
4
4
  "description": "A simple slider library for Angular",
5
- "author": "Gyoksel Ahmedov",
5
+ "author": "WingmanColt",
6
6
  "license": "MIT",
7
7
  "peerDependencies": {
8
8
  "@angular/common": ">=18.0.0",