epic-slidy
Version:
An EPIC slider
1 lines • 60.8 kB
Source Map (JSON)
{"version":3,"file":"slidy.mjs","sources":["../src/utils/index.ts","../src/modules/Controls.ts","../src/modules/Events.ts","../src/modules/Hooks.ts","../src/modules/Nav.ts","../src/modules/Pagination.ts","../src/modules/Manager.ts","../src/index.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/ban-types */\nimport { GenericObject } from '../defs'\n\n/**\n * Get nearest ancestor by classname\n */\n\nexport function parents(\n element: HTMLElement,\n className: string\n): HTMLElement | null {\n for (\n let el = element;\n el && ((el as unknown) as Document) !== document;\n el = el.parentNode as HTMLElement\n ) {\n if (el.classList.contains(className)) {\n return el\n }\n }\n\n return null\n}\n\n/**\n * Check for touchevents.\n */\n\nexport function touchevents(): boolean {\n if ('ontouchstart' in window) {\n return true\n }\n\n return false\n}\n\n/**\n * Given a number, return a zero-filled string.\n * Source: https://github.com/feross/zero-fill\n */\n\nfunction zeroFill(width: number, number: number): string {\n const pad = '0'\n const fill = width - number.toString().length\n\n if (fill > 0) {\n return new Array(fill + 1).join(pad) + number\n }\n\n return String(number)\n}\n\n/**\n * Format number (zerofill or not)\n */\n\nexport function format(\n number: number,\n nbItems: number,\n zerofill: boolean | number\n): string {\n if (zerofill === false) {\n return String(number)\n }\n\n const length = zerofill === true ? nbItems.toString(10).length : zerofill\n\n return zeroFill(length, number)\n}\n\n/**\n * Source: https://gist.github.com/smeijer/6580740a0ff468960a5257108af1384e\n */\n\nfunction get(path: string, obj: GenericObject, fb = `$\\{${path}}`): string {\n return (path\n .split('.')\n .reduce((res, key) => res[key] || fb, obj) as unknown) as string\n}\n\nexport function parseTpl(\n template: string,\n map: GenericObject,\n fallback?: string\n): string {\n return template.replace(/\\$\\{.+?}/g, (match: string) => {\n const path = match.substr(2, match.length - 3).trim()\n\n return get(path, map, fallback)\n })\n}\n\n// Credit David Walsh (https://davidwalsh.name/javascript-debounce-function)\n\n// Returns a function, that, as long as it continues to be invoked, will not\n// be triggered. The function will be called after it stops being called for\n// N milliseconds. If `immediate` is passed, trigger the function on the\n// leading edge, instead of the trailing.\n\nexport function debounce(\n func: Function,\n wait: number\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): (...args: any[]) => void {\n /* eslint-disable no-invalid-this */\n let timeout: number\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return function executedFunction(...args: any[]) {\n const later = () => {\n timeout = null\n func.apply(this, args)\n }\n\n clearTimeout(timeout)\n timeout = window.setTimeout(later, wait)\n }\n /* eslint-enable no-invalid-this */\n}\n","import Slidy from '..'\nimport { Options } from '../defs'\nimport { parseTpl } from '../utils'\n\n/**\n * Create controls.\n */\nexport class Controls {\n /**\n * Disable control.\n */\n private static disable(el: HTMLButtonElement) {\n el.setAttribute('disabled', '')\n el.classList.add('is-disabled')\n }\n\n /**\n * Enable control.\n */\n private static enable(el: HTMLButtonElement) {\n el.removeAttribute('disabled')\n el.classList.remove('is-disabled')\n }\n\n private _el: HTMLDivElement\n private _prev: HTMLButtonElement\n private _next: HTMLButtonElement\n\n /**\n * Creates an instance of Controls.\n */\n constructor(private _slidy: Slidy, private _opts: Options) {\n this._init()\n this._bind()\n }\n\n /**\n * Destroy component.\n */\n public destroy(): void {\n this._el = document.querySelector('.slidy-controls')\n this._el.parentNode.removeChild(this._el)\n this._slidy.hooks.remove('beforeSlide', this._update)\n }\n\n /**\n * Init component.\n */\n private _init() {\n const { namespace: ns, outer } = this._slidy\n const { controls } = this._opts\n\n let prev = '<'\n let next = '>'\n\n if (controls !== true) {\n prev = parseTpl(controls as string, {\n label: 'previous slide',\n })\n next = parseTpl(controls as string, {\n label: 'next slide',\n })\n }\n\n const el = document.createElement('div')\n\n el.classList.add(`${ns}-controls`)\n\n const html = `<button type=\"button\" class=\"${ns}-controls__item--prev\">${prev}</button>\n <button type=\"button\" class=\"${ns}-controls__item--next\">${next}</button>`\n\n el.innerHTML = html\n\n this._el = el\n this._prev = this._el.querySelector('button:first-child')\n this._next = this._el.querySelector('button:last-child')\n outer.appendChild(this._el)\n\n this._update()\n }\n\n /**\n * Bind event handlers.\n */\n private _bind() {\n this._prevClick = this._prevClick.bind(this)\n this._nextClick = this._nextClick.bind(this)\n this._update = this._update.bind(this)\n\n this._slidy.hooks.add('beforeSlide', this._update)\n\n this._bindControls()\n\n if (this._opts.keyboard) {\n addEventListener('keydown', event => {\n if (event.keyCode === 37) {\n this._slidy.slidePrev('controls')\n } else if (event.keyCode === 39) {\n this._slidy.slideNext('controls')\n }\n })\n }\n }\n\n /**\n * Bind controls handlers\n */\n private _bindControls() {\n this._prev.addEventListener('click', this._prevClick)\n this._next.addEventListener('click', this._nextClick)\n }\n\n /**\n * On prev click.\n */\n private _prevClick() {\n this._slidy.slidePrev('controls')\n }\n\n /**\n * On next click.\n */\n private _nextClick() {\n this._slidy.slideNext('controls')\n }\n\n /**\n * Update controls.\n */\n private _update() {\n if (!this._opts.loop) {\n const { newIndex } = this._slidy\n const { length } = this._slidy.items\n\n Controls.enable(this._prev)\n Controls.enable(this._next)\n\n if (newIndex === 0) {\n Controls.disable(this._prev)\n }\n\n if (newIndex + this._slidy.group >= length) {\n Controls.disable(this._next)\n }\n }\n }\n}\n","/* eslint-disable @typescript-eslint/ban-types */\nimport { GestureDirection, SupportedEvents, SupportedTypes } from '../defs'\n\ntype Cb = (e: TouchEvent | MouseEvent) => void\n\nexport class Events {\n private _events: Map<SupportedEvents, Function> = new Map()\n private _types: Map<SupportedTypes, Cb> = new Map()\n private _x0: number\n private _t0: number\n\n private _holdThreshold = 1000\n private _movePrecision = 50\n private _moveThreshold = 100\n\n private _hasTouchListener: boolean\n private _hasMouseListener: boolean\n\n constructor(private _el: HTMLElement) {\n this._lock = this._lock.bind(this)\n this._release = this._release.bind(this)\n this._move = this._move.bind(this)\n }\n\n public on(name: SupportedEvents, cb: Function): void {\n let eventIn\n let eventOut\n let eventMove\n\n // Add event callback by event name.\n this._events.set(name, cb)\n\n switch (name) {\n case 'click':\n case 'drag':\n // Do not listen twice.\n if (!this._hasMouseListener) {\n // When it starts\n eventIn =\n Events._defaultType('mousedown') ||\n Events._pointerType('PointerDown')\n // When it ends\n eventOut =\n Events._defaultType('mouseup') || Events._pointerType('PointerUp')\n // In between…\n eventMove =\n Events._defaultType('mousemove') ||\n Events._pointerType('PointerMove')\n this._hasMouseListener = true\n }\n break\n case 'tap':\n case 'swipe':\n // Do not listen twice.\n if (!this._hasTouchListener) {\n // When it starts\n eventIn =\n Events._defaultType('touchstart') ||\n Events._pointerType('PointerDown')\n // When it ends\n eventOut =\n Events._defaultType('touchend') || Events._pointerType('PointerUp')\n // In between…\n eventMove =\n Events._defaultType('touchmove') ||\n Events._pointerType('PointerMove')\n this._hasTouchListener = true\n\n if (name === 'swipe') {\n this._el.style.touchAction = 'pan-y'\n }\n }\n break\n default:\n }\n\n // Binding\n eventIn && this._bind(eventIn as SupportedTypes, this._lock)\n eventOut && this._bind(eventOut as SupportedTypes, this._release)\n eventMove && this._bind(eventMove as SupportedTypes, this._move)\n }\n\n public destroy(): void {\n this._events.forEach((cb, name) => {\n this._events.delete(name)\n })\n this._types.forEach((cb, type) => {\n this._unbind(type)\n })\n }\n\n /**\n * Convert touch event with multiple fingers\n * to single one, similar to mouse event.\n * Needed to manage coordinates.\n */\n private static _unify(e: TouchEvent | MouseEvent): Touch | MouseEvent {\n return (e as TouchEvent).changedTouches\n ? (e as TouchEvent).changedTouches[0]\n : (e as MouseEvent)\n }\n\n /**\n * Get pointer event\n * Supports MS prefixed events.\n */\n private static _pointerType(type: string): SupportedTypes | boolean {\n const lo = type.toLowerCase() as SupportedTypes\n const ms = `MS${type}` as SupportedTypes\n\n // eslint-disable-next-line no-nested-ternary\n return navigator.msPointerEnabled ? ms : window.PointerEvent ? lo : false\n }\n\n /**\n * Get HTML5 native event\n * Receives 'touchstart', returns 'ontouchstart'\n */\n private static _defaultType(type: SupportedTypes): SupportedTypes | boolean {\n return `on${type}` in window ? type : false\n }\n\n private _bind(type: SupportedTypes, cb: Cb, options = false) {\n if (!this._types.has(type)) {\n this._el.addEventListener(type, cb, options)\n this._types.set(type, cb)\n }\n }\n\n private _unbind(type: SupportedTypes) {\n if (this._types.has(type)) {\n const cb = this._types.get(type)\n\n this._el.removeEventListener(type, cb)\n this._types.delete(type)\n }\n }\n\n /**\n * First step, we lock initial time and coordinates.\n */\n private _lock(e: TouchEvent | MouseEvent) {\n this._x0 = Events._unify(e).clientX\n this._t0 = Date.now()\n }\n\n /**\n * Last step, do some calculation and trigger event callbacks.\n */\n private _release(e: TouchEvent | MouseEvent) {\n const dx = Events._unify(e).clientX - this._x0\n const dt = Date.now() - this._t0\n\n // Click if…\n if (Math.abs(dx) <= this._movePrecision && dt <= this._holdThreshold) {\n this._events.has('click') && this._events.get('click')()\n this._events.has('tap') && this._events.get('tap')()\n\n return\n }\n\n // Drag if…\n if (Math.abs(dx) > this._movePrecision && dt > this._moveThreshold) {\n const direction: GestureDirection = dx >= 0 ? 'right' : 'left'\n\n this._events.has('drag') && this._events.get('drag')(direction, 'drag')\n this._events.has('swipe') && this._events.get('swipe')(direction, 'swipe')\n }\n }\n\n // eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars\n private _move(e: TouchEvent | MouseEvent) {\n // Bad code, replace with `touch-action: pan-y;`\n // e.preventDefault()\n }\n}\n","/* eslint-disable @typescript-eslint/ban-types */\nimport { HooksNames } from '../defs'\n\nexport class Hooks {\n private _callbacksByName: Map<HooksNames, Set<Function>> = new Map()\n\n public add(name: HooksNames, cb: Function): void {\n const callbacks = this._callbacksByName.has(name)\n ? this._callbacksByName.get(name)\n : new Set<Function>()\n\n callbacks.add(cb)\n this._callbacksByName.set(name, callbacks)\n }\n\n public remove(name: HooksNames, cb: Function): void {\n if (!this._callbacksByName.has(name)) {\n return\n }\n\n const callbacks = this._callbacksByName.get(name)\n\n callbacks.delete(cb)\n\n if (callbacks.size === 0) {\n this._callbacksByName.delete(name)\n } else {\n this._callbacksByName.set(name, callbacks)\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types\n public call(name: HooksNames, ctx: any, ...args: any[]): void {\n if (!this._callbacksByName.has(name)) {\n return\n }\n\n const callbacks = this._callbacksByName.get(name)\n\n callbacks.forEach(cb => {\n cb.call(ctx, ...args)\n })\n }\n}\n","import Slidy from '..'\nimport { GenericObject, Options } from '../defs'\nimport { parents, parseTpl, format } from '../utils'\n\n/**\n * Create navigation.\n *\n * @export\n * @class Nav\n */\nexport class Nav {\n /**\n * Create thumbnail.\n */\n\n private static _createThumb(slide: HTMLElement) {\n let thumb\n\n if ('slidyThumb' in slide.dataset) {\n thumb = slide.dataset.slidyThumb\n } else {\n const src = slide.querySelector('img').getAttribute('src')\n\n thumb = src.replace(/(.*)(\\.\\w{3,4}$)/, '$1_thumb$2')\n }\n\n return `<img src=\"${thumb}\">`\n }\n\n private static _createContent(content: string) {\n return `<button type=\"button\">\n <span>\n ${content}\n </span>\n </button>`\n }\n\n private _type: string\n private _template: string\n private _el: HTMLOListElement\n private _items: HTMLLIElement[]\n\n /**\n * Creates an instance of Nav.\n */\n\n constructor(private _slidy: Slidy, private _opts: Options) {\n if (!this._opts.nav) {\n return\n }\n\n // Check nav 'type' (template | thumb | number)\n const type = this._opts.nav === true ? 'number' : this._opts.nav\n\n if (/\\${(number|thumb)}/.test(type)) {\n this._type = 'template'\n this._template = type\n } else if (type === 'thumb') {\n this._type = 'thumb'\n } else if (type === 'number') {\n this._type = 'number'\n } else {\n console.error('Slidy: wrong value for \"nav\" option')\n\n return\n }\n\n this._init()\n this._bind()\n }\n\n /**\n * Destroy component.\n */\n public destroy(): void {\n this._el.parentNode.removeChild(this._el)\n\n this._slidy.hooks.remove('beforeSlide', this._clearActive)\n this._slidy.hooks.remove('beforeSlide', this._setActive)\n }\n\n /**\n * Init component.\n */\n private _init() {\n const { items, groupsMax, namespace: ns, outer } = this._slidy\n const { zerofill } = this._opts\n\n // HTML elements.\n const el = document.createElement('ol')\n\n el.classList.add(`${ns}-nav`)\n\n const html = items\n .filter((slide, i) => i % this._slidy.group === 0)\n .map((slide, i) => {\n if ('slidyNav' in slide.dataset) {\n const { slidyNav } = slide.dataset\n\n // Overrided content if data-slidy-nav attribute\n // data-slidy-nav value will replace ${number} and ${thumb}\n if (this._type === 'template') {\n return parseTpl(this._template, {\n number: slidyNav,\n thumb: slidyNav,\n })\n }\n\n return Nav._createContent(slidyNav)\n }\n\n let number\n let thumb\n\n // Check for template, thumb or number…\n const dataTpl: GenericObject = {}\n\n switch (this._type) {\n case 'template':\n // We can have both number and thumb into the template string\n // or nothing…\n if (/\\${number}/.test(this._template)) {\n dataTpl.number = format(i + 1, groupsMax, zerofill)\n }\n\n if (/\\${thumb}/.test(this._template)) {\n dataTpl.thumb = Nav._createThumb(slide)\n }\n\n return parseTpl(this._template, dataTpl)\n\n case 'thumb':\n thumb = Nav._createThumb(slide)\n\n return Nav._createContent(thumb)\n\n case 'number':\n default:\n number = format(i + 1, groupsMax, zerofill)\n\n return Nav._createContent(number)\n }\n })\n .map(content => `<li class=\"${ns}-nav__item\">${content}</li>`)\n .join('')\n\n el.innerHTML = html\n this._el = el\n outer.appendChild(this._el)\n this._items = Array.from(this._el.querySelectorAll('li'))\n\n this._setActive()\n }\n\n /**\n * Bind event handlers.\n */\n private _bind() {\n this._onClick = this._onClick.bind(this)\n this._clearActive = this._clearActive.bind(this)\n this._setActive = this._setActive.bind(this)\n\n this._slidy.hooks.add('beforeSlide', this._clearActive)\n this._slidy.hooks.add('beforeSlide', this._setActive)\n\n this._bindNav()\n }\n\n /**\n * Bind nav handlers.\n */\n private _bindNav() {\n this._el.addEventListener('click', this._onClick)\n }\n\n /**\n * Clear active nav item.\n */\n private _clearActive() {\n const currentItem = this._el.querySelector('.is-active')\n\n if (currentItem) {\n currentItem.classList.remove('is-active')\n const button = currentItem.querySelector('button')\n\n if (button) {\n button.disabled = false\n }\n }\n }\n\n /**\n * Set active nav item.\n */\n private _setActive() {\n const newItem = this._items[this._slidy.newGroup]\n\n newItem.classList.add('is-active')\n\n const button = newItem.querySelector('button')\n\n if (button) {\n button.disabled = true\n }\n }\n\n /**\n * On nav item click\n */\n private _onClick(e: MouseEvent) {\n const clicked = parents(\n e.target as HTMLElement,\n `${this._slidy.namespace}-nav__item`\n )\n\n if (clicked !== null) {\n const newGroup = Array.from(this._el.children).indexOf(clicked)\n\n this._slidy.slideTo(newGroup, 'nav')\n }\n }\n}\n","import Slidy from '..'\nimport { Options } from '../defs'\nimport { format } from '../utils'\n\n/**\n * Create a pagination.\n *\n * @export\n * @class Pagination\n */\nexport class Pagination {\n private _el: HTMLDivElement\n private _currentEl: HTMLSpanElement\n private _total: number\n\n /**\n * Creates an instance of Pagination.\n */\n constructor(private _slidy: Slidy, private _opts: Options) {\n this._init()\n this._bind()\n }\n\n /**\n * Destroy component.\n */\n public destroy(): void {\n this._el.parentNode.removeChild(this._el)\n }\n\n /**\n * Init component.\n */\n private _init() {\n const { currentGroup, groupsMax, items, namespace: ns, outer } = this._slidy\n const { length } = items\n const { pagination, zerofill } = this._opts\n\n // Text content.\n const cur = format(currentGroup + 1, length, zerofill)\n const sep = pagination === true ? '/' : (pagination as string)\n const tot = format(groupsMax, groupsMax, zerofill)\n\n // HTML elements.\n const el = document.createElement('div')\n\n el.classList.add(`${ns}-pagination`)\n\n const html = `<span class=\"${ns}-pagination__current\">${cur}</span>\n <span class=\"${ns}-pagination__separator\">${sep}</span>\n <span class=\"${ns}-pagination__total\">${tot}</span>`\n\n el.innerHTML = html\n\n this._el = el\n this._currentEl = this._el.querySelector('span:nth-child(1)')\n\n outer.appendChild(this._el)\n\n this._update()\n }\n\n /**\n * Bind event handlers.\n */\n private _bind() {\n this._slidy.hooks.add('beforeSlide', this._update.bind(this))\n }\n\n /**\n * Update current index.\n */\n private _update() {\n this._currentEl.textContent = format(\n this._slidy.newGroup + 1,\n this._total,\n this._opts.zerofill\n )\n }\n}\n","import Slidy from '..'\nimport { Direction, Action, Transition, TransitionInfos } from '../defs'\n\n/**\n * Create manager.\n */\nexport class Manager {\n private _transition: Transition['cb']\n private _isAnimating = false\n private _max: number\n private _actions: Action[]\n public shouldPrevent = false\n\n /**\n * Creates an instance of Manager.\n */\n\n constructor(private _slidy: Slidy, transition: Transition['cb']) {\n this._transition = transition\n this._isAnimating = false\n this._max = this._slidy.options.queue\n this._actions = []\n }\n\n /**\n * Add \"action\" to Manager.\n */\n public add(action: Action): void {\n // Prevent slide ?\n this._slidy.hooks.call('preventSlide', this._slidy, action, this)\n\n if (this.shouldPrevent) {\n this.shouldPrevent = false\n\n return\n }\n\n this.shouldPrevent = false\n\n if (this._actions.length > this._max) {\n this._actions.length = this._max\n }\n\n this._actions.push(action)\n\n if (!this._isAnimating) {\n this._play()\n }\n }\n\n /**\n * Empty manager.\n */\n public empty(): void {\n this._actions = []\n }\n\n /**\n * Get current items.\n * Used for transitions and CSS active styling\n */\n public getCurrentItems(currentIndex: number): HTMLElement[] {\n const { group, items } = this._slidy\n const { loop, preserveGroup } = this._slidy.options\n\n let currentItems = items.slice(currentIndex, currentIndex + group)\n\n if (currentIndex + group >= items.length && loop && preserveGroup) {\n currentItems = currentItems.concat(\n items.slice(0, group - (items.length - currentIndex))\n )\n }\n\n return currentItems\n }\n\n /**\n * Get new items.\n * Used for transitions and CSS active styling\n */\n public getNewItems(newIndex: number): HTMLElement[] {\n const { group, items } = this._slidy\n const { loop, preserveGroup } = this._slidy.options\n\n let newItems = items.slice(newIndex, newIndex + group)\n\n if (newIndex + group >= items.length && loop && preserveGroup) {\n newItems = newItems.concat(\n items.slice(0, group - (items.length - newIndex))\n )\n }\n\n return newItems\n }\n\n /**\n * Play manager.\n */\n private _play() {\n if (this._actions.length === 0) {\n return\n }\n\n const [{ move, trigger, index, animate }] = this._actions\n const infos: TransitionInfos = { trigger, animate } as TransitionInfos\n const { items } = this._slidy\n const { length } = items\n const {\n currentIndex,\n currentGroup,\n groupsMax,\n group,\n options,\n } = this._slidy\n const { loop, preserveGroup } = options\n\n let newIndex: number\n let newGroup: number\n let direction: Direction\n\n // Get the newIndex according to \"move type\"\n if (move === 'to') {\n newGroup = index\n newIndex = newGroup * group\n } else {\n if (move === 'prev') {\n newGroup = currentGroup - 1\n newIndex = currentIndex - group\n }\n\n if (move === 'next') {\n newGroup = currentGroup + 1\n newIndex = currentIndex + group\n }\n }\n\n // Manage group with loop option.\n // If no loop, current is ok…\n if (newGroup < 0) {\n newGroup = loop ? groupsMax - 1 : currentGroup\n }\n\n if (newGroup >= groupsMax) {\n newGroup = loop ? 0 : currentGroup\n }\n\n // Manage index with loop option.\n // If no loop, current is ok…\n if (newIndex < 0) {\n if (loop) {\n // Depending on `preserveGroup`…\n newIndex = preserveGroup\n ? length - Math.abs(newIndex) // Get some item at the end\n : length - (length % group) // Get the last group\n } else {\n newIndex = currentIndex\n }\n }\n\n if (newIndex >= length) {\n if (loop) {\n // Depending on `preserveGroup`…\n newIndex = preserveGroup\n ? newIndex - length // Get some item in the begining\n : 0 // Get the first group\n } else {\n newIndex = currentIndex\n }\n }\n\n // Get direction.\n if (move === 'to') {\n direction = newGroup > currentGroup ? 'next' : 'prev'\n } else {\n direction = move\n }\n\n // Add missing/calculated infos\n infos.direction = direction\n infos.currentIndex = currentIndex\n infos.newIndex = newIndex\n infos.currentGroup = currentGroup\n infos.newGroup = newGroup\n\n // If same than current -> dequeue + next.\n if (newGroup === currentGroup) {\n this._actions.shift()\n this._play()\n\n return\n }\n\n // Update slide indexes and get current/next slides.\n const currentSlides = this.getCurrentItems(currentIndex)\n const newSlides = this.getNewItems(newIndex)\n\n // Set new index.\n this._slidy.newIndex = newIndex\n this._slidy.newGroup = newGroup\n\n // Start slide.\n this._slidy.hooks.call(\n 'beforeSlide',\n this._slidy,\n infos,\n this._slidy.context,\n this._slidy.data\n )\n\n // Update status and active class.\n this._isAnimating = true\n\n currentSlides.forEach(s => {\n s.classList.remove('is-active')\n })\n\n const transition = animate ? this._transition : () => Promise.resolve()\n\n transition\n .call(\n this._slidy,\n group > 1 ? currentSlides : currentSlides[0],\n group > 1 ? newSlides : newSlides[0],\n infos,\n this._slidy.context,\n this._slidy.data\n )\n .then(() => {\n // Update indexes, manager, status and active class.\n this._slidy.currentIndex = newIndex\n this._slidy.currentGroup = newGroup\n this._actions.shift()\n this._isAnimating = false\n\n newSlides.forEach(s => {\n s.classList.add('is-active')\n })\n\n // End slide.\n this._slidy.hooks.call(\n 'afterSlide',\n this._slidy,\n infos,\n this._slidy.context,\n this._slidy.data\n )\n\n // Play next queued transition.\n this._play()\n })\n }\n}\n","/**\n * Slidy main file.\n */\n\nimport {\n Action,\n GestureDirection,\n HooksNames,\n Options,\n SupportedEvents,\n Trigger,\n} from './defs'\nimport { Controls, Events, Hooks, Nav, Pagination, Manager } from './modules'\nimport { debounce, touchevents } from './utils'\n\n/**\n * Slidy main class.\n */\nclass Slidy {\n public hooks = new Hooks()\n\n public el: HTMLElement\n public outer: HTMLDivElement\n public items: HTMLElement[]\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n public context: any\n\n public currentIndex: number\n public newIndex: number\n public itemsMax: number\n public group: number\n public currentGroup: number\n public newGroup: number\n public groupsMax: number\n\n private _currentItems: HTMLElement[]\n\n private _manager: Manager\n private _controls: Controls\n private _nav: Nav\n private _pagination: Pagination\n private _eventManager: Events\n\n private _opts: Options\n private _debounceDelay: number\n private _hasPause: boolean\n private _hasErrors: boolean\n private _destroyed: boolean\n private _t1: number\n private _t2: number\n\n constructor(\n element: HTMLElement | string,\n opts: Options,\n _context: any = null, // eslint-disable-line @typescript-eslint/no-explicit-any\n public data: any = null // eslint-disable-line @typescript-eslint/no-explicit-any\n ) {\n let el: NodeListOf<HTMLElement>\n\n // Check and get element(s).\n if (typeof element === 'string') {\n el = document.querySelectorAll(element as string)\n }\n\n if (!element || (el && el.length === 0)) {\n this._hasErrors = true\n console.error('Slidy: no element matching!')\n\n return\n }\n\n if (el && el.length > 1) {\n console.warn('Slidy: multiple elements matching!')\n }\n\n this.el = el ? el[0] : (element as HTMLElement)\n\n // Check and get options.\n this._opts = {\n auto: false, // Boolean: start slider automaticaly\n click: true, // Boolean: enable click on slider\n controls: false, // Boolean: create prev/next buttons\n debounce: 100, // Integer: debounce delay on resize\n drag: false, // Boolean: enable mouse drag\n group: () => 1, // Mixed(number | Function)\n height: 'auto', // Mixed: integer (px) or 'auto'\n index: 0, // Integer: initial index\n interval: 2000, // Integer: time between 2 transitions\n keyboard: true, // Boolean: enable/disable keyboard controls\n loop: true, // Boolean: enable/disable loop\n namespace: 'slidy', // String: custom namespace\n nav: false, // Mixed: create navigation (number, thumb, custom)\n pagination: false, // Mixed: create pagination (1[separator]10)\n pause: true, // Boolean: pause on hover\n preserveGroup: true, // Boolean: enable if group show\n queue: 1, // Integer: queue max items\n resize: true, // Boolean: enable resize event and callback\n reverse: false, // Boolean: reverse directions / controls\n swipe: false, // Boolean: enable swipe\n tap: false, // Boolean: enable tap\n transition: null, // Function: transition which returns a resolved promise\n zerofill: false, // Mixed(Boolean | number): '1' -> '01' for navigation/pagination\n ...opts,\n }\n\n if (this._opts.transition === null) {\n this._hasErrors = true\n console.error('Slidy: you should define a transition!')\n\n return\n }\n\n this.context = _context || this\n this._debounceDelay = this._opts.debounce\n this.currentIndex = this._opts.index\n this.newIndex = this.currentIndex\n this.group = Number(this._opts.group) || this._opts.group()\n this.currentGroup = Math.ceil(this.currentIndex / this.group)\n this.newGroup = this.currentGroup\n\n // TODO: fix starting at index === 1 && group === 3 && !preserveGroup\n // currentGroup === 1 but\n // 'controls/prev' does not display group#0 (but loops to the end)\n /* istanbul ignore next */\n if (this.newIndex % this.group !== 0 && !this.options.preserveGroup) {\n console.warn('Slidy: index does not match with group!')\n }\n }\n\n /**\n * Getters/setters.\n */\n get options(): Options {\n return this._opts\n }\n\n get namespace(): string {\n return this._opts.namespace\n }\n\n /**\n * Init component.\n */\n public init(): void {\n if (this._hasErrors) {\n console.error('Slidy: fix errors!')\n\n return\n }\n\n this.items = Array.from(this.el.children) as HTMLElement[]\n this.itemsMax = this.items.length\n this.groupsMax = Math.ceil(this.itemsMax / this.group)\n this._hasPause = false\n\n // Binding\n this.start = this.start.bind(this)\n this.slideNext = this.slideNext.bind(this)\n\n this.hooks.add('afterSlide', () => {\n // Accessibility\n this.el.setAttribute('aria-valuenow', `${this.currentIndex + 1}`)\n })\n\n // Start initialization\n this.hooks.call('beforeInit', this, this.el)\n\n this._manager = new Manager(this, this._opts.transition)\n\n // Set height.\n // To get the most 'correct' auto-height,\n // do it before applying anything…\n if (this._opts.height === 'auto') {\n this.reset()\n } else {\n this.el.style.height = `${this._opts.height}px`\n }\n\n // Add HTML wrapper.\n this.outer = document.createElement('div')\n this.el.parentNode.insertBefore(this.outer, this.el)\n this.outer.appendChild(this.el)\n\n // Get current items.\n this._currentItems = this._manager.getCurrentItems(this.currentIndex)\n\n // Add CSS classes.\n this.outer.classList.add(`${this.namespace}-outer`)\n this.el.classList.add(this.namespace)\n this.items.forEach(slide => {\n slide.classList.add(`${this.namespace}__item`)\n })\n // Set active class on each element of currentItems.\n this._currentItems.forEach(slide => {\n slide.classList.add('is-active')\n })\n\n // Accessibility\n this.el.setAttribute('role', 'slider')\n this.el.setAttribute('aria-valuemin', `${1}`)\n this.el.setAttribute('aria-valuemax', `${this.items.length}`)\n this.el.setAttribute('aria-valuenow', `${this.currentIndex + 1}`)\n this.el.setAttribute('aria-valuenow', `${this.currentGroup + 1}`)\n\n if (this._opts.click) {\n this.el.style.cursor = 'pointer'\n }\n\n // Add controls.\n if (this._opts.controls) {\n this._controls = new Controls(this, this._opts)\n }\n\n // Add nav.\n if (this._opts.nav) {\n this._nav = new Nav(this, this._opts)\n }\n\n // Add pagination.\n if (this._opts.pagination) {\n this._pagination = new Pagination(this, this._opts)\n }\n\n // Bind events\n this.bind()\n\n // Start auto mode.\n if (this._opts.auto) {\n this.start()\n }\n\n this.hooks.call('afterInit', this, this.el)\n }\n\n /**\n * API (hooks)\n */\n // eslint-disable-next-line @typescript-eslint/ban-types\n public on(hookName: HooksNames, cb: Function): void {\n this.hooks.add(hookName, cb)\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-types\n public off(hookName: HooksNames, cb: Function): void {\n this.hooks.remove(hookName, cb)\n }\n\n /**\n * Navigate to previous slide.\n */\n public slidePrev(trigger: Trigger, force = false): void {\n if (this._opts.reverse && !force) {\n this.slideNext(trigger, true)\n\n return\n }\n\n this.slide({ move: 'prev', trigger })\n }\n\n /**\n * Navigate to next slide.\n */\n public slideNext(trigger: Trigger, force = false): void {\n if (this._opts.reverse && !force) {\n this.slidePrev(trigger, true)\n\n return\n }\n\n this.slide({ move: 'next', trigger })\n }\n\n /**\n * Navigate to slide by index.\n */\n public slideTo(index: number, trigger: Trigger, animate = true): void {\n this.slide({ move: 'to', trigger, index, animate })\n }\n\n /**\n * Add move to the manager.\n */\n public slide(action: Action): void {\n if (this._opts.auto) {\n window.clearInterval(this._t1)\n this._t1 = window.setInterval(this.slideNext, this._opts.interval)\n }\n\n if (this._manager) {\n this._manager.add({\n index: null,\n animate: true,\n ...action,\n })\n } else {\n // Prevent 'persistent' auto\n this.stop()\n }\n }\n\n /**\n * Start autoplay.\n * Enabled via \"auto\" and used by \"pause\" options.\n */\n public start(delay = this._opts.interval, auto = this._opts.auto): void {\n this._t2 = window.setTimeout(() => {\n this.slideNext('auto')\n\n if (!this._hasPause && this._opts.pause) {\n this.outer.addEventListener('mouseenter', this._onEnter)\n }\n\n if (auto) {\n window.clearInterval(this._t1)\n this._t1 = window.setInterval(this.slideNext, this._opts.interval)\n }\n }, delay)\n }\n\n /**\n * Pause autoplay.\n * Used by \"pause\" options.\n */\n public stop(): void {\n if (this._hasPause) {\n this.outer.removeEventListener('mouseenter', this._onEnter)\n }\n window.clearTimeout(this._t2)\n window.clearInterval(this._t1)\n }\n\n /**\n * Destroy component.\n */\n public destroy(): void {\n this._destroyed = true\n\n // Remove interval.\n this.stop()\n\n // Empty manager.\n /* istanbul ignore else */\n if (this._manager) {\n this._manager.empty()\n delete this._manager\n }\n\n // Remove listeners.\n /* istanbul ignore else */\n if (this._opts.resize) {\n window.removeEventListener('resize', this.onResize)\n }\n this.el.removeEventListener('mouseenter', this._onEnter)\n this.el.removeEventListener('mouseleave', this._onLeave)\n this.el.removeEventListener('click', this._onClick)\n\n this.items.forEach(s => {\n s.classList.remove('is-active')\n })\n\n // Remove event manager.\n /* istanbul ignore else */\n if (this._eventManager) {\n this._eventManager.destroy()\n delete this._eventManager\n }\n\n // Remove controls.\n /* istanbul ignore else */\n if (this._controls) {\n this._controls.destroy()\n delete this._controls\n }\n\n // Remove nav.\n /* istanbul ignore else */\n if (this._nav) {\n this._nav.destroy()\n delete this._nav\n }\n\n // Remove pagination.\n /* istanbul ignore else */\n if (this._pagination) {\n this._pagination.destroy()\n delete this._pagination\n }\n\n // Remove HTML wrapper.\n this.outer.before(this.el)\n /* istanbul ignore else */\n if (this.outer.parentNode) {\n this.outer.parentNode.removeChild(this.outer)\n }\n\n // Remove CSS classes.\n this.el.classList.remove(this.namespace)\n this.items.forEach(slide => {\n slide.classList.remove(`${this.namespace}__item`)\n slide.removeAttribute('style')\n Array.from(slide.children).forEach(child => {\n child.removeAttribute('style')\n })\n })\n\n this.el.removeAttribute('style')\n\n // Removeccessibility\n this.el.removeAttribute('role')\n this.el.removeAttribute('aria-valuemin')\n this.el.removeAttribute('aria-valuemax')\n this.el.removeAttribute('aria-valuenow')\n this.el.removeAttribute('aria-valuenow')\n }\n\n /**\n * Bind event handlers.\n */\n private bind() {\n this._onEnter = this._onEnter.bind(this)\n this._onLeave = this._onLeave.bind(this)\n this._onClick = this._onClick.bind(this)\n this._onTap = this._onTap.bind(this)\n this._onMove = this._onMove.bind(this)\n // // this.onDrag = this.onDrag.bind(this)\n\n if (this._opts.resize) {\n this.onResize = debounce(this.onResize, this._debounceDelay).bind(this)\n\n window.addEventListener('resize', this.onResize)\n }\n\n if (this._opts.pause && this._opts.auto) {\n this.outer.addEventListener('mouseenter', this._onEnter)\n this._hasPause = true\n }\n\n // Events binding\n this._eventManager = new Events(this.el)\n\n if (touchevents()) {\n this._opts.tap && this._eventManager.on('tap', this._onTap)\n this._opts.swipe && this._eventManager.on('swipe', this._onMove)\n } else {\n this._opts.click && this._eventManager.on('click', this._onClick)\n this._opts.drag && this._eventManager.on('drag', this._onMove)\n }\n }\n\n /**\n * Height calculation if auto.\n */\n private reset() {\n if (this._opts.height === 'auto') {\n // Reset inline height.\n this.el.style.height = ''\n\n // Check if items have height\n // if not, check first node\n // then remove 0 height, sort DESC, get the highest height.\n const getMinHeight = (arr: number[]) =>\n arr\n .filter(item => item > 0)\n .sort((a, b) => b - a)\n .slice(0, 1)\n\n const heights: number[] = []\n const hasNoHeight = this.items[0].offsetHeight === 0\n\n this.items.forEach(item => {\n if (hasNoHeight && item.firstElementChild) {\n heights.push((item.firstElementChild as HTMLElement).offsetHeight)\n } else {\n heights.push(item.offsetHeight)\n }\n })\n this.el.style.height = `${getMinHeight(heights)}px`\n }\n }\n\n /**\n * RWD reset.\n * Mainly for height… but also for \"responsive\" groups\n */\n private onResize() {\n if (!this._destroyed) {\n this.reset()\n\n if (!Number(this._opts.group)) {\n this.group = this._opts.group()\n }\n\n this.hooks.call('afterResize', this, this.el)\n }\n }\n\n /**\n * Click on slider to go to the next slide.\n * Enabled via \"click\" option.\n */\n private _onClick() {\n this.slideNext('click')\n }\n\n /**\n * Same as click but for touch devices.\n * Enabled via \"tap\" option.\n */\n private _onTap() {\n this.slideNext('tap')\n }\n\n /**\n * Complement gesture for horizontal mouse drag or swipe.\n * Enabled via \"drag\" option.\n */\n private _onMove(direction: GestureDirection, type: SupportedEvents) {\n if (direction === 'right') {\n this.slidePrev(type as Trigger)\n }\n if (direction === 'left') {\n this.slideNext(type as Trigger)\n }\n }\n\n /**\n * Play/pause on hover.\n * Enabled via \"auto\" + \"pause\" options.\n */\n private _onEnter() {\n this.outer.removeEventListener('mouseenter', this._onEnter)\n this.stop()\n this.outer.addEventListener('mouseleave', this._onLeave)\n }\n\n private _onLeave() {\n this.outer.removeEventListener('mouseleave', this._onLeave)\n this.start()\n this.outer.addEventListener('mouseenter', this._onEnter)\n }\n}\n\nexport default Slidy\nexport * from './defs'\n"],"names":["format","number","nbItems","zerofill","String","width","fill","toString","length","Array","join","zeroFill","parseTpl","template","map","fallback","replace","match","path","substr","trim","obj","fb","split","reduce","res","key","Controls","_slidy","_opts","this","_init","_bind","disable","el","setAttribute","classList","add","enable","removeAttribute","remove","destroy","_el","document","querySelector","parentNode","removeChild","hooks","_update","ns","namespace","outer","controls","prev","next","label","createElement","innerHTML","_prev","_next","appendChild","_prevClick","bind","_nextClick","_bindControls","keyboard","addEventListener","event","keyCode","_this","slidePrev","slideNext","loop","newIndex","items","group","Events","Map","_lock","_release","_move","on","name","cb","eventIn","eventOut","eventMove","_events","set","_hasMouseListener","_defaultType","_pointerType","_hasTouchListener","style","touchAction","forEach","_types","type","_unbind","_unify","e","changedTouches","lo","toLowerCase","navigator","msPointerEnabled","window","PointerEvent","options","has","get","removeEventListener","_x0","clientX","_t0","Date","now","dx","dt","Math","abs","_movePrecision","_holdThreshold","_moveThreshold","direction","Hooks","callbacks","_callbacksByName","Set","size","call","ctx","Nav","nav","test","_type","_template","console","error","_createThumb","slide","dataset","slidyThumb","getAttribute","_createContent","content","_clearActive","_setActive","groupsMax","html","filter","i","slidyNav","thumb","dataTpl","_items","from","querySelectorAll","_onClick","_bindNav","currentItem","button","disabled","newItem","newGroup","clicked","element","className","contains","parents","target","children","indexOf","slideTo","Pagination","pagination","cur","currentGroup","sep","tot","_currentEl","textContent","_total","Manager","transition","_transition","_isAnimating","_max","queue","_actions","action","shouldPrevent","push","_play","empty","getCurrentItems","currentIndex","preserveGroup","currentItems","slice","concat","getNewItems","newItems","move","animate","infos","trigger","index","shift","currentSlides","newSlides","context","data","s","Promise","resolve","then","Slidy","opts","_context","_hasErrors","warn","auto","click","debounce","drag","height","interval","pause","resize","reverse","swipe","tap","_debounceDelay","Number","ceil","init","itemsMax","_hasPause","start","_manager","reset","insertBefore","_currentItems","cursor","_controls","_nav","_pagination","hookName","off","force","clearInterval","_t1","setInterval","stop","delay","_t2","setTimeout","_this2","_onEnter","clearTimeout","_destroyed","onResize","_onLeave","_eventManager","before","_this3","child","func","wait","timeout","_onTap","_onMove","later","apply","heights","hasNoHeight","offsetHeight","item","firstElementChild","sort","a","b"],"mappings":"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