UNPKG

vue-ssr-carousel

Version:

A performance focused Vue carousel designed for SSR/SSG environments.

1,443 lines (1,303 loc) 87.6 kB
module.exports = /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; // ESM COMPAT FLAG __webpack_require__.r(__webpack_exports__); // CONCATENATED MODULE: ./node_modules/vue-loader/lib/loaders/templateLoader.js??ref--5!./node_modules/pug-plain-loader!./node_modules/vue-loader/lib??vue-loader-options!./src/ssr-carousel.vue?vue&type=template&id=df66d814&lang=pug& var render = function render() { var _vm = this, _c = _vm._self._c return _vm.$slots.default && _vm.$slots.default.length ? _c( "div", { key: _vm.$slots.default.length, staticClass: "ssr-carousel", attrs: { "data-ssrc-id": _vm.scopeId }, on: { keyup: function ($event) { if ( !$event.type.indexOf("key") && _vm._k($event.keyCode, "tab", 9, $event.key, "Tab") ) return null return _vm.onTab.apply(null, arguments) }, }, }, [ _c("style", { tag: "component", domProps: { innerHTML: _vm._s(_vm.instanceStyles) }, }), _c( "div", { staticClass: "ssr-carousel-slides" }, [ _c("div", { ref: "peekValues", staticClass: "ssr-peek-values", style: _vm.peekStyles, }), _c( "div", _vm._g( { ref: "mask", staticClass: "ssr-carousel-mask", class: { pressing: _vm.pressing, disabled: _vm.disabled, "no-mask": _vm.overflowVisible, "not-draggable": _vm.noDrag, }, }, _vm.maskListeners ), [ _c( "ssr-carousel-track", _vm._b( { ref: "track", scopedSlots: _vm._u( [ { key: "default", fn: function () { return [_vm._t("default")] }, proxy: true, }, _vm.hasPeekClones ? { key: "clones", fn: function () { return [_vm._t("default")] }, proxy: true, } : null, ], null, true ), }, "ssr-carousel-track", { dragging: _vm.dragging, trackTranslateX: _vm.trackTranslateX, slideOrder: _vm.slideOrder, activeSlides: _vm.activeSlides, leftPeekingSlideIndex: _vm.leftPeekingSlideIndex, rightPeekingSlideIndex: _vm.rightPeekingSlideIndex, rtl: _vm.rtl, dimensionsKnown: _vm.dimensionsKnown, }, false ) ), ], 1 ), _vm.showArrows ? _c( "ssr-carousel-arrows", _vm._b( { on: { back: _vm.back, next: _vm.next }, scopedSlots: _vm._u( [ { key: "back", fn: function (props) { return [_vm._t("back-arrow", null, null, props)] }, }, { key: "next", fn: function (props) { return [_vm._t("next-arrow", null, null, props)] }, }, ], null, true ), }, "ssr-carousel-arrows", { index: _vm.index, pages: _vm.pages, shouldLoop: _vm.shouldLoop, pageLabel: _vm.pageLabel, rtl: _vm.rtl, }, false ) ) : _vm._e(), ], 1 ), _vm.showDots ? _c( "ssr-carousel-dots", _vm._b( { on: { goto: _vm.gotoDot }, scopedSlots: _vm._u( [ { key: "dot", fn: function (props) { return [_vm._t("dot", null, null, props)] }, }, ], null, true ), }, "ssr-carousel-dots", { boundedIndex: _vm.boundedIndex, pages: _vm.pages, pageLabel: _vm.pageLabel, rtl: _vm.rtl, }, false ) ) : _vm._e(), _c( "div", { staticClass: "ssr-carousel-visually-hidden", attrs: { "aria-live": "polite", "aria-atomic": "true" }, }, [_vm._v(_vm._s(_vm.currentSlideMessage))] ), ], 1 ) : _vm._e() } var staticRenderFns = [] render._withStripped = true // CONCATENATED MODULE: ./src/ssr-carousel.vue?vue&type=template&id=df66d814&lang=pug& // CONCATENATED MODULE: ./node_modules/vue-loader/lib/loaders/templateLoader.js??ref--5!./node_modules/pug-plain-loader!./node_modules/vue-loader/lib??vue-loader-options!./src/ssr-carousel-arrows.vue?vue&type=template&id=df7e1608&lang=pug& var ssr_carousel_arrowsvue_type_template_id_df7e1608_lang_pug_render = function render() { var _vm = this, _c = _vm._self._c return _c("div", { staticClass: "ssr-carousel-arrows" }, [ _c( "button", { staticClass: "ssr-carousel-left-button", class: _vm.rtl ? "ssr-carousel-next-button" : "ssr-carousel-back-button", attrs: { "aria-label": _vm.rtl ? _vm.nextLabel : _vm.backLabel, "aria-disabled": _vm.leftDisabled, }, on: { click: function ($event) { return _vm.$emit("back") }, }, }, [ _vm._t( _vm.rtl ? "next" : "back", function () { return [_c("span", { staticClass: "ssr-carousel-left-icon" })] }, { disabled: _vm.leftDisabled } ), ], 2 ), _c( "button", { staticClass: "ssr-carousel-right-button", class: _vm.rtl ? "ssr-carousel-back-button" : "ssr-carousel-next-button", attrs: { "aria-label": _vm.rtl ? _vm.backLabel : _vm.nextLabel, "aria-disabled": _vm.rightDisabled, }, on: { click: function ($event) { return _vm.$emit("next") }, }, }, [ _vm._t( _vm.rtl ? "back" : "next", function () { return [_c("span", { staticClass: "ssr-carousel-right-icon" })] }, { disabled: _vm.rightDisabled } ), ], 2 ), ]) } var ssr_carousel_arrowsvue_type_template_id_df7e1608_lang_pug_staticRenderFns = [] ssr_carousel_arrowsvue_type_template_id_df7e1608_lang_pug_render._withStripped = true // CONCATENATED MODULE: ./src/ssr-carousel-arrows.vue?vue&type=template&id=df7e1608&lang=pug& // CONCATENATED MODULE: ./node_modules/babel-loader/lib!./node_modules/coffee-loader!./node_modules/vue-loader/lib??vue-loader-options!./src/ssr-carousel-arrows.vue?vue&type=script&lang=coffee& /* harmony default export */ var ssr_carousel_arrowsvue_type_script_lang_coffee_ = ({ props: { index: Number, pages: Number, shouldLoop: Boolean, pageLabel: String, rtl: Boolean }, computed: { // Make the labels backLabel: function () { return `Previous ${this.pageLabel}`; }, nextLabel: function () { return `Next ${this.pageLabel}`; }, // Determine if button should be disabled because we're at the limits leftDisabled: function () { if (!this.shouldLoop) { return this.index === 0; } }, rightDisabled: function () { if (!this.shouldLoop) { return this.index === this.pages - 1; } } } }); // CONCATENATED MODULE: ./src/ssr-carousel-arrows.vue?vue&type=script&lang=coffee& /* harmony default export */ var src_ssr_carousel_arrowsvue_type_script_lang_coffee_ = (ssr_carousel_arrowsvue_type_script_lang_coffee_); // CONCATENATED MODULE: ./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src!./node_modules/stylus-loader!./node_modules/vue-loader/lib??vue-loader-options!./src/ssr-carousel-arrows.vue?vue&type=style&index=0&id=df7e1608&prod&lang=stylus& // extracted by mini-css-extract-plugin // CONCATENATED MODULE: ./src/ssr-carousel-arrows.vue?vue&type=style&index=0&id=df7e1608&prod&lang=stylus& // CONCATENATED MODULE: ./node_modules/vue-loader/lib/runtime/componentNormalizer.js /* globals __VUE_SSR_CONTEXT__ */ // IMPORTANT: Do NOT use ES2015 features in this file (except for modules). // This module is a runtime utility for cleaner component module output and will // be included in the final webpack user bundle. function normalizeComponent( scriptExports, render, staticRenderFns, functionalTemplate, injectStyles, scopeId, moduleIdentifier /* server only */, shadowMode /* vue-cli only */ ) { // Vue.extend constructor export interop var options = typeof scriptExports === 'function' ? scriptExports.options : scriptExports // render functions if (render) { options.render = render options.staticRenderFns = staticRenderFns options._compiled = true } // functional template if (functionalTemplate) { options.functional = true } // scopedId if (scopeId) { options._scopeId = 'data-v-' + scopeId } var hook if (moduleIdentifier) { // server build hook = function (context) { // 2.3 injection context = context || // cached call (this.$vnode && this.$vnode.ssrContext) || // stateful (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional // 2.2 with runInNewContext: true if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { context = __VUE_SSR_CONTEXT__ } // inject component styles if (injectStyles) { injectStyles.call(this, context) } // register component module identifier for async chunk inferrence if (context && context._registeredComponents) { context._registeredComponents.add(moduleIdentifier) } } // used by ssr in case component is cached and beforeCreate // never gets called options._ssrRegister = hook } else if (injectStyles) { hook = shadowMode ? function () { injectStyles.call( this, (options.functional ? this.parent : this).$root.$options.shadowRoot ) } : injectStyles } if (hook) { if (options.functional) { // for template-only hot-reload because in that case the render fn doesn't // go through the normalizer options._injectStyles = hook // register for functional component in vue file var originalRender = options.render options.render = function renderWithStyleInjection(h, context) { hook.call(context) return originalRender(h, context) } } else { // inject component registration as beforeCreate hook var existing = options.beforeCreate options.beforeCreate = existing ? [].concat(existing, hook) : [hook] } } return { exports: scriptExports, options: options } } // CONCATENATED MODULE: ./src/ssr-carousel-arrows.vue /* normalize component */ var component = normalizeComponent( src_ssr_carousel_arrowsvue_type_script_lang_coffee_, ssr_carousel_arrowsvue_type_template_id_df7e1608_lang_pug_render, ssr_carousel_arrowsvue_type_template_id_df7e1608_lang_pug_staticRenderFns, false, null, null, null ) /* harmony default export */ var ssr_carousel_arrows = (component.exports); // CONCATENATED MODULE: ./node_modules/vue-loader/lib/loaders/templateLoader.js??ref--5!./node_modules/pug-plain-loader!./node_modules/vue-loader/lib??vue-loader-options!./src/ssr-carousel-dots.vue?vue&type=template&id=09426fe1&lang=pug& var ssr_carousel_dotsvue_type_template_id_09426fe1_lang_pug_render = function render() { var _vm = this, _c = _vm._self._c return _c( "div", { staticClass: "ssr-carousel-dots" }, _vm._l(_vm.pages, function (i) { return _c( "button", { key: i, staticClass: "ssr-carousel-dot-button", attrs: { "aria-label": _vm.makeLabel(i), "aria-disabled": _vm.isDisabled(i), }, on: { click: function ($event) { return _vm.$emit("goto", i - 1) }, }, }, [ _vm._t( "dot", function () { return [_c("span", { staticClass: "ssr-carousel-dot-icon" })] }, { index: i, disabled: _vm.isDisabled(i) } ), ], 2 ) }), 0 ) } var ssr_carousel_dotsvue_type_template_id_09426fe1_lang_pug_staticRenderFns = [] ssr_carousel_dotsvue_type_template_id_09426fe1_lang_pug_render._withStripped = true // CONCATENATED MODULE: ./src/ssr-carousel-dots.vue?vue&type=template&id=09426fe1&lang=pug& // CONCATENATED MODULE: ./node_modules/babel-loader/lib!./node_modules/coffee-loader!./node_modules/vue-loader/lib??vue-loader-options!./src/ssr-carousel-dots.vue?vue&type=script&lang=coffee& /* harmony default export */ var ssr_carousel_dotsvue_type_script_lang_coffee_ = ({ props: { boundedIndex: Number, pages: Number, pageLabel: String, rtl: Boolean }, methods: { // Make the label for the dot makeLabel: function (index) { var pageNumber; pageNumber = this.rtl ? this.pages - index + 1 : index; return `Go to ${this.pageLabel} ${pageNumber}`; }, // Check if dot index shuold be disabled isDisabled: function (index) { return this.boundedIndex === index - 1; } } }); // CONCATENATED MODULE: ./src/ssr-carousel-dots.vue?vue&type=script&lang=coffee& /* harmony default export */ var src_ssr_carousel_dotsvue_type_script_lang_coffee_ = (ssr_carousel_dotsvue_type_script_lang_coffee_); // CONCATENATED MODULE: ./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src!./node_modules/stylus-loader!./node_modules/vue-loader/lib??vue-loader-options!./src/ssr-carousel-dots.vue?vue&type=style&index=0&id=09426fe1&prod&lang=stylus& // extracted by mini-css-extract-plugin // CONCATENATED MODULE: ./src/ssr-carousel-dots.vue?vue&type=style&index=0&id=09426fe1&prod&lang=stylus& // CONCATENATED MODULE: ./src/ssr-carousel-dots.vue /* normalize component */ var ssr_carousel_dots_component = normalizeComponent( src_ssr_carousel_dotsvue_type_script_lang_coffee_, ssr_carousel_dotsvue_type_template_id_09426fe1_lang_pug_render, ssr_carousel_dotsvue_type_template_id_09426fe1_lang_pug_staticRenderFns, false, null, null, null ) /* harmony default export */ var ssr_carousel_dots = (ssr_carousel_dots_component.exports); // CONCATENATED MODULE: ./node_modules/babel-loader/lib!./node_modules/coffee-loader!./node_modules/vue-loader/lib??vue-loader-options!./src/ssr-carousel-track.vue?vue&type=script&lang=coffee& var interactiveSelector, indexOf = [].indexOf; interactiveSelector = 'a, button, input, textarea, select'; /* harmony default export */ var ssr_carousel_trackvue_type_script_lang_coffee_ = ({ props: { dragging: Boolean, trackTranslateX: Number, slideOrder: Array, activeSlides: Array, leftPeekingSlideIndex: Number, rightPeekingSlideIndex: Number, rtl: Boolean, dimensionsKnown: Number }, data: function () { return { // Should the track element be an ul renderAsList: false, // Should the track element be a tablist renderAsTablist: false }; }, // Set tabindex of inactive slides on mount mounted: function () { this.denyTabindex(this.inactiveSlides); return this.denyTabindex(this.clonedSlides); }, computed: { // The HTML element of the track trackHTMLElement: function () { if (this.renderAsList) { return 'ul'; } else { return 'div'; } }, // Get the count of non-cloned slides uniqueSlidesCount: function () { return this.slideOrder.length; }, // Get the total slides count, including clones allSlidesCount: function () { return this.getSlideComponents().length; }, // Check if there are cloned slides hasClonedSlides: function () { return this.allSlidesCount > this.uniqueSlidesCount; }, // Make an array of inactive slide indices inactiveSlides: function () { var ref; return function () { var results = []; for (var j = 0, ref = this.uniqueSlidesCount; 0 <= ref ? j < ref : j > ref; 0 <= ref ? j++ : j--) { results.push(j); } return results; }.apply(this).filter(index => { return indexOf.call(this.activeSlides, index) < 0; }); }, // An array of the cloned slides indices clonedSlides: function () { var ref, ref1; return function () { var results = []; for (var j = ref = this.uniqueSlidesCount, ref1 = this.allSlidesCount; ref <= ref1 ? j < ref1 : j > ref1; ref <= ref1 ? j++ : j--) { results.push(j); } return results; }.apply(this); }, // Styles that are used to position the track styles: function () { if (this.trackTranslateX) { return { transform: `translateX(${this.trackTranslateX}px)` }; } } }, // Update the tabindex of interactive elements when slides change watch: { activeSlides: function () { this.allowTabindex(this.activeSlides); return this.denyTabindex(this.inactiveSlides); } }, methods: { // Make the slides to render into the track makeSlides: function () { return this.getSlideComponents().map((vnode, index) => { var cssClass, isPeekingClone, peekingIndex, ref, ref1, slideCount; vnode = this.makeReactiveVnode(vnode); if (index === 0 && vnode.tag === 'li') { // Check if we are rendering a list of elements this.renderAsList = true; } if (index === 0 && (vnode != null ? (ref = vnode.data) != null ? (ref1 = ref.attrs) != null ? ref1.role : void 0 : void 0 : void 0) === 'tab') { // Check if we are rendering a tablist this.renderAsTablist = true; } // This is a peeking clone if it's index is greater than the slide count slideCount = this.uniqueSlidesCount; isPeekingClone = index >= slideCount; peekingIndex = index - slideCount; // Add the slide class using staticClass since it isn't reactive to data cssClass = 'ssr-carousel-slide'; if (vnode.data.staticClass) { vnode.data.staticClass += ` ${cssClass}`; } else { vnode.data.staticClass = cssClass; } // Order the slide, like for looping if (!isPeekingClone) { vnode.data.style.order = this.slideOrder[index] || 0; } else { // Or put at the beginning / end if peeking vnode.data.style.order = function () { switch (false) { case peekingIndex !== this.leftPeekingSlideIndex: return '-1'; case peekingIndex !== this.rightPeekingSlideIndex: return this.slideOrder.length; } }.call(this); } // Hide cloned slides that aren't involved in peeking if (isPeekingClone && peekingIndex !== this.leftPeekingSlideIndex && peekingIndex !== this.rightPeekingSlideIndex) { vnode.data.style.display = 'none'; } // Make peeking clones and slides not in viewport as aria-hidden if (isPeekingClone || indexOf.call(this.activeSlides, index) < 0) { vnode.data.attrs['aria-hidden'] = 'true'; } // Prevent duplicate keys on clones if (isPeekingClone && vnode.key != null) { vnode.key += '-clone-' + index; } // Return modified vnode return vnode; }); }, // Get the list of non-text slides, including peeking clones. This doesn't // work as a computed function getSlideComponents: function () { var slides; slides = [...(this.$slots.default || []), ...(this.$slots.clones || [])].filter(function (vnode) { return !vnode.text; }); // Reverses the slide if rtl and if the dimensions are known. This // second condition exists to prevent the reversal from happening on SSR. // Which is important because this logic is paired with setting the // intial index to the last page which can't be known until the slide // width is known. if (this.rtl && this.dimensionsKnown) { slides = slides.reverse(); } return slides; }, // Makes a clone of the vnode properties we'll be updating so the changes // get rendered. Based on: // https://github.com/vuejs/vue/issues/6052#issuecomment-313705168 makeReactiveVnode: function (vnode) { var newVnode; if (!vnode.data) { // Expect a data object. When it doesn't exist, it's a sign this this // vnode can't be manipulated vue-ssr-carousel. console.error("vnode has no data", vnode); } // Make the new vnode and data newVnode = { ...vnode }; newVnode.data = { ...vnode.data }; // Clone style property. String styles will be on staticStyle so we can // ignore them. newVnode.data.style = { ...vnode.data.style }; // Clone attrs property newVnode.data.attrs = { ...vnode.data.attrs }; // Return the clone return newVnode; }, // Prevent tabbing to interactive elements in slides with the passed in // index values denyTabindex: function (indices) { return this.setTabindex(indices, -1); }, // Allow tabindex on interactive elements in slides with the passed in // index values allowTabindex: function (indices) { return this.setTabindex(indices, 0); }, // Set tabindex value on interactive elements in slides setTabindex: function (slideIndices, tabindexValue) { var el, j, len, ref, results; ref = this.getSlideElementsByIndices(slideIndices); results = []; for (j = 0, len = ref.length; j < len; j++) { el = ref[j]; // Set tabindex value on the slide, like in the case that the slide is // an <a> if (el.matches(interactiveSelector)) { el.tabIndex = tabindexValue; } // Set tabindex values on all interactive children results.push(el.querySelectorAll(interactiveSelector).forEach(function (el) { return el.tabIndex = tabindexValue; })); } return results; }, // Get the slide elements that match the array of indices getSlideElementsByIndices: function (slideIndices) { return Array.from(this.$el.children).filter(function (el, i) { return indexOf.call(slideIndices, i) >= 0; }); } }, // Render the track and slotted slides render: function (create) { return create(this.trackHTMLElement, { attrs: { role: this.renderAsTablist ? "tablist" : void 0 }, style: this.styles, class: ['ssr-carousel-track', { dragging: this.dragging, "ssr-carousel-rtl": this.rtl }] }, this.makeSlides()); } }); // CONCATENATED MODULE: ./src/ssr-carousel-track.vue?vue&type=script&lang=coffee& /* harmony default export */ var src_ssr_carousel_trackvue_type_script_lang_coffee_ = (ssr_carousel_trackvue_type_script_lang_coffee_); // CONCATENATED MODULE: ./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src!./node_modules/stylus-loader!./node_modules/vue-loader/lib??vue-loader-options!./src/ssr-carousel-track.vue?vue&type=style&index=0&id=9d2ba830&prod&lang=stylus& // extracted by mini-css-extract-plugin // CONCATENATED MODULE: ./src/ssr-carousel-track.vue?vue&type=style&index=0&id=9d2ba830&prod&lang=stylus& // CONCATENATED MODULE: ./src/ssr-carousel-track.vue var ssr_carousel_track_render, ssr_carousel_track_staticRenderFns /* normalize component */ var ssr_carousel_track_component = normalizeComponent( src_ssr_carousel_trackvue_type_script_lang_coffee_, ssr_carousel_track_render, ssr_carousel_track_staticRenderFns, false, null, null, null ) /* harmony default export */ var ssr_carousel_track = (ssr_carousel_track_component.exports); // CONCATENATED MODULE: ./src/concerns/accessibility.coffee /* Code related to supporting keyboard interaction and screen readers */ /* harmony default export */ var accessibility_coffee = ({ // The label to use for pagination props: { paginationLabel: String }, // Store whether the user appears to be using keyboard to navigate data: function () { return { usingKeyboard: false }; }, computed: { // Determine the descriptor to use in aria messages pageLabel: function () { switch (false) { case !this.paginationLabel: return this.paginationLabel; case !this.paginateBySlide: return "Slide"; default: return "Page"; } }, // Make the current slide message // https://www.w3.org/WAI/tutorials/carousels/functionality/#announce-the-current-item currentSlideMessage: function () { return `${this.pageLabel} ${this.boundedIndex + 1} of ${this.pages}`; } }, watch: { // When switching to keyboard navigation, I could never reproduce a scenario // where the focused elements wasn't the first slide, so I'm resetting the // active page to the first slide usingKeyboard: function () { if (this.usingKeyboard) { return this.goto(0); } } }, methods: { // Once a user uses tab on the carousel, mark them as using their keyboard. // This is cleared by the onPointerDown method. onTab: function () { return this.usingKeyboard = true; } } }); // CONCATENATED MODULE: ./src/concerns/autoplay.coffee /* Code related to auotplay features of the carousel */ /* harmony default export */ var autoplay_coffee = ({ props: { // A delay provided in seconds for the autoplay. 0 is disabled autoplayDelay: { type: Number, default: 0 }, // Should we pause on hover pauseOnFocus: { type: Boolean, default: true } }, // Start autolaying on mount mounted: function () { return this.autoplayStart(); }, beforeDestroy: function () { return this.autoplayStop(); }, computed: { // Conditions that result in pausing autoplay autoplayPaused: function () { switch (false) { // Always pause when using keyboard navigation case !this.usingKeyboard: return true; // Stop animation if window is hidden or if carousel is focused case !this.pauseOnFocus: return this.windowHidden || this.isFocused; } } }, watch: { // Respond to conditions that may automatically pause autoplaying autoplayPaused: function (paused) { if (paused) { return this.autoplayStop(); } else { return this.autoplayStart(); } } }, methods: { autoplayStart: function () { // Require a delay amount if (!this.autoplayDelay) { return; } // Don't loop if we only have one page if (!this.pages) { return; } // Start autoplaying return this.autoPlayInterval = setInterval(() => { if (!this.autoplayPaused) { return this.autoplayNext(); } }, this.autoplayDelay * 1000); }, autoplayStop: function () { return clearInterval(this.autoPlayInterval); }, // Advance to the next slide autoplayNext: function () { if (this.shouldLoop || this.index < this.pages - 1) { return this.next(); } else { return this.goto(0); // Reset because loop wasn't enabled } } } }); // CONCATENATED MODULE: ./src/concerns/dimensions.coffee /* Code related to measuring the size of the carousel after mounting */ /* harmony default export */ var dimensions_coffee = ({ data: function () { return { viewportWidth: null, // Width of the viewport, for media query calculation carouselWidth: null, // Width of a page of the carousel gutterWidth: 0 // Computed width of gutters, since they support css vars }; }, // Add resize listening mounted: function () { var ref; this.onResize(); // Resize observer listens for the element itself to change dimensions if (((ref = this.$el) != null ? ref.nodeType : void 0) === Node.ELEMENT_NODE) { this.resizeObserver = new ResizeObserver(this.onResize); return this.resizeObserver.observe(this.$el); } }, beforeDestroy: function () { var ref; return (ref = this.resizeObserver) != null ? ref.disconnect() : void 0; }, computed: { // The width of a page of slides, which may be less than the carouselWidth // if there is peeking. This includes the affect of gutters. pageWidth: function () { return this.carouselWidth - this.combinedPeek; }, // Calculate the width of a slide based on client side measured pageWidth // rather than measuring it explicitly in the DOM. This value includes the // gutter. slideWidth: function () { return this.pageWidth / this.currentSlidesPerPage; }, // Calculate the width of the whole track from the slideWidth. trackWidth: function () { if (this.isVariableWidth) { return this.measuredTrackWidth + this.gutterWidth; } else { return this.slideWidth * this.slidesCount; } }, // Figure out the width of the last page, which may not have enough slides // to fill it. lastPageWidth: function () { var slidesOnLastPage, slidesPerPage, width; // Determine how many slides are on the final page of pagination. If the // remainder was 0, that means the page is flush with slides, so swap // the 0 for the max amount. slidesPerPage = this.currentSlidesPerPage; slidesOnLastPage = this.slidesCount % slidesPerPage; if (slidesOnLastPage === 0) { slidesOnLastPage = slidesPerPage; } // Turn the slide count into a width value width = slidesOnLastPage * this.slideWidth; return width; }, // The ending x value, only used when not looping. The peeking values in // here result in the final page using the left peeking value and the // actualy peeking appearing to apply to the left. The +1 is to fix subpixel // rounding issues. endX: function () { if (this.disabled) { return 0; } else { return this.pageWidth - this.trackWidth - this.peekLeftPx + this.peekRightPx + 1; } }, // Check if the drag is currently out bounds isOutOfBounds: function () { return this.currentX > 0 || this.currentX < this.endX; }, // Helper for things that are triggered once dimensions are known so // they can be more specific about their dependencies dimensionsKnown: function () { return this.carouselWidth && this.viewportWidth; } }, methods: { // Measure the component width for various calculations. Using // getBoundingClientRect so we can get fractional values. We also need // the width of the gutter since that's effectively part of the page. onResize: function () { var firstSlide, ref; if (((ref = this.$el) != null ? ref.nodeType : void 0) !== Node.ELEMENT_NODE) { return; } if (!(firstSlide = this.$refs.track.$el.firstElementChild)) { return; } this.gutterWidth = parseInt(getComputedStyle(firstSlide).marginRight); this.carouselWidth = this.$el.getBoundingClientRect().width + this.gutterWidth; this.viewportWidth = window.innerWidth; this.capturePeekingMeasurements(); if (this.isVariableWidth) { return this.captureCarouselDims(); } }, // Make the width style that gives a slide it's width given // slidesPerPage. Reduce this width by the gutter if present makeBreakpointSlideWidthStyle: function (breakpoint) { if (this.isVariableWidth) { return; } return `${this.scopeSelector} .ssr-carousel-slide { width: ${this.makeSlideWidthCalc(breakpoint)}; }`; }, // Build the calc string which makes a percentage width for a slide and // reduces it by combined peeking and gutter influence. The computed // style this produces should have an equal value to the `slideWidth` // computed property which is client side JS dependent. makeSlideWidthCalc: function (breakpoint) { var gutter, isDisabled, peekLeft, peekRight, slidesPerPage; isDisabled = this.isDisabledAtBreakpoint(breakpoint); slidesPerPage = this.getResponsiveValue('slidesPerPage', breakpoint); gutter = this.getResponsiveValue('gutter', breakpoint); // A common use case when not looping is to have a larger peek on just the // right. But when disabled, this looks strange. So this balances out // the peeking in the disbaled state. peekLeft = this.getResponsiveValue('peekLeft', breakpoint); if (this.matchPeekWhenDisabled && isDisabled) { peekRight = peekLeft; } else { peekRight = this.getResponsiveValue('peekRight', breakpoint); } // Render the styles return `calc( ${100 / slidesPerPage}% - (${this.autoUnit(peekLeft)} + ${this.autoUnit(peekRight)}) / ${slidesPerPage} - (${this.autoUnit(gutter)} * ${slidesPerPage - 1}) / ${slidesPerPage} )`; }, // Get the target X scroll position of a given slide targetXOfIdx: function (idx) { if (this.isVariableWidth) { return this.measuredSlidesWidths[idx].targetXScroll; } else { return this.pageWidth / this.currentSlidesPerPage; } } } }); // CONCATENATED MODULE: ./src/concerns/dragging.coffee /* Code related to handling dragging of the track */ var notPassive, passive; passive = { passive: true }; notPassive = { passive: false }; /* harmony default export */ var dragging_coffee = ({ props: { // Boundary drag dampening modifier. Increase to allow greater travel outside // the boundaries. boundaryDampening: { type: Number, default: 0.6 }, // The percentage of a pageWidth that was dragged before we advance to // another page on slide dragAdvanceRatio: { type: Number, default: .33 }, // The ratio of X:Y mouse travel. Decrease this number to allow for greater // y dragging before the drag is cancelled. verticalDragTreshold: { type: Number, default: 1 }, // Turn off draggability noDrag: Boolean }, data: function () { return { pressing: false, // The user pressing pointer down dragging: false, // The user has translated while pointer was down isTouchDrag: false, // Is the browser firing touch events startPointer: null, // Where was the mouse when the drag started lastPointer: null, // Where was the mouse on the last move event dragVelocity: null, // The px/tick while dragging, negative is rightward dragDirectionRatio: null // The ratio of horizontal vs vertical dragging }; }, // Cleanup listeners beforeDestroy: function () { window.removeEventListener('mousemove', this.onPointerMove, passive); window.removeEventListener('mouseup', this.onPointerUp, passive); window.removeEventListener('touchmove', this.onPointerMove, passive); window.removeEventListener('touchend', this.onPointerUp, passive); window.removeEventListener('touchmove', this.onWinMove, notPassive); window.removeEventListener('contextmenu', this.onPointerUp, passive); return window.removeEventListener('touchmove', this.stopEvent, notPassive); }, computed: { // The current slide or page index. It rounds differently depending on the // direction of the velocity. So that it eases to a stop in the direction // the user was dragging. dragIndex: function () { switch (false) { // If there is very little velocity, go to the closet page case !(Math.abs(this.dragVelocity) <= 2): return Math.round(this.fractionalIndex); // User was moving forward case !(this.dragVelocity < 0): return Math.ceil(this.fractionalIndex); default: // User was moving backward return Math.floor(this.fractionalIndex); } }, // Determine the current index given the currentX as a fraction. For // instance, when dragging forward, it will be like 0.1 and when you've // dragged almost a full page, forward it would be 0.9. This got // complicated because the final page may not have a full compliment of // slides like if we have 2 per page and 3 slides. When you have tweened // to the 2nd page, the fractionalIndex should be 2 even though you // haven't traveled the same width as it took to get from 1 to 2. fractionalIndex: function () { var distanceIntoPage, isLastPage, pageIndex, pageProgressPercent, pageWidth, remainingSlides, setIndex, slidesPerPage, widthDivisor, x; if (!this.trackWidth) { return 0; } if (this.isVariableWidth) { return this.fractionalIndexFromMeasurements; } // Work in positive numbers x = this.currentX * -1; // Figure out what set we're in, like if, through looping, we've gone // through all the pages multiple times. setIndex = Math.floor(x / this.trackWidth); // Figure out the index of last page of the set that has been fully // scrolled into. Not using modulo for this because I got rounding errors. widthDivisor = this.paginateBySlide ? this.slideWidth : this.pageWidth; pageIndex = Math.floor((x - setIndex * this.trackWidth) / widthDivisor); // Figure out the progress into the current page distanceIntoPage = x - setIndex * this.trackWidth - pageIndex * widthDivisor; // Determine if we're on the last page. If we're not looping, an extra // "page" of slides is treated as part of the last page because of how we // end with the slides flush with the right edge. slidesPerPage = this.currentSlidesPerPage; remainingSlides = function () { switch (false) { case !this.shouldLoop: return this.slidesCount - pageIndex * slidesPerPage; default: return this.slidesCount - (pageIndex + 1) * slidesPerPage; } }.call(this); isLastPage = remainingSlides <= slidesPerPage; // Make a percentage of travel into the page pageWidth = isLastPage ? this.lastPageWidth : widthDivisor; pageProgressPercent = distanceIntoPage / pageWidth; // Return the final value by adding all the passed index values return pageProgressPercent + setIndex * this.pages + pageIndex; }, fractionalIndexFromMeasurements: function () { var percentage, slideIdx, x; // Work in positive numbers x = this.currentX * -1; slideIdx = this.measuredSlidesWidths.findIndex(measuredSlide => { return measuredSlide.targetXScroll > x; }) - 1; percentage = (x - this.measuredSlidesWidths[slideIdx].targetXScroll) / this.measuredSlidesWidths[slideIdx].width; return slideIdx + percentage; }, // Determine if the user is dragging vertically isVerticalDrag: function () { if (!this.dragDirectionRatio) { return; } return this.dragDirectionRatio < this.verticalDragTreshold; }, // If we're horiztonally swiping on a touch device, prevent vertical scroll preventVerticalScroll: function () { return this.pressing && this.isTouchDrag && !this.isVerticalDrag; } }, watch: { // Watch for mouse move changes when the user starts dragging pressing: function () { var moveEvent, upEvent; // Determine the type of event [moveEvent, upEvent] = this.isTouchDrag ? ['touchmove', 'touchend'] : ['mousemove', 'mouseup']; // Pointer is down, start watching for drags if (this.pressing) { window.addEventListener(moveEvent, this.onPointerMove, passive); window.addEventListener(upEvent, this.onPointerUp, passive); window.addEventListener('contextmenu', this.onPointerUp, passive); this.dragVelocity = 0; // Reset any previous velocity this.preventContentDrag(); this.stopTweening(); } else { // Tween so the track is in bounds if it was out // The pointer is up, so tween to final position if (this.isOutOfBounds && !this.shouldLoop) { if (this.currentX >= 0) { this.gotoStart(); } else { this.gotoEnd(); } // If rendering variable width slides, don't come to a rest at an index } else if (this.isVariableWidth) { this.goto(this.dragIndex); // If user was vertically dragging, reset the index } else if (this.isVerticalDrag) { this.goto(this.index); } else { // Handle normal swiping this.goto(this.dragIndex); } // Cleanup vars and listeners window.removeEventListener(moveEvent, this.onPointerMove, passive); window.removeEventListener(upEvent, this.onPointerUp, passive); window.removeEventListener('contextmenu', this.onPointerUp, passive); this.dragging = false; this.startPointer = this.lastPointer = this.dragDirectionRatio = null; } // Fire events if (this.pressing) { return this.$emit('press'); } else { return this.$emit('release'); } }, // Fire events related to dragging dragging: function () { if (this.dragging) { return this.$emit('drag:start'); } else { return this.$emit('drag:end'); } }, // If the user is dragging vertically, end the drag based on the assumption // that the user is attempting to scroll the page via touch rather than // pan the carousel. isVerticalDrag: function () { if (!(this.isVerticalDrag && this.isTouchDrag)) { return; } return this.pressing = false; }, // Stop vertical scrolling by listening for touchmove events on the body // and cancel them. Need to explicitly set pasive because some mobile // browsers set to true by default. preventVerticalScroll: function (shouldPrevent) { if (shouldPrevent) { return window.addEventListener('touchmove', this.stopEvent, notPassive); } else { return window.removeEventListener('touchmove', this.stopEvent, notPassive); } } }, methods: { // Cancel an Event stopEvent: function (e) { if (e.cancelable) { return e.preventDefault(); } }, // Keep track of whether user is dragging onPointerDown: function (pointerEvent) { this.isTouchDrag = typeof TouchEvent !== "undefined" && TouchEvent !== null && pointerEvent instanceof TouchEvent; this.startPointer = this.lastPointer = this.getPointerCoords(pointerEvent); this.pressing = true; return this.usingKeyboard = false; }, // Keep track of release of press onPointerUp: function () { return this.pressing = false; }, // Keep x values up to date while dragging onPointerMove: function (pointerEvent) { var pointer; if (!this.dragging) { // Mark the carousel as dragging, which is used to disable clicks this.dragging = true; } // Calculated how much drag has happened since the list move pointer = this.getPointerCoords(pointerEvent); this.dragVelocity = pointer.x - this.lastPointer.x; this.targetX += this.dragVelocity; this.lastPointer = pointer; // Caculate the drag direction ratio this.dragDirectionRatio = Math.abs((pointer.x - this.startPointer.x) / (pointer.y - this.startPointer.y)); // Update the track position return this.currentX = this.applyBoundaryDampening(this.targetX); }, // Helper to