| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 | import { VantComponent } from '../common/component';import { touch } from '../mixins/touch';import { getAllRect, getRect, groupSetData, nextTick, requestAnimationFrame, } from '../common/utils';import { isDef } from '../common/validator';import { useChildren } from '../common/relation';VantComponent({    mixins: [touch],    classes: [        'nav-class',        'tab-class',        'tab-active-class',        'line-class',        'wrap-class',    ],    relation: useChildren('tab', function () {        this.updateTabs();    }),    props: {        sticky: Boolean,        border: Boolean,        swipeable: Boolean,        titleActiveColor: String,        titleInactiveColor: String,        color: String,        animated: {            type: Boolean,            observer() {                this.children.forEach((child, index) => child.updateRender(index === this.data.currentIndex, this));            },        },        lineWidth: {            type: null,            value: 40,            observer: 'resize',        },        lineHeight: {            type: null,            value: -1,        },        active: {            type: null,            value: 0,            observer(name) {                if (name !== this.getCurrentName()) {                    this.setCurrentIndexByName(name);                }            },        },        type: {            type: String,            value: 'line',        },        ellipsis: {            type: Boolean,            value: true,        },        duration: {            type: Number,            value: 0.3,        },        zIndex: {            type: Number,            value: 1,        },        swipeThreshold: {            type: Number,            value: 5,            observer(value) {                this.setData({                    scrollable: this.children.length > value || !this.data.ellipsis,                });            },        },        offsetTop: {            type: Number,            value: 0,        },        lazyRender: {            type: Boolean,            value: true,        },        useBeforeChange: {            type: Boolean,            value: false,        },    },    data: {        tabs: [],        scrollLeft: 0,        scrollable: false,        currentIndex: 0,        container: null,        skipTransition: true,        scrollWithAnimation: false,        lineOffsetLeft: 0,        inited: false,    },    mounted() {        requestAnimationFrame(() => {            this.swiping = true;            this.setData({                container: () => this.createSelectorQuery().select('.van-tabs'),            });            this.resize();            this.scrollIntoView();        });    },    methods: {        updateTabs() {            const { children = [], data } = this;            this.setData({                tabs: children.map((child) => child.data),                scrollable: this.children.length > data.swipeThreshold || !data.ellipsis,            });            this.setCurrentIndexByName(data.active || this.getCurrentName());        },        trigger(eventName, child) {            const { currentIndex } = this.data;            const data = this.getChildData(currentIndex, child);            if (!isDef(data)) {                return;            }            this.$emit(eventName, data);        },        onTap(event) {            const { index } = event.currentTarget.dataset;            const child = this.children[index];            if (child.data.disabled) {                this.trigger('disabled', child);                return;            }            this.onBeforeChange(index).then(() => {                this.setCurrentIndex(index);                nextTick(() => {                    this.trigger('click');                });            });        },        // correct the index of active tab        setCurrentIndexByName(name) {            const { children = [] } = this;            const matched = children.filter((child) => child.getComputedName() === name);            if (matched.length) {                this.setCurrentIndex(matched[0].index);            }        },        setCurrentIndex(currentIndex) {            const { data, children = [] } = this;            if (!isDef(currentIndex) ||                currentIndex >= children.length ||                currentIndex < 0) {                return;            }            groupSetData(this, () => {                children.forEach((item, index) => {                    const active = index === currentIndex;                    if (active !== item.data.active || !item.inited) {                        item.updateRender(active, this);                    }                });            });            if (currentIndex === data.currentIndex) {                if (!data.inited) {                    this.resize();                }                return;            }            const shouldEmitChange = data.currentIndex !== null;            this.setData({ currentIndex });            requestAnimationFrame(() => {                this.resize();                this.scrollIntoView();            });            nextTick(() => {                this.trigger('input');                if (shouldEmitChange) {                    this.trigger('change');                }            });        },        getCurrentName() {            const activeTab = this.children[this.data.currentIndex];            if (activeTab) {                return activeTab.getComputedName();            }        },        resize() {            if (this.data.type !== 'line') {                return;            }            const { currentIndex, ellipsis, skipTransition } = this.data;            Promise.all([                getAllRect(this, '.van-tab'),                getRect(this, '.van-tabs__line'),            ]).then(([rects = [], lineRect]) => {                const rect = rects[currentIndex];                if (rect == null) {                    return;                }                let lineOffsetLeft = rects                    .slice(0, currentIndex)                    .reduce((prev, curr) => prev + curr.width, 0);                lineOffsetLeft +=                    (rect.width - lineRect.width) / 2 + (ellipsis ? 0 : 8);                this.setData({ lineOffsetLeft, inited: true });                this.swiping = true;                if (skipTransition) {                    // waiting transition end                    setTimeout(() => {                        this.setData({ skipTransition: false });                    }, this.data.duration);                }            });        },        // scroll active tab into view        scrollIntoView() {            const { currentIndex, scrollable, scrollWithAnimation } = this.data;            if (!scrollable) {                return;            }            Promise.all([                getAllRect(this, '.van-tab'),                getRect(this, '.van-tabs__nav'),            ]).then(([tabRects, navRect]) => {                const tabRect = tabRects[currentIndex];                const offsetLeft = tabRects                    .slice(0, currentIndex)                    .reduce((prev, curr) => prev + curr.width, 0);                this.setData({                    scrollLeft: offsetLeft - (navRect.width - tabRect.width) / 2,                });                if (!scrollWithAnimation) {                    nextTick(() => {                        this.setData({ scrollWithAnimation: true });                    });                }            });        },        onTouchScroll(event) {            this.$emit('scroll', event.detail);        },        onTouchStart(event) {            if (!this.data.swipeable)                return;            this.swiping = true;            this.touchStart(event);        },        onTouchMove(event) {            if (!this.data.swipeable || !this.swiping)                return;            this.touchMove(event);        },        // watch swipe touch end        onTouchEnd() {            if (!this.data.swipeable || !this.swiping)                return;            const { direction, deltaX, offsetX } = this;            const minSwipeDistance = 50;            if (direction === 'horizontal' && offsetX >= minSwipeDistance) {                const index = this.getAvaiableTab(deltaX);                if (index !== -1) {                    this.onBeforeChange(index).then(() => this.setCurrentIndex(index));                }            }            this.swiping = false;        },        getAvaiableTab(direction) {            const { tabs, currentIndex } = this.data;            const step = direction > 0 ? -1 : 1;            for (let i = step; currentIndex + i < tabs.length && currentIndex + i >= 0; i += step) {                const index = currentIndex + i;                if (index >= 0 &&                    index < tabs.length &&                    tabs[index] &&                    !tabs[index].disabled) {                    return index;                }            }            return -1;        },        onBeforeChange(index) {            const { useBeforeChange } = this.data;            if (!useBeforeChange) {                return Promise.resolve();            }            return new Promise((resolve, reject) => {                this.$emit('before-change', Object.assign(Object.assign({}, this.getChildData(index)), { callback: (status) => (status ? resolve() : reject()) }));            });        },        getChildData(index, child) {            const currentChild = child || this.children[index];            if (!isDef(currentChild)) {                return;            }            return {                index: currentChild.index,                name: currentChild.getComputedName(),                title: currentChild.data.title,            };        },    },});
 |