@ishitatsuyuki/oruga-next
Version:
UI components for Vue.js and CSS framework agnostic
548 lines (538 loc) • 19.4 kB
JavaScript
'use strict';
var vue = require('vue');
var helpers = require('./helpers.js');
var config = require('./config.js');
var BaseComponentMixin = require('./BaseComponentMixin-a03c02e3.js');
var Icon = require('./Icon-172f9998.js');
var InjectedChildMixin = require('./InjectedChildMixin-5f524fc3.js');
/**
* A Slideshow for cycling images in confined spaces
* @displayName Carousel
* @style _carousel.scss
*/
var script = vue.defineComponent({
name: 'OCarousel',
components: {
[Icon.script.name]: Icon.script
},
configField: 'carousel',
mixins: [InjectedChildMixin.ProviderParentMixin('carousel', InjectedChildMixin.Sorted), BaseComponentMixin.BaseComponentMixin],
emits: ['update:modelValue', 'scroll', 'click'],
props: {
modelValue: {
type: Number,
default: 0
},
interval: {
type: Number,
default: () => { return helpers.getValueByPath(config.getOptions(), 'carousel.interval', 3500); }
},
hasDrag: {
type: Boolean,
default: true
},
autoplay: {
type: Boolean,
default: false
},
pauseHover: {
type: Boolean,
default: false
},
repeat: {
type: Boolean,
default: false
},
indicator: {
type: Boolean,
default: true
},
indicatorInside: {
type: Boolean,
default: false
},
indicatorMode: {
type: String,
default: 'click'
},
indicatorPosition: {
type: String,
default: 'bottom'
},
indicatorStyle: {
type: String,
default: 'dots'
},
overlay: Boolean,
itemsToShow: {
type: Number,
default: 1
},
itemsToList: {
type: Number,
default: 1
},
asIndicator: Boolean,
arrow: {
type: Boolean,
default: true
},
arrowHover: {
type: Boolean,
default: true
},
iconPack: String,
iconSize: String,
iconPrev: {
type: String,
default: () => { return helpers.getValueByPath(config.getOptions(), 'carousel.iconPrev', 'chevron-left'); }
},
iconNext: {
type: String,
default: () => { return helpers.getValueByPath(config.getOptions(), 'carousel.iconNext', 'chevron-right'); }
},
breakpoints: {
type: Object,
default: () => ({})
},
rootClass: [String, Function, Array],
overlayClass: [String, Function, Array],
sceneClass: [String, Function, Array],
itemsClass: [String, Function, Array],
itemsDraggingClass: [String, Function, Array],
arrowIconClass: [String, Function, Array],
arrowIconPrevClass: [String, Function, Array],
arrowIconNextClass: [String, Function, Array],
indicatorsClass: [String, Function, Array],
indicatorsInsideClass: [String, Function, Array],
indicatorsInsidePositionClass: [String, Function, Array],
indicatorItemClass: [String, Function, Array],
indicatorItemActiveClass: [String, Function, Array],
indicatorItemStyleClass: [String, Function, Array]
},
data() {
return {
activeIndex: this.modelValue,
scrollIndex: this.modelValue,
delta: 0,
dragX: false,
hold: 0,
windowWidth: 0,
touch: false,
observer: null,
refresh_: 0,
itemsHovered: false,
isPause: false,
timer: null
};
},
computed: {
rootClasses() {
return [
this.computedClass('rootClass', 'o-car'),
{ [this.computedClass('overlayClass', 'o-car__overlay')]: this.overlay }
];
},
sceneClasses() {
return [
this.computedClass('sceneClass', 'o-car__scene')
];
},
itemsClasses() {
return [
this.computedClass('itemsClass', 'o-car__items'),
{ [this.computedClass('itemsDraggingClass', 'o-car__items--dragging')]: this.dragging },
];
},
arrowIconClasses() {
return [
this.computedClass('arrowIconClass', 'o-car__arrow__icon'),
];
},
arrowIconPrevClasses() {
return [
...this.arrowIconClasses,
this.computedClass('arrowIconPrevClass', 'o-car__arrow__icon-prev')
];
},
arrowIconNextClasses() {
return [
...this.arrowIconClasses,
this.computedClass('arrowIconNextClass', 'o-car__arrow__icon-next')
];
},
indicatorsClasses() {
return [
this.computedClass('indicatorsClass', 'o-car__indicators'),
{ [this.computedClass('indicatorsInsideClass', 'o-car__indicators--inside')]: this.indicatorInside },
{ [this.computedClass('indicatorsInsidePositionClass', 'o-car__indicators--inside--', this.indicatorPosition)]: this.indicatorInside && this.indicatorPosition }
];
},
indicatorClasses() {
return [
this.computedClass('indicatorClass', 'o-car__indicator')
];
},
dragging() {
return this.dragX !== false;
},
itemStyle() {
return `width: ${this.itemWidth}px;`;
},
translation() {
return -helpers.bound(this.delta + (this.scrollIndex * this.itemWidth), 0, (this.childItems.length - this.settings.itemsToShow) * this.itemWidth);
},
total() {
return this.childItems.length - this.settings.itemsToShow;
},
indicatorCount() {
return Math.ceil(this.total / this.settings.itemsToList) + 1;
},
indicatorIndex() {
return Math.ceil(this.scrollIndex / this.settings.itemsToList);
},
hasArrows() {
return (this.settings.arrowHover && this.itemsHovered) || !this.settings.arrowHover;
},
hasPrev() {
return (this.settings.repeat || this.scrollIndex > 0) && this.hasArrows;
},
hasNext() {
return (this.settings.repeat || this.scrollIndex < this.total) && this.hasArrows;
},
breakpointKeys() {
const keys = Object.keys(this.breakpoints).map(Number);
return keys.sort((a, b) => b - a);
},
settings() {
let breakpoint = this.breakpointKeys.filter((breakpoint) => {
if (this.windowWidth >= breakpoint) {
return true;
}
})[0];
if (breakpoint) {
return { ...this.$props, ...this.breakpoints[breakpoint] };
}
return this.$props;
},
itemWidth() {
if (this.windowWidth) { // Ensure component is mounted
/* eslint-disable-next-line */
this.refresh_; // We force the computed property to refresh if this prop is changed
const rect = this.$el.getBoundingClientRect();
return rect.width / this.settings.itemsToShow;
}
return 0;
}
},
watch: {
/**
* When v-model is changed set the new active item.
*/
modelValue(value) {
if (value <= this.childItems.length - 1) {
this.activeIndex = value;
this.switchTo(value * this.settings.itemsToList, true);
}
},
/**
* When autoplay is changed, start or pause timer accordingly
*/
autoplay(status) {
if (status) {
this.startTimer();
}
else {
this.pauseTimer();
}
},
/**
* Since the timer can get paused at the end, if repeat is changed we need to restart it
*/
repeat(status) {
if (status) {
this.startTimer();
}
}
},
methods: {
indicatorItemClasses(index) {
return [
this.computedClass('indicatorItemClass', 'o-car__indicator__item'),
{ [this.computedClass('indicatorItemActiveClass', 'o-car__indicator__item--active')]: this.indicatorIndex === index },
{ [this.computedClass('indicatorItemStyleClass', 'o-car__indicator__item--', this.indicatorStyle)]: this.indicatorStyle },
];
},
onMouseEnter() {
this.itemsHovered = true;
this.checkPause();
},
onMouseLeave() {
this.itemsHovered = false;
this.startTimer();
},
startTimer() {
if (!this.autoplay || this.timer)
return;
this.isPause = false;
this.timer = setInterval(() => {
if (!this.repeat && this.activeIndex >= this.childItems.length - 1) {
this.pauseTimer();
}
else {
this.next();
}
}, this.interval);
},
pauseTimer() {
this.isPause = true;
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
},
restartTimer() {
this.pauseTimer();
this.startTimer();
},
checkPause() {
if (this.pauseHover && this.autoplay) {
this.pauseTimer();
}
},
modeChange(trigger, value) {
if (this.indicatorMode === trigger) {
return this.switchTo(value * this.settings.itemsToList);
}
},
resized() {
this.windowWidth = window.innerWidth;
},
switchTo(newIndex, onlyMove = this.asIndicator) {
if (this.settings.repeat) {
newIndex = helpers.mod(newIndex, this.total + 1);
}
newIndex = helpers.bound(newIndex, 0, this.total);
this.scrollIndex = newIndex;
this.$emit('scroll', this.indicatorIndex);
if (!onlyMove) {
this.activeIndex = Math.ceil(newIndex / this.settings.itemsToList);
if (this.modelValue !== this.activeIndex) {
this.$emit('update:modelValue', this.activeIndex);
}
}
},
next() {
this.switchTo(this.scrollIndex + this.settings.itemsToList);
},
prev() {
this.switchTo(this.scrollIndex - this.settings.itemsToList);
},
// handle drag event
dragStart(event) {
if (this.dragging || !this.settings.hasDrag || (event.button !== 0 && event.type !== 'touchstart'))
return;
this.hold = Date.now();
this.touch = !!event.touches;
this.dragX = this.touch ? event.touches[0].clientX : event.clientX;
if (this.touch) {
this.pauseTimer();
}
window.addEventListener(this.touch ? 'touchmove' : 'mousemove', this.dragMove);
window.addEventListener(this.touch ? 'touchend' : 'mouseup', this.dragEnd);
},
dragMove(event) {
if (!this.dragging)
return;
const dragEndX = event.touches
? (event.changedTouches[0] || event.touches[0]).clientX : event.clientX;
this.delta = this.dragX - dragEndX;
if (!event.touches) {
event.preventDefault();
}
},
dragEnd(event) {
if (!this.dragging && !this.hold)
return;
if (this.hold) {
const signCheck = helpers.sign(this.delta);
const results = Math.round(Math.abs(this.delta / this.itemWidth) + 0.15); // Hack
this.switchTo(this.scrollIndex + signCheck * results);
}
this.delta = 0;
this.dragX = false;
if (event && event.touches) {
this.startTimer();
}
window.removeEventListener(this.touch ? 'touchmove' : 'mousemove', this.dragMove);
window.removeEventListener(this.touch ? 'touchend' : 'mouseup', this.dragEnd);
},
refresh() {
this.$nextTick(() => {
this.refresh_++;
});
}
},
mounted() {
if (typeof window !== 'undefined') {
if (window.ResizeObserver) {
this.observer = new window.ResizeObserver(this.refresh);
this.observer.observe(this.$el);
}
window.addEventListener('resize', this.resized);
document.addEventListener('animationend', this.refresh);
document.addEventListener('transitionend', this.refresh);
document.addEventListener('transitionstart', this.refresh);
this.resized();
this.startTimer();
}
if (this.$attrs.config) {
throw new Error('The config prop was removed, you need to use v-bind instead');
}
},
beforeUnmount() {
if (typeof window !== 'undefined') {
if (window.ResizeObserver) {
this.observer.disconnect();
}
window.removeEventListener('resize', this.resized);
document.removeEventListener('animationend', this.refresh);
document.removeEventListener('transitionend', this.refresh);
document.removeEventListener('transitionstart', this.refresh);
this.dragEnd();
this.pauseTimer();
}
}
});
function render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_o_icon = vue.resolveComponent("o-icon");
return vue.openBlock(), vue.createBlock("div", {
class: _ctx.rootClasses,
onMouseenter: _cache[3] || (_cache[3] = (...args) => _ctx.onMouseEnter(...args)),
onMouseleave: _cache[4] || (_cache[4] = (...args) => _ctx.onMouseLeave(...args))
}, [vue.createVNode("div", {
class: _ctx.sceneClasses
}, [vue.createVNode("div", {
onMousedown: _cache[1] || (_cache[1] = vue.withModifiers((...args) => _ctx.dragStart(...args), ["prevent"])),
onTouchstart: _cache[2] || (_cache[2] = (...args) => _ctx.dragStart(...args)),
class: _ctx.itemsClasses,
style: 'transform:translateX(' + _ctx.translation + 'px)'
}, [vue.renderSlot(_ctx.$slots, "default")], 38
/* CLASS, STYLE, HYDRATE_EVENTS */
), vue.renderSlot(_ctx.$slots, "arrow", {
hasPrev: _ctx.hasPrev,
prev: _ctx.prev,
hasNext: _ctx.hasNext,
next: _ctx.next
}, () => [_ctx.arrow ? (vue.openBlock(), vue.createBlock(vue.Fragment, {
key: 0
}, [vue.withDirectives(vue.createVNode(_component_o_icon, {
class: _ctx.arrowIconPrevClasses,
onClick: _ctx.prev,
pack: _ctx.iconPack,
icon: _ctx.iconPrev,
size: _ctx.iconSize,
both: ""
}, null, 8
/* PROPS */
, ["class", "onClick", "pack", "icon", "size"]), [[vue.vShow, _ctx.hasPrev]]), vue.withDirectives(vue.createVNode(_component_o_icon, {
class: _ctx.arrowIconNextClasses,
onClick: _ctx.next,
pack: _ctx.iconPack,
icon: _ctx.iconNext,
size: _ctx.iconSize,
both: ""
}, null, 8
/* PROPS */
, ["class", "onClick", "pack", "icon", "size"]), [[vue.vShow, _ctx.hasNext]])], 64
/* STABLE_FRAGMENT */
)) : vue.createCommentVNode("v-if", true)])], 2
/* CLASS */
), vue.renderSlot(_ctx.$slots, "indicators", {
active: _ctx.activeIndex,
switchTo: _ctx.switchTo,
indicatorIndex: _ctx.indicatorIndex
}, () => [_ctx.childItems.length ? (vue.openBlock(), vue.createBlock(vue.Fragment, {
key: 0
}, [_ctx.indicator && !_ctx.asIndicator ? (vue.openBlock(), vue.createBlock("div", {
key: 0,
class: _ctx.indicatorsClasses
}, [(vue.openBlock(true), vue.createBlock(vue.Fragment, null, vue.renderList(_ctx.indicatorCount, (_, index) => {
return vue.openBlock(), vue.createBlock("a", {
class: _ctx.indicatorClasses,
onMouseover: $event => _ctx.modeChange('hover', index),
onClick: $event => _ctx.modeChange('click', index),
key: index
}, [vue.renderSlot(_ctx.$slots, "indicator", {
i: index
}, () => [vue.createVNode("span", {
class: _ctx.indicatorItemClasses(index)
}, null, 2
/* CLASS */
)])], 42
/* CLASS, PROPS, HYDRATE_EVENTS */
, ["onMouseover", "onClick"]);
}), 128
/* KEYED_FRAGMENT */
))], 2
/* CLASS */
)) : vue.createCommentVNode("v-if", true)], 64
/* STABLE_FRAGMENT */
)) : vue.createCommentVNode("v-if", true)]), _ctx.overlay ? vue.renderSlot(_ctx.$slots, "overlay", {
key: 0
}) : vue.createCommentVNode("v-if", true)], 34
/* CLASS, HYDRATE_EVENTS */
);
}
script.render = render;
script.__file = "src/components/carousel/Carousel.vue";
/**
* @displayName Carousel Item
*/
var script$1 = vue.defineComponent({
name: 'OCarouselItem',
configField: 'carousel',
mixins: [InjectedChildMixin.InjectedChildMixin('carousel', InjectedChildMixin.Sorted$1), BaseComponentMixin.BaseComponentMixin],
props: {
itemClass: [String, Function, Array],
itemActiveClass: [String, Function, Array]
},
computed: {
itemClasses() {
return [
this.computedClass('itemClass', 'o-car__item'),
{ [this.computedClass('itemActiveClass', 'o-car__item--active')]: this.isActive }
];
},
itemStyle() {
return `width: ${this.parent.itemWidth}px;`;
},
isActive() {
return this.parent.activeIndex === this.index;
}
},
methods: {
onClick(event) {
if (this.isActive) {
this.parent.$emit('click', event);
}
if (this.parent.asIndicator) {
this.parent.activeIndex = this.index;
this.parent.$emit('update:modelValue', this.index);
}
}
}
});
function render$1(_ctx, _cache, $props, $setup, $data, $options) {
return vue.openBlock(), vue.createBlock("div", {
class: _ctx.itemClasses,
onClick: _cache[1] || (_cache[1] = (...args) => _ctx.onClick(...args)),
style: _ctx.itemStyle
}, [vue.renderSlot(_ctx.$slots, "default")], 6
/* CLASS, STYLE */
);
}
script$1.render = render$1;
script$1.__file = "src/components/carousel/CarouselItem.vue";
exports.script = script;
exports.script$1 = script$1;