choo-taro-ui-vue3
Version:
Taro UI Rewritten in Vue 3.0
262 lines (254 loc) • 10.1 kB
JavaScript
import Taro from '@tarojs/taro';
import { defineComponent, ref, reactive, computed, watch, onMounted, toRefs, createCommentVNode, renderList, Fragment, openBlock, createElementBlock, toDisplayString, createTextVNode, resolveComponent, withCtx, createVNode, normalizeClass, createBlock, normalizeStyle, renderSlot, mergeProps } from 'vue';
import { uuid } from '../utils';
const ENV = Taro.getEnv();
const MIN_DISTANCE = 100;
const MAX_INTERVAL = 10;
const AtTabs = defineComponent({
name: "AtTabs",
emits: {
"click": (index, event) => {
return !!(typeof index === "number" && typeof event === "object");
}
},
props: {
tabDirection: {
type: String,
default: "horizontal"
},
height: String,
current: {
type: Number,
default: 0
},
scroll: Boolean,
animated: {
type: Boolean,
default: true
},
swipeable: {
type: Boolean,
default: true
},
tabList: {
type: Array,
default: []
}
},
setup(props, { emit }) {
const tabId = ref(uuid());
const _touchDot = ref(0);
const _timer = ref(null);
const _interval = ref(0);
const _isMoving = ref(false);
const tabHeaderRef = ref(null);
const state = reactive({
scrollLeft: 0,
scrollTop: 0,
scrollIntoView: ""
});
const scrollX = computed(() => props.tabDirection === "horizontal");
const scrollY = computed(() => props.tabDirection === "vertical");
const rootClasses = computed(() => ({
[`at-tabs--${props.tabDirection}`]: true,
"at-tabs--scroll": props.scroll,
[`at-tabs--${ENV}`]: true,
"at-tabs": true
}));
const heightStyle = computed(() => scrollY.value ? { height: props.height } : {});
const underlineStyle = computed(() => ({
height: scrollY.value ? `${props.tabList.length * 100}%` : "1PX",
width: scrollX.value ? `${props.tabList.length * 100}%` : "1PX"
}));
const bodyStyle = computed(() => {
const transformStyle = scrollX.value ? `translate3d(-${props.current * 100}%, 0px, 0px)` : `translate3d(0px, -${props.current * 100}%, 0px)`;
const bodyStyle2 = {
...heightStyle.value,
transform: transformStyle,
WebkitTransform: transformStyle
};
if (!props.animated) {
bodyStyle2.transition = "unset";
}
return bodyStyle2;
});
const genTabItemClasses = computed(() => (idx) => ({
"at-tabs__item": true,
"at-tabs__item--active": props.current === idx
}));
function updateState(idx) {
if (props.scroll) {
if (ENV !== Taro.ENV_TYPE.WEB) {
const index = Math.max(idx - 1, 0);
state.scrollIntoView = `tab${tabId.value}${index}`;
} else {
const index = Math.max(idx - 1, 0);
const prevTabItem = tabHeaderRef.value.$el.children[index];
if (prevTabItem) {
state.scrollTop = prevTabItem.offsetTop;
state.scrollLeft = prevTabItem.offsetLeft;
}
}
}
}
function handleClick(index, event) {
emit("click", index, event);
}
function handleTouchstart(e) {
if (!props.swipeable || scrollY.value)
return;
_touchDot.value = e.touches[0].pageX;
_timer.value = setInterval(() => {
_interval.value++;
}, 100);
}
function handleTouchmove(e) {
if (!props.swipeable || scrollY.value)
return;
const touchMove = e.touches[0].pageX;
const moveDistance = touchMove - _touchDot.value;
const maxIndex = props.tabList.length;
if (!_isMoving.value && _interval.value < MAX_INTERVAL && _touchDot.value > 20) {
if (props.current + 1 < maxIndex && moveDistance <= -MIN_DISTANCE) {
_isMoving.value = true;
handleClick(props.current + 1, e);
} else if (props.current - 1 >= 0 && moveDistance >= MIN_DISTANCE) {
_isMoving.value = true;
handleClick(props.current - 1, e);
}
}
}
function handleTouchend() {
if (!props.swipeable || scrollY.value)
return;
clearInterval(_timer.value);
_interval.value = 0;
_isMoving.value = false;
}
watch(() => props.current, (current) => {
updateState(current);
});
onMounted(() => {
updateState(props.current);
});
return {
...toRefs(props),
...toRefs(state),
tabId,
scrollX,
scrollY,
bodyStyle,
heightStyle,
underlineStyle,
tabHeaderRef,
rootClasses,
genTabItemClasses,
handleClick,
handleTouchend,
handleTouchmove,
handleTouchstart
};
}
});
// Binding optimization for webpack code-split
const _createCommentVNode = createCommentVNode, _renderList = renderList, _Fragment = Fragment, _openBlock = openBlock, _createElementBlock = createElementBlock, _toDisplayString = toDisplayString, _createTextVNode = createTextVNode, _resolveComponent = resolveComponent, _withCtx = withCtx, _createVNode = createVNode, _normalizeClass = normalizeClass, _createBlock = createBlock, _normalizeStyle = normalizeStyle, _renderSlot = renderSlot, _mergeProps = mergeProps;
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_taro_text = process.env.TARO_ENV === "h5" ? _resolveComponent("taro-text") : "text";
const _component_taro_view = process.env.TARO_ENV === "h5" ? _resolveComponent("taro-view") : "view";
const _component_taro_scroll_view = process.env.TARO_ENV === "h5" ? _resolveComponent("taro-scroll-view") : "scroll-view";
return (_openBlock(), _createBlock(_component_taro_view, _mergeProps(_ctx.$attrs, {
class: _ctx.rootClasses,
style: _ctx.heightStyle
}), {
default: _withCtx(() => [
_createCommentVNode(" with scroll view "),
(_ctx.scroll)
? (_openBlock(), _createBlock(_component_taro_scroll_view, {
key: 0,
class: "at-tabs__header",
"scroll-with-animation": "",
id: _ctx.tabId,
ref: (el) => _ctx.tabHeaderRef = el,
style: _normalizeStyle(_ctx.heightStyle),
"scroll-x": _ctx.scrollX,
"scroll-y": _ctx.scrollY,
"scroll-top": _ctx.scrollTop,
"scroll-left": _ctx.scrollLeft,
"scroll-into-view": _ctx.scrollIntoView
}, {
default: _withCtx(() => [
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.tabList, (item, idx) => {
return (_openBlock(), _createBlock(_component_taro_view, {
key: `${item.title}-${idx}`,
id: `tab${_ctx.tabId}${idx}`,
class: _normalizeClass(_ctx.genTabItemClasses(idx)),
onTap: $event => (_ctx.handleClick(idx, $event))
}, {
default: _withCtx(() => [
_createVNode(_component_taro_text, { style: {"white-space":"nowrap"} }, {
default: _withCtx(() => [
_createTextVNode(_toDisplayString(item.title), 1 /* TEXT */)
]),
_: 2 /* DYNAMIC */
}, 1024 /* DYNAMIC_SLOTS */),
_createVNode(_component_taro_view, { class: "at-tabs__item-underline" })
]),
_: 2 /* DYNAMIC */
}, 1032 /* PROPS, DYNAMIC_SLOTS */, ["id", "class", "onTap"]))
}), 128 /* KEYED_FRAGMENT */))
]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["id", "style", "scroll-x", "scroll-y", "scroll-top", "scroll-left", "scroll-into-view"]))
: (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [
_createCommentVNode(" without scroll view "),
_createVNode(_component_taro_view, {
id: _ctx.tabId,
class: "at-tabs__header"
}, {
default: _withCtx(() => [
(_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.tabList, (item, idx) => {
return (_openBlock(), _createBlock(_component_taro_view, {
key: `${item.title}-${idx}`,
id: `tab${_ctx.tabId}${idx}`,
class: _normalizeClass(_ctx.genTabItemClasses(idx)),
onTap: $event => (_ctx.handleClick(idx, $event))
}, {
default: _withCtx(() => [
_createVNode(_component_taro_text, { style: {"white-space":"nowrap"} }, {
default: _withCtx(() => [
_createTextVNode(_toDisplayString(item.title), 1 /* TEXT */)
]),
_: 2 /* DYNAMIC */
}, 1024 /* DYNAMIC_SLOTS */),
_createVNode(_component_taro_view, { class: "at-tabs__item-underline" })
]),
_: 2 /* DYNAMIC */
}, 1032 /* PROPS, DYNAMIC_SLOTS */, ["id", "class", "onTap"]))
}), 128 /* KEYED_FRAGMENT */))
]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["id"])
], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */)),
_createCommentVNode(" tab body "),
_createVNode(_component_taro_view, {
class: "at-tabs__body",
style: _normalizeStyle(_ctx.bodyStyle),
onTouchend: _ctx.handleTouchend,
onTouchmove: _ctx.handleTouchmove,
onTouchstart: _ctx.handleTouchstart
}, {
default: _withCtx(() => [
_createVNode(_component_taro_view, {
class: "at-tabs__underline",
style: _normalizeStyle(_ctx.underlineStyle)
}, null, 8 /* PROPS */, ["style"]),
_renderSlot(_ctx.$slots, "default")
]),
_: 3 /* FORWARDED */
}, 8 /* PROPS */, ["style", "onTouchend", "onTouchmove", "onTouchstart"])
]),
_: 3 /* FORWARDED */
}, 16 /* FULL_PROPS */, ["class", "style"]))
}
AtTabs.render = _sfc_render;
export default AtTabs;