UNPKG

vuestic-ui

Version:
1 lines 8.05 kB
{"version":3,"file":"useSwipe.mjs","sources":["../../../../src/composables/useSwipe.ts"],"sourcesContent":["import { ref, reactive, watch, PropType, ShallowRef, ExtractPropTypes, watchEffect } from 'vue'\n\nimport { useEvent } from './'\n\ntype SwipeDirection = 'up' | 'down' | 'left' | 'right' | ''\ntype SwipePosition = 'start' | 'end'\ntype SwipeCoordinates = Record<'x' | 'y', number>\ntype SwipePath = Record<SwipePosition, SwipeCoordinates>\nexport type SwipeState = { direction: SwipeDirection, duration: number }\n\ntype AllowedSwipeDirection = 'all' | 'horizontal' | 'vertical' | SwipeDirection\n\nconst mouseEvents = ['mousedown', 'mousemove']\nconst touchEvents = ['touchstart', 'touchmove']\n\nconst commonAllowedDirections = {\n vertical: ['', 'all', 'vertical'],\n horizontal: ['', 'all', 'horizontal'],\n}\nconst verticalSpecificAllowedDirections = [...commonAllowedDirections.vertical, 'up', 'down']\nconst horizontalSpecificAllowedDirections = [...commonAllowedDirections.horizontal, 'left', 'right']\n\n/**\n * swipable - enables swiping.\n * swipeDistance - distance in px considered sufficient for the swipe event.\n * swipeDirection - allowed and handled swipe directions.\n */\nexport const useSwipeProps = {\n swipable: { type: Boolean, default: false },\n swipeDistance: { type: Number, default: 75 },\n swipeDirection: { type: String as PropType<AllowedSwipeDirection>, default: 'all' },\n}\n\n/**\n * @description composable for handling swipes direction via mouse or touchpad.\n * @param props - use swipe props.\n * @param container - swipable container shallow ref.\n * @param cb - callback for every swipe event.\n * @example\n * props: { ...useSwipeProps }\n * const container = shallowRef<HTMLElement>()\n * const onSwipe = () => { if(swipeState === 'left') { local component's logic } }\n * const { swipeState } = useSwipe(props, container, onSwipe)\n */\nexport const useSwipe = (\n props: ExtractPropTypes<typeof useSwipeProps>,\n container: ShallowRef<HTMLElement | undefined>,\n cb: (state: SwipeState) => void,\n) => {\n const swipeStarted = ref(false)\n const swipePath = reactive({\n start: { x: 0, y: 0 },\n end: { x: 0, y: 0 },\n }) as SwipePath\n const swipeDuration = reactive({\n start: 0,\n end: 0,\n })\n\n const setState = (e: TouchEvent | MouseEvent, type: SwipePosition) => {\n let event: MouseEvent | Touch | undefined\n if (mouseEvents.includes(e.type)) { event = e as MouseEvent }\n if (touchEvents.includes(e.type)) {\n const touchEvent = e as TouchEvent\n event = touchEvent.changedTouches[touchEvent.changedTouches.length - 1]\n }\n if (!event) { return }\n\n swipePath[type].x = event.pageX\n swipePath[type].y = event.pageY\n swipeDuration[type] = new Date().getTime()\n }\n\n const onSwipeStart = (e: TouchEvent | MouseEvent) => {\n if (!props.swipable || swipeStarted.value) { return }\n swipeStarted.value = true\n setState(e, 'start')\n }\n\n const onSwipeMove = (e: TouchEvent | MouseEvent) => {\n if (!swipeStarted.value) { return }\n setState(e, 'end')\n }\n\n const resetSwipe = () => {\n (['start', 'end'] as SwipePosition[]).forEach((type) => {\n swipePath[type].x = 0\n swipePath[type].y = 0\n swipeDuration[type] = 0\n })\n swipeStarted.value = false\n }\n\n const isSwipeAllowed = reactive({\n vertical: false,\n horizontal: false,\n })\n watchEffect(() => {\n isSwipeAllowed.horizontal = horizontalSpecificAllowedDirections.includes(props.swipeDirection)\n isSwipeAllowed.vertical = verticalSpecificAllowedDirections.includes(props.swipeDirection)\n })\n\n const calcDistance = (axis: 'x' | 'y') => {\n return isSwipeAllowed[axis === 'x' ? 'horizontal' : 'vertical'] &&\n swipePath.start[axis] && swipePath.end[axis]\n ? Math.trunc(swipePath.start[axis] - swipePath.end[axis])\n : 0\n }\n\n const getAcceptableValue = (direction: 'horizontal' | 'vertical', result: SwipeDirection) => {\n return result === props.swipeDirection || commonAllowedDirections[direction].includes(props.swipeDirection) ? result : ''\n }\n\n const swipeState = reactive({ direction: '', duration: 0 }) as SwipeState\n watch(swipePath, () => {\n const xDistance = calcDistance('x')\n const yDistance = calcDistance('y')\n\n if ((xDistance || yDistance) && [xDistance, yDistance].some((el) => Math.abs(el) >= props.swipeDistance)) {\n if (Math.abs(xDistance) >= Math.abs(yDistance) && isSwipeAllowed.horizontal) {\n const result = xDistance > 0 ? 'left' : 'right'\n swipeState.direction = getAcceptableValue('horizontal', result)\n } else if (Math.abs(xDistance) < Math.abs(yDistance) && isSwipeAllowed.vertical) {\n const result = yDistance > 0 ? 'down' : 'up'\n swipeState.direction = getAcceptableValue('vertical', result)\n }\n\n swipeState.duration = swipeDuration.end - swipeDuration.start\n\n resetSwipe()\n }\n }, { deep: true })\n\n watch(swipeState, () => cb(swipeState), { deep: true })\n\n if (props.swipable) {\n useEvent(['touchstart', 'mousedown'], onSwipeStart, container)\n useEvent(['touchmove', 'mousemove'], onSwipeMove, container)\n useEvent(['touchcancel', 'mouseup', 'touchend', 'mouseleave'], resetSwipe, container)\n }\n\n return { swipeState }\n}\n"],"names":[],"mappings":"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"}