UNPKG

@exadel/esl

Version:

Exadel Smart Library (ESL) is the lightweight custom elements library that provide a set of super-flexible components

191 lines (190 loc) 8.52 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { memoize } from '../../esl-utils/decorators'; import { isEqual } from '../../esl-utils/misc/object'; import { SyntheticEventTarget } from '../../esl-utils/dom'; import { ESLCarouselDirection } from './esl-carousel.types'; import { ESLCarouselSlideEvent } from './esl-carousel.events'; import { indexToDirection, normalize, normalizeIndex, sequence } from './esl-carousel.utils'; export class ESLCarouselRenderer { constructor($carousel, options) { /** (visible) slide count per view */ this.count = 0; /** cyclic carousel rendering mode */ this.loop = false; /** vertical carousel rendering mode */ this.vertical = false; /** marker if the renderer is applied to the carousel */ this._bound = false; this.$carousel = $carousel; this.count = options.count; this.loop = options.loop; this.vertical = options.vertical; } /** @returns marker if the renderer is applied to the carousel */ get bound() { return this._bound; } /** @returns renderer type name */ get type() { return this.constructor.is; } /** @returns slide total count or 0 if the renderer is not bound */ get size() { return this._bound ? this.$slides.length : 0; } /** @returns renderer config */ get config() { const { type, size, count, loop, vertical } = this; return { type, size, count, loop, vertical }; } /** @returns {@link ESLCarousel} `$slidesArea` */ get $area() { return this.$carousel.$slidesArea; } /** @returns {@link ESLCarousel} `$slides` */ get $slides() { return this.$carousel.$slides || []; } equal(config) { return isEqual(this.config, config); } bind() { this._bound = true; const type = this.constructor; const orientationCls = `esl-carousel-${this.vertical ? 'vertical' : 'horizontal'}`; this.$carousel.classList.add(orientationCls, ...type.classes); this.onBind(); } unbind() { if (!this._bound) return; const type = this.constructor; const orientationCls = ['esl-carousel-vertical', 'esl-carousel-horizontal']; this.$carousel.classList.remove(...orientationCls, ...type.classes); this.onUnbind(); this._bound = false; } /** Processes binding of defined renderer to the carousel {@link ESLCarousel}. */ onBind() { } /** Processes unbinding of defined renderer from the carousel {@link ESLCarousel}. */ onUnbind() { } /** Processes drawing of the carousel {@link ESLCarousel}. */ redraw() { } /** Normalizes an index before navigation */ normalizeIndex(index, params) { return normalizeIndex(index, this); } /** Normalizes a direction before navigation */ normalizeDirection(direction, params) { return (this.loop ? params && params.direction : null) || direction || ESLCarouselDirection.NEXT; } /** Processes changing slides */ navigate(to, params) { return __awaiter(this, void 0, void 0, function* () { const index = this.normalizeIndex(to.index, params); const direction = this.normalizeDirection(to.direction, params); const indexesAfter = sequence(index, this.count, this.size); const indexesBefore = this.$carousel.activeIndexes; if (indexesBefore.toString() === indexesAfter.toString()) return; const details = Object.assign(Object.assign({}, params), { direction, indexesBefore, indexesAfter }); if (!this.$carousel.dispatchEvent(ESLCarouselSlideEvent.create('BEFORE', details))) return; this.$carousel.dispatchEvent(ESLCarouselSlideEvent.create('CHANGE', details)); this.setPreActive(index); try { yield this.onBeforeAnimate(index, direction, params); yield this.onAnimate(index, direction, params); yield this.onAfterAnimate(index, direction, params); } catch (e) { console.error(e); } this.setActive(index, Object.assign({ direction }, params)); }); } /** Pre-processing animation action. */ onBeforeAnimate(index, direction, params) { return __awaiter(this, void 0, void 0, function* () { }); } /** Post-processing animation action. */ onAfterAnimate(index, direction, params) { return __awaiter(this, void 0, void 0, function* () { }); } /** Sets active slides from passed index **/ setActive(current, event) { const related = this.$carousel.activeIndex; const indexesBefore = this.$carousel.activeIndexes; const count = Math.min(this.count, this.size); const indexesAfter = []; for (let i = 0; i < this.size; i++) { const position = normalize(i + current, this.size); const $slide = this.$slides[position]; if (i < count) indexesAfter.push(position); $slide.toggleAttribute('active', i < count); $slide.toggleAttribute('pre-active', false); $slide.toggleAttribute('next', i === count && (this.loop || position !== 0)); $slide.toggleAttribute('prev', i === this.size - 1 && i >= count && (this.loop || position !== this.size - 1)); } if (event && typeof event === 'object') { const direction = event.direction || indexToDirection(related, this.$carousel.state); const details = Object.assign(Object.assign({}, event), { direction, indexesBefore, indexesAfter }); this.$carousel.dispatchEvent(ESLCarouselSlideEvent.create('AFTER', details)); } } setPreActive(from, force = true) { const count = Math.min(this.count, this.size); for (let i = 0; i < this.size; ++i) { const $slide = this.$slides[normalize(i + from, this.size)]; $slide.toggleAttribute('pre-active', force && i < count); } } // Register API static get registry() { return new ESLCarouselRendererRegistry(); } static register(view = this) { ESLCarouselRenderer.registry.register(view); } } ESLCarouselRenderer.classes = []; __decorate([ memoize() ], ESLCarouselRenderer, "registry", null); export class ESLCarouselRendererRegistry extends SyntheticEventTarget { constructor() { super(...arguments); this.store = new Map(); } create(carousel, config) { let Renderer = this.store.get(config.type); if (!Renderer) [Renderer] = this.store.values(); // take first Renderer in store return new Renderer(carousel, config); } register(view) { if (!view || !view.is) throw Error('[ESL]: CarouselRendererRegistry: incorrect registration request'); if (this.store.has(view.is)) throw Error(`View with name ${view.is} already defined`); this.store.set(view.is, view); const detail = { name: view.is, view }; const event = new CustomEvent('change', { detail }); this.dispatchEvent(event); } }