UNPKG

22.5 kBJavaScriptView Raw
1import { EventEmitter, Component, ChangeDetectionStrategy, ViewEncapsulation, ElementRef, NgZone, ChangeDetectorRef, Input, Output, ViewChild, ContentChild, ContentChildren, NgModule } from '@angular/core';
2import { Header, Footer, PrimeTemplate, SharedModule } from 'primeng/api';
3import { RippleModule } from 'primeng/ripple';
4import { CommonModule } from '@angular/common';
5import { UniqueComponentId } from 'primeng/utils';
6
7class Carousel {
8 constructor(el, zone, cd) {
9 this.el = el;
10 this.zone = zone;
11 this.cd = cd;
12 this.orientation = "horizontal";
13 this.verticalViewPortHeight = "300px";
14 this.contentClass = "";
15 this.indicatorsContentClass = "";
16 this.circular = false;
17 this.autoplayInterval = 0;
18 this.onPage = new EventEmitter();
19 this._numVisible = 1;
20 this._numScroll = 1;
21 this._oldNumScroll = 0;
22 this.prevState = {
23 numScroll: 0,
24 numVisible: 0,
25 value: []
26 };
27 this.defaultNumScroll = 1;
28 this.defaultNumVisible = 1;
29 this._page = 0;
30 this.isRemainingItemsAdded = false;
31 this.remainingItems = 0;
32 this.swipeThreshold = 20;
33 this.totalShiftedItems = this.page * this.numScroll * -1;
34 }
35 get page() {
36 return this._page;
37 }
38 set page(val) {
39 if (this.isCreated && val !== this._page) {
40 if (this.autoplayInterval) {
41 this.stopAutoplay();
42 this.allowAutoplay = false;
43 }
44 if (val > this._page && val <= (this.totalDots() - 1)) {
45 this.step(-1, val);
46 }
47 else if (val < this._page) {
48 this.step(1, val);
49 }
50 }
51 this._page = val;
52 }
53 get numVisible() {
54 return this._numVisible;
55 }
56 set numVisible(val) {
57 this._numVisible = val;
58 }
59 get numScroll() {
60 return this._numVisible;
61 }
62 set numScroll(val) {
63 this._numScroll = val;
64 }
65 get value() {
66 return this._value;
67 }
68 ;
69 set value(val) {
70 this._value = val;
71 if (this.circular && this._value) {
72 this.setCloneItems();
73 }
74 }
75 ngAfterContentInit() {
76 this.id = UniqueComponentId();
77 this.allowAutoplay = !!this.autoplayInterval;
78 if (this.circular) {
79 this.setCloneItems();
80 }
81 if (this.responsiveOptions) {
82 this.defaultNumScroll = this._numScroll;
83 this.defaultNumVisible = this._numVisible;
84 }
85 this.createStyle();
86 this.calculatePosition();
87 if (this.responsiveOptions) {
88 this.bindDocumentListeners();
89 }
90 this.templates.forEach((item) => {
91 switch (item.getType()) {
92 case 'item':
93 this.itemTemplate = item.template;
94 break;
95 case 'header':
96 this.headerTemplate = item.template;
97 break;
98 case 'footer':
99 this.footerTemplate = item.template;
100 break;
101 default:
102 this.itemTemplate = item.template;
103 break;
104 }
105 });
106 }
107 ngAfterContentChecked() {
108 const isCircular = this.isCircular();
109 let totalShiftedItems = this.totalShiftedItems;
110 if (this.value && (this.prevState.numScroll !== this._numScroll || this.prevState.numVisible !== this._numVisible || this.prevState.value.length !== this.value.length)) {
111 if (this.autoplayInterval) {
112 this.stopAutoplay();
113 }
114 this.remainingItems = (this.value.length - this._numVisible) % this._numScroll;
115 let page = this._page;
116 if (this.totalDots() !== 0 && page >= this.totalDots()) {
117 page = this.totalDots() - 1;
118 this._page = page;
119 this.onPage.emit({
120 page: this.page
121 });
122 }
123 totalShiftedItems = (page * this._numScroll) * -1;
124 if (isCircular) {
125 totalShiftedItems -= this._numVisible;
126 }
127 if (page === (this.totalDots() - 1) && this.remainingItems > 0) {
128 totalShiftedItems += (-1 * this.remainingItems) + this._numScroll;
129 this.isRemainingItemsAdded = true;
130 }
131 else {
132 this.isRemainingItemsAdded = false;
133 }
134 if (totalShiftedItems !== this.totalShiftedItems) {
135 this.totalShiftedItems = totalShiftedItems;
136 }
137 this._oldNumScroll = this._numScroll;
138 this.prevState.numScroll = this._numScroll;
139 this.prevState.numVisible = this._numVisible;
140 this.prevState.value = this._value;
141 if (this.totalDots() > 0 && this.itemsContainer && this.itemsContainer.nativeElement) {
142 this.itemsContainer.nativeElement.style.transform = this.isVertical() ? `translate3d(0, ${totalShiftedItems * (100 / this._numVisible)}%, 0)` : `translate3d(${totalShiftedItems * (100 / this._numVisible)}%, 0, 0)`;
143 }
144 this.isCreated = true;
145 if (this.autoplayInterval && this.isAutoplay()) {
146 this.startAutoplay();
147 }
148 }
149 if (isCircular) {
150 if (this.page === 0) {
151 totalShiftedItems = -1 * this._numVisible;
152 }
153 else if (totalShiftedItems === 0) {
154 totalShiftedItems = -1 * this.value.length;
155 if (this.remainingItems > 0) {
156 this.isRemainingItemsAdded = true;
157 }
158 }
159 if (totalShiftedItems !== this.totalShiftedItems) {
160 this.totalShiftedItems = totalShiftedItems;
161 }
162 }
163 }
164 createStyle() {
165 if (!this.carouselStyle) {
166 this.carouselStyle = document.createElement('style');
167 this.carouselStyle.type = 'text/css';
168 document.body.appendChild(this.carouselStyle);
169 }
170 let innerHTML = `
171 #${this.id} .p-carousel-item {
172 flex: 1 0 ${(100 / this.numVisible)}%
173 }
174 `;
175 if (this.responsiveOptions) {
176 this.responsiveOptions.sort((data1, data2) => {
177 const value1 = data1.breakpoint;
178 const value2 = data2.breakpoint;
179 let result = null;
180 if (value1 == null && value2 != null)
181 result = -1;
182 else if (value1 != null && value2 == null)
183 result = 1;
184 else if (value1 == null && value2 == null)
185 result = 0;
186 else if (typeof value1 === 'string' && typeof value2 === 'string')
187 result = value1.localeCompare(value2, undefined, { numeric: true });
188 else
189 result = (value1 < value2) ? -1 : (value1 > value2) ? 1 : 0;
190 return -1 * result;
191 });
192 for (let i = 0; i < this.responsiveOptions.length; i++) {
193 let res = this.responsiveOptions[i];
194 innerHTML += `
195 @media screen and (max-width: ${res.breakpoint}) {
196 #${this.id} .p-carousel-item {
197 flex: 1 0 ${(100 / res.numVisible)}%
198 }
199 }
200 `;
201 }
202 }
203 this.carouselStyle.innerHTML = innerHTML;
204 }
205 calculatePosition() {
206 if (this.itemsContainer && this.responsiveOptions) {
207 let windowWidth = window.innerWidth;
208 let matchedResponsiveData = {
209 numVisible: this.defaultNumVisible,
210 numScroll: this.defaultNumScroll
211 };
212 for (let i = 0; i < this.responsiveOptions.length; i++) {
213 let res = this.responsiveOptions[i];
214 if (parseInt(res.breakpoint, 10) >= windowWidth) {
215 matchedResponsiveData = res;
216 }
217 }
218 if (this._numScroll !== matchedResponsiveData.numScroll) {
219 let page = this._page;
220 page = Math.floor((page * this._numScroll) / matchedResponsiveData.numScroll);
221 let totalShiftedItems = (matchedResponsiveData.numScroll * this.page) * -1;
222 if (this.isCircular()) {
223 totalShiftedItems -= matchedResponsiveData.numVisible;
224 }
225 this.totalShiftedItems = totalShiftedItems;
226 this._numScroll = matchedResponsiveData.numScroll;
227 this._page = page;
228 this.onPage.emit({
229 page: this.page
230 });
231 }
232 if (this._numVisible !== matchedResponsiveData.numVisible) {
233 this._numVisible = matchedResponsiveData.numVisible;
234 this.setCloneItems();
235 }
236 this.cd.markForCheck();
237 }
238 }
239 setCloneItems() {
240 this.clonedItemsForStarting = [];
241 this.clonedItemsForFinishing = [];
242 if (this.isCircular()) {
243 this.clonedItemsForStarting.push(...this.value.slice(-1 * this._numVisible));
244 this.clonedItemsForFinishing.push(...this.value.slice(0, this._numVisible));
245 }
246 }
247 firstIndex() {
248 return this.isCircular() ? (-1 * (this.totalShiftedItems + this.numVisible)) : (this.totalShiftedItems * -1);
249 }
250 lastIndex() {
251 return this.firstIndex() + this.numVisible - 1;
252 }
253 totalDots() {
254 return this.value ? Math.ceil((this.value.length - this._numVisible) / this._numScroll) + 1 : 0;
255 }
256 totalDotsArray() {
257 const totalDots = this.totalDots();
258 return totalDots <= 0 ? [] : Array(totalDots).fill(0);
259 }
260 isVertical() {
261 return this.orientation === 'vertical';
262 }
263 isCircular() {
264 return this.circular && this.value && this.value.length >= this.numVisible;
265 }
266 isAutoplay() {
267 return this.autoplayInterval && this.allowAutoplay;
268 }
269 isForwardNavDisabled() {
270 return this.isEmpty() || (this._page >= (this.totalDots() - 1) && !this.isCircular());
271 }
272 isBackwardNavDisabled() {
273 return this.isEmpty() || (this._page <= 0 && !this.isCircular());
274 }
275 isEmpty() {
276 return !this.value || this.value.length === 0;
277 }
278 navForward(e, index) {
279 if (this.isCircular() || this._page < (this.totalDots() - 1)) {
280 this.step(-1, index);
281 }
282 if (this.autoplayInterval) {
283 this.stopAutoplay();
284 this.allowAutoplay = false;
285 }
286 if (e && e.cancelable) {
287 e.preventDefault();
288 }
289 }
290 navBackward(e, index) {
291 if (this.isCircular() || this._page !== 0) {
292 this.step(1, index);
293 }
294 if (this.autoplayInterval) {
295 this.stopAutoplay();
296 this.allowAutoplay = false;
297 }
298 if (e && e.cancelable) {
299 e.preventDefault();
300 }
301 }
302 onDotClick(e, index) {
303 let page = this._page;
304 if (this.autoplayInterval) {
305 this.stopAutoplay();
306 this.allowAutoplay = false;
307 }
308 if (index > page) {
309 this.navForward(e, index);
310 }
311 else if (index < page) {
312 this.navBackward(e, index);
313 }
314 }
315 step(dir, page) {
316 let totalShiftedItems = this.totalShiftedItems;
317 const isCircular = this.isCircular();
318 if (page != null) {
319 totalShiftedItems = (this._numScroll * page) * -1;
320 if (isCircular) {
321 totalShiftedItems -= this._numVisible;
322 }
323 this.isRemainingItemsAdded = false;
324 }
325 else {
326 totalShiftedItems += (this._numScroll * dir);
327 if (this.isRemainingItemsAdded) {
328 totalShiftedItems += this.remainingItems - (this._numScroll * dir);
329 this.isRemainingItemsAdded = false;
330 }
331 let originalShiftedItems = isCircular ? (totalShiftedItems + this._numVisible) : totalShiftedItems;
332 page = Math.abs(Math.floor((originalShiftedItems / this._numScroll)));
333 }
334 if (isCircular && this.page === (this.totalDots() - 1) && dir === -1) {
335 totalShiftedItems = -1 * (this.value.length + this._numVisible);
336 page = 0;
337 }
338 else if (isCircular && this.page === 0 && dir === 1) {
339 totalShiftedItems = 0;
340 page = (this.totalDots() - 1);
341 }
342 else if (page === (this.totalDots() - 1) && this.remainingItems > 0) {
343 totalShiftedItems += ((this.remainingItems * -1) - (this._numScroll * dir));
344 this.isRemainingItemsAdded = true;
345 }
346 if (this.itemsContainer) {
347 this.itemsContainer.nativeElement.style.transform = this.isVertical() ? `translate3d(0, ${totalShiftedItems * (100 / this._numVisible)}%, 0)` : `translate3d(${totalShiftedItems * (100 / this._numVisible)}%, 0, 0)`;
348 this.itemsContainer.nativeElement.style.transition = 'transform 500ms ease 0s';
349 }
350 this.totalShiftedItems = totalShiftedItems;
351 this._page = page;
352 this.onPage.emit({
353 page: this.page
354 });
355 }
356 startAutoplay() {
357 this.interval = setInterval(() => {
358 if (this.totalDots() > 0) {
359 if (this.page === (this.totalDots() - 1)) {
360 this.step(-1, 0);
361 }
362 else {
363 this.step(-1, this.page + 1);
364 }
365 }
366 }, this.autoplayInterval);
367 }
368 stopAutoplay() {
369 if (this.interval) {
370 clearInterval(this.interval);
371 }
372 }
373 onTransitionEnd() {
374 if (this.itemsContainer) {
375 this.itemsContainer.nativeElement.style.transition = '';
376 if ((this.page === 0 || this.page === (this.totalDots() - 1)) && this.isCircular()) {
377 this.itemsContainer.nativeElement.style.transform = this.isVertical() ? `translate3d(0, ${this.totalShiftedItems * (100 / this._numVisible)}%, 0)` : `translate3d(${this.totalShiftedItems * (100 / this._numVisible)}%, 0, 0)`;
378 }
379 }
380 }
381 onTouchStart(e) {
382 let touchobj = e.changedTouches[0];
383 this.startPos = {
384 x: touchobj.pageX,
385 y: touchobj.pageY
386 };
387 }
388 onTouchMove(e) {
389 if (e.cancelable) {
390 e.preventDefault();
391 }
392 }
393 onTouchEnd(e) {
394 let touchobj = e.changedTouches[0];
395 if (this.isVertical()) {
396 this.changePageOnTouch(e, (touchobj.pageY - this.startPos.y));
397 }
398 else {
399 this.changePageOnTouch(e, (touchobj.pageX - this.startPos.x));
400 }
401 }
402 changePageOnTouch(e, diff) {
403 if (Math.abs(diff) > this.swipeThreshold) {
404 if (diff < 0) {
405 this.navForward(e);
406 }
407 else {
408 this.navBackward(e);
409 }
410 }
411 }
412 bindDocumentListeners() {
413 if (!this.documentResizeListener) {
414 this.documentResizeListener = (e) => {
415 this.calculatePosition();
416 };
417 window.addEventListener('resize', this.documentResizeListener);
418 }
419 }
420 unbindDocumentListeners() {
421 if (this.documentResizeListener) {
422 window.removeEventListener('resize', this.documentResizeListener);
423 this.documentResizeListener = null;
424 }
425 }
426 ngOnDestroy() {
427 if (this.responsiveOptions) {
428 this.unbindDocumentListeners();
429 }
430 if (this.autoplayInterval) {
431 this.stopAutoplay();
432 }
433 }
434}
435Carousel.decorators = [
436 { type: Component, args: [{
437 selector: 'p-carousel',
438 template: `
439 <div [attr.id]="id" [ngClass]="{'p-carousel p-component':true, 'p-carousel-vertical': isVertical(), 'p-carousel-horizontal': !isVertical()}" [ngStyle]="style" [class]="styleClass">
440 <div class="p-carousel-header" *ngIf="headerFacet || headerTemplate">
441 <ng-content select="p-header"></ng-content>
442 <ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
443 </div>
444 <div [class]="contentClass" [ngClass]="'p-carousel-content'">
445 <div class="p-carousel-container">
446 <button type="button" [ngClass]="{'p-carousel-prev p-link':true, 'p-disabled': isBackwardNavDisabled()}" [disabled]="isBackwardNavDisabled()" (click)="navBackward($event)" pRipple>
447 <span [ngClass]="{'p-carousel-prev-icon pi': true, 'pi-chevron-left': !isVertical(), 'pi-chevron-up': isVertical()}"></span>
448 </button>
449 <div class="p-carousel-items-content" [ngStyle]="{'height': isVertical() ? verticalViewPortHeight : 'auto'}">
450 <div #itemsContainer class="p-carousel-items-container" (transitionend)="onTransitionEnd()" (touchend)="onTouchEnd($event)" (touchstart)="onTouchStart($event)" (touchmove)="onTouchMove($event)">
451 <div *ngFor="let item of clonedItemsForStarting; let index = index" [ngClass]= "{'p-carousel-item p-carousel-item-cloned': true,
452 'p-carousel-item-active': (totalShiftedItems * -1) === (value.length),
453 'p-carousel-item-start': 0 === index,
454 'p-carousel-item-end': (clonedItemsForStarting.length - 1) === index}">
455 <ng-container *ngTemplateOutlet="itemTemplate; context: {$implicit: item}"></ng-container>
456 </div>
457 <div *ngFor="let item of value; let index = index" [ngClass]= "{'p-carousel-item': true,
458 'p-carousel-item-active': (firstIndex() <= index && lastIndex() >= index),
459 'p-carousel-item-start': firstIndex() === index,
460 'p-carousel-item-end': lastIndex() === index}">
461 <ng-container *ngTemplateOutlet="itemTemplate; context: {$implicit: item}"></ng-container>
462 </div>
463 <div *ngFor="let item of clonedItemsForFinishing; let index = index" [ngClass]= "{'p-carousel-item p-carousel-item-cloned': true,
464 'p-carousel-item-active': ((totalShiftedItems *-1) === numVisible),
465 'p-carousel-item-start': 0 === index,
466 'p-carousel-item-end': (clonedItemsForFinishing.length - 1) === index}">
467 <ng-container *ngTemplateOutlet="itemTemplate; context: {$implicit: item}"></ng-container>
468 </div>
469 </div>
470 </div>
471 <button type="button" [ngClass]="{'p-carousel-next p-link': true, 'p-disabled': isForwardNavDisabled()}" [disabled]="isForwardNavDisabled()" (click)="navForward($event)" pRipple>
472 <span [ngClass]="{'p-carousel-prev-icon pi': true, 'pi-chevron-right': !isVertical(), 'pi-chevron-down': isVertical()}"></span>
473 </button>
474 </div>
475 <ul [ngClass]="'p-carousel-indicators p-reset'" [class]="indicatorsContentClass">
476 <li *ngFor="let totalDot of totalDotsArray(); let i = index" [ngClass]="{'p-carousel-indicator':true,'p-highlight': _page === i}">
477 <button type="button" class="p-link" (click)="onDotClick($event, i)"></button>
478 </li>
479 </ul>
480 </div>
481 <div class="p-carousel-footer" *ngIf="footerFacet || footerTemplate">
482 <ng-content select="p-footer"></ng-content>
483 <ng-container *ngTemplateOutlet="footerTemplate"></ng-container>
484 </div>
485 </div>
486 `,
487 changeDetection: ChangeDetectionStrategy.OnPush,
488 encapsulation: ViewEncapsulation.None,
489 styles: [".p-carousel,.p-carousel-content{-ms-flex-direction:column;display:-ms-flexbox;display:flex;flex-direction:column}.p-carousel-content{overflow:auto}.p-carousel-next,.p-carousel-prev{-ms-flex-align:center;-ms-flex-item-align:center;-ms-flex-negative:0;-ms-flex-pack:center;-ms-flex-positive:0;align-items:center;align-self:center;display:-ms-flexbox;display:flex;flex-grow:0;flex-shrink:0;justify-content:center;overflow:hidden;position:relative}.p-carousel-container{-ms-flex-direction:row;display:-ms-flexbox;display:flex;flex-direction:row}.p-carousel-items-content{overflow:hidden;width:100%}.p-carousel-indicators,.p-carousel-items-container{-ms-flex-direction:row;display:-ms-flexbox;display:flex;flex-direction:row}.p-carousel-indicators{-ms-flex-pack:center;-ms-flex-wrap:wrap;flex-wrap:wrap;justify-content:center}.p-carousel-indicator>button{-ms-flex-align:center;-ms-flex-pack:center;align-items:center;display:-ms-flexbox;display:flex;justify-content:center}.p-carousel-vertical .p-carousel-container{-ms-flex-direction:column;flex-direction:column}.p-carousel-vertical .p-carousel-items-container{-ms-flex-direction:column;flex-direction:column;height:100%}.p-items-hidden .p-carousel-item{visibility:hidden}.p-items-hidden .p-carousel-item.p-carousel-item-active{visibility:visible}"]
490 },] }
491];
492Carousel.ctorParameters = () => [
493 { type: ElementRef },
494 { type: NgZone },
495 { type: ChangeDetectorRef }
496];
497Carousel.propDecorators = {
498 page: [{ type: Input }],
499 numVisible: [{ type: Input }],
500 numScroll: [{ type: Input }],
501 responsiveOptions: [{ type: Input }],
502 orientation: [{ type: Input }],
503 verticalViewPortHeight: [{ type: Input }],
504 contentClass: [{ type: Input }],
505 indicatorsContentClass: [{ type: Input }],
506 value: [{ type: Input }],
507 circular: [{ type: Input }],
508 autoplayInterval: [{ type: Input }],
509 style: [{ type: Input }],
510 styleClass: [{ type: Input }],
511 onPage: [{ type: Output }],
512 itemsContainer: [{ type: ViewChild, args: ['itemsContainer',] }],
513 headerFacet: [{ type: ContentChild, args: [Header,] }],
514 footerFacet: [{ type: ContentChild, args: [Footer,] }],
515 templates: [{ type: ContentChildren, args: [PrimeTemplate,] }]
516};
517class CarouselModule {
518}
519CarouselModule.decorators = [
520 { type: NgModule, args: [{
521 imports: [CommonModule, SharedModule, RippleModule],
522 exports: [CommonModule, Carousel, SharedModule],
523 declarations: [Carousel]
524 },] }
525];
526
527/**
528 * Generated bundle index. Do not edit.
529 */
530
531export { Carousel, CarouselModule };
532//# sourceMappingURL=primeng-carousel.js.map
533
\No newline at end of file