<template>
    <!-- NOTE:min-h-1在使用useElementSize时非常重要 -->
    <view ref="container" class="fx-col flex-1 min-h-1">
        <uv-tabs :current="current" :list="tabs" :key-name="keyName" v-bind="tabBindings"
                 class="bg-white bd-b-1" @change="handleTabChange">
            <template v-if="$slots.tab" #default="scope">
                <slot name="tab" v-bind="scope"/>
            </template>
        </uv-tabs>
        <uv-line v-if="border"/>
        <swiper :current="current" :style="{height: swiperHeight+'px'}" v-bind="swiperBindings"
                @change="handleSwiperChange">
            <swiper-item v-for="t in tabs" :key="t.name">
                <!-- 延迟渲染 -->
                <!-- 如果配置相同的template可共享卡槽 -->
                <!-- 内容如果要支持滚动，请使用css class - .tabs-swiper-content -->
                <keep-alive v-if="lazy">
                    <slot v-if="t.show" :name="t.template||template||t.name" v-bind="t"/>
                </keep-alive>
                <slot v-else :name="t.template||template||t.name" v-bind="t"/>
            </swiper-item>
        </swiper>
    </view>
</template>

<script setup>
import {ref, computed, watch} from 'vue'
import _ from "lodash";
import {createPropDefine} from "@/utils";
import {useElementSize} from "@vueuse/core";

const props = defineProps({
    // support synchronization
    modelValue: createPropDefine(0, Number),
    tabs: createPropDefine([], Array),
    keyName: createPropDefine('name'),
    // other options of uv-tabs
    tabOptions: createPropDefine(null, Object),
    // other uni-app swiper options
    swiperOptions: createPropDefine(null, Object),
    // support lazy rendering, default is true
    lazy: createPropDefine(true, Boolean),
    // whether cache all tabs? if not use cacheSize, higher priority than cacheSize
    cacheAll: createPropDefine(false, Boolean),
    // cached accessed tab. <=0 means cache all.
    cacheSize: createPropDefine(5, Number),
    // tab content will render after delay time, recommended for complex dynamic content.
    // related to lazy, default is true
    delay: createPropDefine(true, Boolean),
    preload: createPropDefine(true, Boolean), // 一般情况下preload会非常平滑，但有大请求时可以禁用
    // bigger than the animation time。 default 400ms.
    delayTime: createPropDefine(400, Number),
    border: createPropDefine(false, Boolean),
    tabsHeight: createPropDefine(44, Number),
    // 统一指定模板，优先级低于tab.template，因为有时候让tab指定template会破坏原数据结构
    template: createPropDefine('')
})
const emits = defineEmits(['update:modelValue', 'change'])

// 因为外部可能不会使用v-model:current接收回传，所以这里本地保持住该值
const current = ref(0)
const cachedTabs = ref([])
const container = ref(null)
const {height} = useElementSize(container)
const swiperHeight = computed(() => height.value - props.tabsHeight - (props.border ? 1 : 0))

const tabBindings = computed(() => {
    // make some default options for u-tabs here
    return {
        scrollable: props.tabs.length > 4,
        itemStyle: {height: props.tabsHeight + 'px'},
        ...props.tabOptions
    }
})
const swiperBindings = computed(() => {
    // make some default options for uni-app swiper here
    return {
        ...props.swiperOptions
    }
})

// 保持localCurrent与props.current同步，即props优先级更高
watch(() => props.modelValue, (val) => current.value = val, {immediate: true})

watch([current, () => props.tabs], ([current]) => {
    const {cacheAll, cacheSize, tabs, delay, preload, delayTime} = props
    if (!tabs.length) return // no data, wait for tabs ready.

    // delay control, first tab must render immediately.
    const effectDelay = cachedTabs.value.length && delay ? delayTime : 0

    // cache tabs, keep accessed sequence.
    const next = [current]
    if (preload) {
        // keep current -1 +1 in cache list while preload=true.
        // this will make swiper action much-much smooth.
        if (current + 1 < tabs.length) next.unshift(current + 1)
        if (current - 1 >= 0) next.unshift(current - 1)
    }
    _.pull(cachedTabs.value, ...next)
    cachedTabs.value.push(...next)
    while (!cacheAll && cacheSize && cachedTabs.value.length > cacheSize) {
        cachedTabs.value.shift()
    }

    // reset show property of all tabs
    if (effectDelay) {
        setTimeout(() => {
            tabs.forEach((t, i) => t.show = cachedTabs.value.includes(i))
        }, effectDelay)
    } else {
        tabs.forEach((t, i) => t.show = cachedTabs.value.includes(i))
    }
}, {immediate: true})

const handleTabChange = function ({index}) {
    current.value = index
    emits('update:modelValue', current.value)
    emits('change', current.value)
}
const handleSwiperChange = function (e) {
    current.value = e.detail.current
    emits('update:modelValue', current.value)
    emits('change', current.value)
}
</script>

<style>
</style>
