taro-ui-vue3
Version:
Taro UI Rewritten in Vue 3.0
208 lines (207 loc) • 6.09 kB
JavaScript
import {
h,
defineComponent,
ref,
reactive,
watch,
computed,
mergeProps
} from "vue";
import _inRange from "lodash-es/inRange";
import _isEmpty from "lodash-es/isEmpty";
import {Text, View} from "@tarojs/components";
import {
delayGetClientRect,
delayGetScrollOffset,
uuid
} from "../utils/common";
import AtSwipeActionOptions from "./options/index";
const AtSwipeAction = defineComponent({
name: "AtSwipeAction",
props: {
isOpened: Boolean,
disabled: Boolean,
autoClose: Boolean,
options: {
type: Array,
default: () => []
},
onClick: Function,
onOpened: Function,
onClosed: Function
},
setup(props, {attrs, slots}) {
const endValue = ref(0);
const startX = ref(0);
const startY = ref(0);
const maxOffsetSize = ref(0);
const isMoving = ref(false);
const isTouching = ref(false);
const domInfo = ref({
top: 0,
bottom: 0,
left: 0,
right: 0
});
const state = reactive({
componentId: uuid(),
offsetSize: 0,
_isOpened: !!props.isOpened
});
const transformStyle = computed(() => {
const transform = computeTransform(state.offsetSize);
return transform ? {transform} : {};
});
const actionContentClass = computed(() => ({
"at-swipe-action__content": true,
animation: !isTouching.value
}));
const genActionItemClass = computed(() => (item) => ({
"at-swipe-action__option": true,
[`${item.className}`]: Boolean(item.className)
}));
watch(() => props.isOpened, (isOpened) => {
if (isOpened !== state._isOpened) {
_reset(!!isOpened);
}
});
function getDomInfo() {
return Promise.all([
delayGetClientRect({
delayTime: 0,
selectorStr: `#swipeAction-${state.componentId}`
}),
delayGetScrollOffset({delayTime: 0})
]).then(([rect, scrollOffset]) => {
if (rect[0]) {
rect[0].top += scrollOffset[0].scrollTop;
rect[0].bottom += scrollOffset[0].scrollTop;
domInfo.value = rect[0];
}
});
}
function _reset(isOpened) {
isMoving.value = false;
isTouching.value = false;
if (isOpened) {
endValue.value = -maxOffsetSize.value;
state._isOpened = true;
state.offsetSize = -maxOffsetSize.value;
} else {
endValue.value = 0;
state.offsetSize = 0;
state._isOpened = false;
}
}
function computeTransform(value) {
return value ? `translate3d(${value}px,0,0)` : null;
}
function handleOpened(event) {
if (typeof props.onOpened === "function" && state._isOpened) {
props.onOpened(event);
}
}
function handleClosed(event) {
if (typeof props.onClosed === "function" && !state._isOpened) {
props.onClosed(event);
}
}
function handleTouchStart(e) {
const {clientX, clientY} = e.touches[0];
if (props.disabled)
return;
getDomInfo();
startX.value = clientX;
startY.value = clientY;
isTouching.value = true;
}
function handleTouchMove(e) {
if (_isEmpty(domInfo.value)) {
return;
}
const {top, bottom, left, right} = domInfo.value;
const {clientX, clientY, pageX, pageY} = e.touches[0];
const x = Math.abs(clientX - startX.value);
const y = Math.abs(clientY - startY.value);
const inDom = _inRange(pageX, left, right) && _inRange(pageY, top, bottom);
if (!isMoving.value && inDom) {
isMoving.value = y === 0 || x / y >= Number.parseFloat(Math.tan(45 * Math.PI / 180).toFixed(2));
}
if (isTouching.value && isMoving.value) {
e.preventDefault();
const offsetSize = clientX - startX.value;
const isRight = offsetSize > 0;
if (state.offsetSize === 0 && isRight)
return;
const value = endValue.value + offsetSize;
state.offsetSize = value >= 0 ? 0 : value;
}
}
function handleTouchEnd(event) {
isTouching.value = false;
const {offsetSize} = state;
endValue.value = offsetSize;
const breakpoint = maxOffsetSize.value / 2;
const absOffsetSize = Math.abs(offsetSize);
if (absOffsetSize > breakpoint) {
_reset(true);
handleOpened(event);
return;
}
_reset(false);
handleClosed(event);
}
function handleDomInfo({width}) {
const {_isOpened} = state;
maxOffsetSize.value = width;
_reset(_isOpened);
}
function handleClick(item, index, event) {
if (typeof props.onClick === "function") {
props.onClick(item, index, event);
}
if (props.autoClose) {
_reset(false);
handleClosed(event);
}
}
return () => {
return h(View, mergeProps(attrs, {
id: `swipeAction-${state.componentId}`,
class: "at-swipe-action",
onTouchmove: handleTouchMove,
onTouchend: handleTouchEnd,
onTouchstart: handleTouchStart
}), {
default: () => [
h(View, {
class: actionContentClass.value,
style: transformStyle.value
}, {default: () => slots.default && slots.default()}),
Array.isArray(props.options) && props.options.length > 0 && h(AtSwipeActionOptions, {
options: props.options,
componentId: state.componentId,
onQueryedDom: handleDomInfo
}, {
default: () => props.options.map((item, key) => h(View, {
key: `${item.text}-${key}`,
class: genActionItemClass.value(item),
style: item.style,
onTap: (e) => handleClick(item, key, e)
}, {
default: () => [
h(Text, {
class: "option__text"
}, {default: () => item.text})
]
}))
})
]
});
};
}
});
var swipe_action_default = AtSwipeAction;
export {
swipe_action_default as default
};