init
This commit is contained in:
27
uni_modules/nutui-uni/components/swipe/index.scss
Normal file
27
uni_modules/nutui-uni/components/swipe/index.scss
Normal file
@@ -0,0 +1,27 @@
|
||||
.nut-swipe {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
|
||||
&__inner {
|
||||
position: relative;
|
||||
cursor: grab;
|
||||
transition: transform 0.3s cubic-bezier(0.19, 1, 0.22, 1);
|
||||
}
|
||||
|
||||
&__left,
|
||||
&__right {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__left {
|
||||
left: 0;
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
|
||||
&__right {
|
||||
right: 0;
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
}
|
||||
2
uni_modules/nutui-uni/components/swipe/index.ts
Normal file
2
uni_modules/nutui-uni/components/swipe/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './swipe'
|
||||
export * from './types'
|
||||
38
uni_modules/nutui-uni/components/swipe/swipe.ts
Normal file
38
uni_modules/nutui-uni/components/swipe/swipe.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { ExtractPropTypes } from 'vue'
|
||||
import { CLICK_EVENT } from '../_constants'
|
||||
import { commonProps, isString, makeArrayProp } from '../_utils'
|
||||
import type { SwipePosition, SwipeToggleEvent } from './types'
|
||||
|
||||
export const swipeProps = {
|
||||
...commonProps,
|
||||
/**
|
||||
* @description 唯一标识
|
||||
*/
|
||||
name: String,
|
||||
/**
|
||||
* @description 是否阻止滑动事件冒泡
|
||||
*/
|
||||
touchMoveStopPropagation: Boolean,
|
||||
/**
|
||||
* @description 是否阻止滑动事件行为
|
||||
*/
|
||||
touchMovePreventDefault: Boolean,
|
||||
/**
|
||||
* @description 是否禁用滑动
|
||||
*/
|
||||
disabled: Boolean,
|
||||
/**
|
||||
* @description 点击自动关闭的部分
|
||||
*/
|
||||
closeOnClick: makeArrayProp<SwipePosition>(['left', 'content', 'right']),
|
||||
}
|
||||
|
||||
export type SwipeProps = ExtractPropTypes<typeof swipeProps>
|
||||
|
||||
export const swipeEmits = {
|
||||
open: (event: SwipeToggleEvent) => event instanceof Object,
|
||||
close: (event: SwipeToggleEvent) => event instanceof Object,
|
||||
[CLICK_EVENT]: (val: string) => isString(val),
|
||||
}
|
||||
|
||||
export type SwipeEmits = typeof swipeEmits
|
||||
247
uni_modules/nutui-uni/components/swipe/swipe.vue
Normal file
247
uni_modules/nutui-uni/components/swipe/swipe.vue
Normal file
@@ -0,0 +1,247 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, defineComponent, getCurrentInstance, inject, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { CLICK_EVENT, PREFIX } from '../_constants'
|
||||
import { useRect, useTouch } from '../_hooks'
|
||||
import { getMainClass, getRandomId } from '../_utils'
|
||||
import { swipeEmits, swipeProps } from './swipe'
|
||||
import type { SwipeDirection, SwipePosition } from './types'
|
||||
|
||||
const props = defineProps(swipeProps)
|
||||
|
||||
const emit = defineEmits(swipeEmits)
|
||||
|
||||
const instance = getCurrentInstance()!
|
||||
|
||||
const parent = inject('swipeGroup', null) as any
|
||||
|
||||
const classes = computed(() => {
|
||||
return getMainClass(props, componentName)
|
||||
})
|
||||
|
||||
const randomId = getRandomId()
|
||||
|
||||
// eslint-disable-next-line ts/no-use-before-define
|
||||
const leftElId = `${componentName}-left-${randomId}`
|
||||
const leftElWidth = ref(0)
|
||||
|
||||
// eslint-disable-next-line ts/no-use-before-define
|
||||
const rightElId = `${componentName}-right-${randomId}`
|
||||
const rightElWidth = ref(0)
|
||||
|
||||
async function getElementWidth(elementId: string) {
|
||||
const rect = await useRect(elementId, instance)
|
||||
|
||||
return rect.width || 0
|
||||
}
|
||||
|
||||
async function initWidth() {
|
||||
const [leftWidth, rightWidth] = await Promise.all([
|
||||
getElementWidth(leftElId),
|
||||
getElementWidth(rightElId),
|
||||
])
|
||||
|
||||
leftElWidth.value = leftWidth
|
||||
rightElWidth.value = rightWidth
|
||||
}
|
||||
|
||||
watch(() => parent?.name.value, (value) => {
|
||||
if (props.name !== value && parent && parent.lock.value)
|
||||
close()
|
||||
})
|
||||
|
||||
const opened = ref(false)
|
||||
|
||||
let direction: SwipeDirection = ''
|
||||
let oldDirection: SwipeDirection = ''
|
||||
|
||||
const state = reactive({
|
||||
offset: 0,
|
||||
moving: false,
|
||||
})
|
||||
|
||||
const innerStyles = computed(() => {
|
||||
return {
|
||||
transform: `translate3d(${state.offset}px, 0, 0)`,
|
||||
}
|
||||
})
|
||||
|
||||
function open(dir: SwipeDirection = '') {
|
||||
parent && parent.update(props.name)
|
||||
|
||||
if (opened.value)
|
||||
return
|
||||
|
||||
if (dir)
|
||||
state.offset = dir === 'left' ? -rightElWidth.value : leftElWidth.value
|
||||
|
||||
opened.value = true
|
||||
|
||||
const finalDirection = direction || dir
|
||||
|
||||
emit('open', {
|
||||
name: props.name!,
|
||||
direction: finalDirection,
|
||||
position: finalDirection,
|
||||
})
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (!opened.value)
|
||||
return
|
||||
|
||||
state.offset = 0
|
||||
|
||||
opened.value = false
|
||||
|
||||
emit('close', {
|
||||
name: props.name!,
|
||||
direction,
|
||||
position: direction,
|
||||
})
|
||||
}
|
||||
|
||||
function handleClick(position: SwipePosition) {
|
||||
if (props.closeOnClick.includes(position))
|
||||
close()
|
||||
|
||||
emit(CLICK_EVENT, position)
|
||||
}
|
||||
|
||||
function updateOffset(deltaX: number) {
|
||||
direction = deltaX > 0 ? 'right' : 'left'
|
||||
|
||||
let offset = deltaX
|
||||
switch (direction) {
|
||||
case 'left': {
|
||||
if (opened.value && oldDirection === direction)
|
||||
offset = -rightElWidth.value
|
||||
else
|
||||
offset = Math.abs(deltaX) > rightElWidth.value ? -rightElWidth.value : deltaX
|
||||
break
|
||||
}
|
||||
case 'right': {
|
||||
if (opened.value && oldDirection === direction)
|
||||
offset = leftElWidth.value
|
||||
else
|
||||
offset = Math.abs(deltaX) > leftElWidth.value ? leftElWidth.value : deltaX
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
state.offset = offset
|
||||
}
|
||||
|
||||
const touch = useTouch()
|
||||
|
||||
function onTouchStart(event: any) {
|
||||
if (props.disabled)
|
||||
return
|
||||
|
||||
touch.start(event)
|
||||
}
|
||||
|
||||
async function onTouchMove(event: any) {
|
||||
if (props.disabled)
|
||||
return
|
||||
|
||||
touch.move(event)
|
||||
|
||||
if (touch.isHorizontal()) {
|
||||
state.moving = true
|
||||
|
||||
updateOffset(touch.deltaX.value)
|
||||
|
||||
if (props.touchMovePreventDefault)
|
||||
event.preventDefault()
|
||||
|
||||
if (props.touchMoveStopPropagation)
|
||||
event.stopPropagation()
|
||||
}
|
||||
}
|
||||
|
||||
function onTouchEnd() {
|
||||
if (!state.moving)
|
||||
return
|
||||
|
||||
state.moving = false
|
||||
|
||||
oldDirection = direction
|
||||
switch (direction) {
|
||||
case 'left': {
|
||||
if (Math.abs(state.offset) <= rightElWidth.value / 2) {
|
||||
close()
|
||||
}
|
||||
else {
|
||||
state.offset = -rightElWidth.value
|
||||
open()
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'right': {
|
||||
if (Math.abs(state.offset) <= leftElWidth.value / 2) {
|
||||
close()
|
||||
}
|
||||
else {
|
||||
state.offset = leftElWidth.value
|
||||
open()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
initWidth()
|
||||
}, 100)
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
close,
|
||||
})
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
const componentName = `${PREFIX}-swipe`
|
||||
|
||||
export default defineComponent({
|
||||
name: componentName,
|
||||
options: {
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
// #ifndef H5
|
||||
styleIsolation: 'shared',
|
||||
// #endif
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view :class="classes" :style="props.customStyle">
|
||||
<view
|
||||
class="nut-swipe__inner"
|
||||
:style="innerStyles"
|
||||
@touchstart="onTouchStart"
|
||||
@touchmove="onTouchMove"
|
||||
@touchend="onTouchEnd"
|
||||
@touchcancel="onTouchEnd"
|
||||
>
|
||||
<view :id="leftElId" class="nut-swipe__left" @click="handleClick('left')">
|
||||
<slot name="left" />
|
||||
</view>
|
||||
|
||||
<view class="nut-swipe__content" @click="handleClick('content')">
|
||||
<slot name="default" />
|
||||
</view>
|
||||
|
||||
<view :id="rightElId" class="nut-swipe__right" @click="handleClick('right')">
|
||||
<slot name="right" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "./index";
|
||||
</style>
|
||||
33
uni_modules/nutui-uni/components/swipe/types.ts
Normal file
33
uni_modules/nutui-uni/components/swipe/types.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
export const swipeDirection = ['left', 'right', ''] as const
|
||||
export type SwipeDirection = (typeof swipeDirection)[number]
|
||||
|
||||
export const swipePosition = ['left', 'content', 'right'] as const
|
||||
export type SwipePosition = (typeof swipePosition)[number]
|
||||
|
||||
export interface SwipeToggleEvent {
|
||||
/**
|
||||
* swipe名称
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* 滑动方向
|
||||
*/
|
||||
direction: SwipeDirection
|
||||
/**
|
||||
* @deprecated 使用 `direction` 代替
|
||||
* 滑动方向
|
||||
*/
|
||||
position: SwipeDirection
|
||||
}
|
||||
|
||||
export interface SwipeInst {
|
||||
/**
|
||||
* 滑动单元格侧边栏,left 指向左滑,right 指向右滑
|
||||
* @param position
|
||||
*/
|
||||
open: (direction?: SwipeDirection) => void
|
||||
/**
|
||||
* 收起单元格侧边栏
|
||||
*/
|
||||
close: () => void
|
||||
}
|
||||
Reference in New Issue
Block a user