import { throttle } from '@opentiny/utils'
import { getDirection } from '@opentiny/utils'
import { POSITION } from '@opentiny/utils'
import { emulate } from '@opentiny/utils'
export const handleMouseEnter =
({ api, state }) =>
() => {
state.hover = true
api.pauseTimer()
}
export const handleMouseLeave =
({ api, state }) =>
() => {
state.hover = false
api.startTimer()
}
export const itemInStage =
(state) =>
({ item, index }) => {
const length = state.items.length
const arr = state.items
if (
(index === length - 1 && item.inStage && arr[0].active) ||
(item.inStage && arr[index + 1] && arr[index + 1].active)
) {
return POSITION.Left
} else if (
(index === 0 && item.inStage && arr[length - 1].active) ||
(item.inStage && arr[index - 1] && arr[index - 1].active)
) {
return POSITION.Right
}
return false
}
export const handleButtonEnter =
({ api, state }) =>
(arrow) => {
state.items.forEach((item, index) => {
if (arrow === api.itemInStage({ item, index })) {
item.hover = true
}
})
}
export const handleButtonLeave = (state) => () => {
state.items.forEach((item) => {
item.hover = false
})
}
export const resetItemPosition = (state) => (oldIndex) => {
if (state.completed) {
state.items.forEach((item, index) => {
item.translateItem({ activeIndex: state.activeIndex, index, oldIndex })
})
}
}
export const playSlides =
({ api, props, state }) =>
() => {
let newIndex
let oldIndex = state.activeIndex
if (state.activeIndex < state.items.length - 1) {
newIndex = state.activeIndex + 1
} else if (props.loop) {
newIndex = 0
}
api.canActive(newIndex, oldIndex).then((result) => {
if (result) {
state.activeIndex = newIndex
}
})
}
export const pauseTimer = (state) => () => clearInterval(state.timer)
export const startTimer =
({ api, props, state }) =>
() => {
if (props.interval <= 0 || !props.autoplay) {
return
}
state.timer = setInterval(api.playSlides, props.interval)
}
export const setActiveItem =
({ api, props, state }) =>
(index) => {
if (typeof index === 'string') {
const filteredItems = state.items.filter((item) => item.name === index)
if (filteredItems.length > 0) {
index = state.items.indexOf(filteredItems[0])
}
}
index = Number(index)
if (isNaN(index) || index !== Math.floor(index)) {
return
}
const length = state.items.length
const oldIndex = state.activeIndex
let newIndex
if (index < 0) {
newIndex = props.loop ? length - 1 : 0
} else if (index >= length) {
newIndex = props.loop ? 0 : length - 1
} else {
newIndex = index
}
const nextProcess = () => {
state.activeIndex = newIndex
if (oldIndex === state.activeIndex) {
api.resetItemPosition(oldIndex)
}
}
if (newIndex === oldIndex) {
nextProcess()
} else {
api.canActive(newIndex, oldIndex).then((result) => {
if (result) {
nextProcess()
}
})
}
}
export const canActive = (props) => (newIndex, oldIndex) => {
return new Promise((resolve) => {
if (typeof props.beforeSwipe === 'function') {
resolve(props.beforeSwipe(newIndex, oldIndex))
} else {
resolve(true)
}
}).then((result) => result !== false)
}
export const prev =
({ api, state }) =>
() =>
api.setActiveItem(state.activeIndex - 1)
export const next =
({ api, state }) =>
() =>
api.setActiveItem(state.activeIndex + 1)
export const handleIndicatorClick =
({ api, state }) =>
(index) => {
api.canActive(index, state.activeIndex).then((result) => {
if (result) {
state.activeIndex = index
}
})
}
export const handleIndicatorHover =
({ api, props, state }) =>
(index) => {
if (props.trigger === 'hover' && index !== state.activeIndex) {
api.canActive(index, state.activeIndex).then((result) => {
if (result) {
state.activeIndex = index
}
})
}
}
export const watchItems =
({ props, api }) =>
(value) => {
if (value.length) {
api.setActiveItem(props.initialIndex)
}
}
export const watchActiveIndex =
({ emit, api }) =>
({ value, oldValue }) => {
api.resetItemPosition(oldValue)
emit('change', value, oldValue)
}
export const watchAutoplay = (api) => (value) => (value ? api.startTimer() : api.pauseTimer())
export const throttledArrowClick = (api) => throttle(300, true, (index) => api.setActiveItem(index))
export const throttledIndicatorHover = (api) => throttle(300, (index) => api.handleIndicatorHover(index))
export const computedHasLabel = (items) => items.some((item) => item.label.toString().length > 0)
export const computedStyle =
({ props }) =>
() => {
if (props.height) {
return { 'height': props.height }
} else {
if (CSS.supports('aspect-ratio', 'auto')) {
return { 'aspect-ratio': props.aspectRatio.replace(':', ' / ') }
} else {
const ratio = props.aspectRatio.split(':')
const paddingTop = ((ratio[1] / ratio[0]) * 100).toFixed(2) + '%'
return { 'width': '100%', 'height': 0, 'padding-top': paddingTop }
}
}
}
export const onComplete =
({ api, count, emit, props, state }) =>
(total) => {
if (count++ === total) {
state.completed = true
if (props.initialIndex < state.items.length && props.initialIndex >= 0) {
api.canActive(props.initialIndex, state.activeIndex).then((result) => {
if (result) {
state.activeIndex = props.initialIndex
}
})
}
emit('complete')
}
}
export const touchstart =
({ props, state, api }) =>
(event) => {
if (state.items.length <= 1 || ~state.noTouchNode.indexOf(event.target.nodeName)) return
if (!props.swipeable) {
event.preventDefault()
event.stopPropagation()
}
resetTouchStatus(state)
api.pauseTimer()
state.itemsTranslate = state.items.map((item) => item.state.translate)
state.moving = true
state.touchTime = Date.now()
state.startPos.X = event.touches[0].clientX
state.startPos.Y = event.touches[0].clientY
}
export const touchmove =
({ props, state, vm }) =>
(event) => {
if (state.items.length <= 1 || ~state.noTouchNode.indexOf(event.target.nodeName)) return
const touch = event.touches[0]
const itemsLen = state.items.length
const carousel = vm.$refs.carousel
state.deltaPos.X = touch.clientX - state.startPos.X
state.deltaPos.Y = touch.clientY - state.startPos.Y
state.offsetPos.X = Math.abs(state.deltaPos.X)
state.offsetPos.Y = Math.abs(state.deltaPos.Y)
state.direction = state.direction || getDirection(state.offsetPos.X, state.offsetPos.Y)
state.isCorrectDirection = state.direction === props.type
if (!state.isCorrectDirection) return
state.size = state.direction === 'horizontal' ? carousel.offsetWidth : carousel.offsetHeight
const nextIndex = state.activeIndex === itemsLen - 1 ? 0 : state.activeIndex + 1
const prevIndex = state.activeIndex === 0 ? itemsLen - 1 : state.activeIndex - 1
state.delta = state.direction === 'horizontal' ? state.deltaPos.X : state.deltaPos.Y
state.moveDisable =
!props.loop &&
((state.activeIndex === 0 && state.delta > 0) ||
(state.activeIndex === state.items.length - 1 && state.delta < 0))
if (state.moveDisable) return
state.items[state.activeIndex].setDelta(state.delta)
state.items[nextIndex].setDelta(state.delta)
if (itemsLen > 2) {
state.items[prevIndex].setDelta(state.delta)
}
}
export const touchend =
({ state, api }) =>
(event) => {
if (state.moveDisable || state.items.length <= 1 || ~state.noTouchNode.indexOf(event.target.nodeName)) return
const speed = state.delta / (Date.now() - state.touchTime)
const isShouldMove = Math.abs(speed) > 0.3 || Math.abs(state.delta) > +(state.size / 2).toFixed(2)
state.moving = false
state.itemsTranslate.forEach((item, index) => {
state.items[index].setDelta(0)
})
if (isShouldMove && state.isCorrectDirection) {
state.delta < 0 ? api.next() : api.prev()
} else if (Math.abs(state.delta) > 1) {
state.items.forEach((item) => {
item.resetAnimatingMf()
})
}
api.startTimer()
}
function resetTouchStatus(state) {
state.direction = ''
state.delta = 0
state.deltaPos.X = 0
state.deltaPos.Y = 0
state.offsetPos.X = 0
state.offsetPos.Y = 0
}
export const simulateTouch =
({ props, vm }) =>
() => {
if (props.swipeable && vm.$refs.carousel) {
emulate()
vm.$refs.carousel.setAttribute('data-tiny-touch-simulate-container', '')
}
}
export const computedHasButtons =
({ props, state, mode }) =>
() => {
if (props.lite) {
return false
} else if (mode === 'mobile-first') {
return props.arrow !== 'never' && state.items.length > 1
} else if (mode === 'pc') {
return props.arrow !== 'never'
}
}
export const computedHasIndicators =
({ props, state, mode }) =>
() => {
if (props.lite) {
return false
} else if (mode === 'mobile-first') {
return props.indicatorPosition !== 'none' && state.items.length > 1
} else if (mode === 'pc') {
return props.indicatorPosition !== 'none'
}
}