UNPKG

choo-taro-ui-vue3

Version:

Taro UI Rewritten in Vue 3.0

262 lines (254 loc) 10.1 kB
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;