import { makeComponentProps } from '@/composables/component' import { makeTagProps } from '@/composables/tag' import { genericComponent, propsFactory, useRender } from '@/utils' import { ExtractPropTypes, Ref } from 'vue' import { ref, computed, onMounted, watch, nextTick, reactive } from 'vue' import { UTabItem } from './UTabItem' import { TabItemData } from '@/types/tabsData' export const makeUTabsProps = propsFactory( { size: { type: String, default: 'sm', required: false, }, fullWidth: { type: Boolean, default: false, required: false, }, variant: { type: String, default: 'underlined', required: false, }, tabItems: { type: Array as () => TabItemData[], default: [], required: false, }, activeIndex: { type: Number, default: 0, required: false, }, ...makeComponentProps(), ...makeTagProps(), }, 'UTabs' ) export type UTabsProps = ExtractPropTypes export type UTabsSlots = { // } export const UTabs = genericComponent()({ name: 'UTabs', props: makeUTabsProps(), emits: { change: (value: { key: number }) => true, }, setup(props, { emit }) { const lineWidth = ref(0) const lineLeft = ref(0) const activeTabIndex = ref(props.activeIndex) const indexValue = ref(props.activeIndex) const itemRefs = reactive<(UTabItem | null)[]>([]) const calculateLineParams = (index: number) => { const tabItem = itemRefs[index] const parentContainer = tabItem?.$el.parentElement if (parentContainer) { const tabItemRect = tabItem.$el.getBoundingClientRect() const parentRect = parentContainer.getBoundingClientRect() lineWidth.value = tabItemRect.width lineLeft.value = tabItemRect.left - parentRect.left } } const lineStyle = computed(() => ({ width: `${lineWidth.value}px`, left: `${lineLeft.value}px`, bottom: `0px`, })) watch( () => activeTabIndex.value, (newValue, oldValue) => { calculateDuration(oldValue, newValue) } ) watch([() => props.size, () => props.tabItems], () => { nextTick(() => { calculateLineParams(activeTabIndex.value) }) }) const duration = ref(300) const calculateDuration = (prev: number, current: number) => { if (Math.abs(current - prev) >= 2) { duration.value = 300 } else { duration.value = 200 } } watch( () => props.fullWidth, async () => { await nextTick() calculateLineParams(activeTabIndex.value) } ) onMounted(() => { if (props.tabItems.length > 0) { nextTick(() => { const defaultActiveTab = props.tabItems[props.activeIndex] const defaultActiveTabIndex = props.tabItems.findIndex( (tabItem) => tabItem === defaultActiveTab ) switchActiveState(defaultActiveTab, defaultActiveTabIndex) setTimeout(() => { calculateLineParams(defaultActiveTabIndex) }, 100) }) } }) const switchActiveState = (tabItem: TabItemData, index: number) => { activeTabIndex.value = index indexValue.value = index calculateLineParams(index) emit('change', { key: index }) } useRender(() => (
{props.tabItems.map((tabItem, index) => { const itemRef: Ref = ref(null) itemRefs[index] = itemRef.value return ( switchActiveState(tabItem, index)} ref={(el) => (itemRefs[index] = el as UTabItem)} /> ) })} {activeTabIndex.value === indexValue.value ? (
) : null}
)) return { switchActiveState, } }, }) export type UTabs = InstanceType