vue-ssr-carousel
Version:
A performance focused Vue carousel designed for SSR/SSG environments.
1,443 lines (1,303 loc) • 87.6 kB
JavaScript
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