100 lines
2.1 KiB
Vue
100 lines
2.1 KiB
Vue
<script lang="ts" setup>
|
|
import { computed, onMounted, ref, useSlots } from 'vue'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
const props = withDefaults(defineProps<{
|
|
class?: string
|
|
delay?: number
|
|
}>(), {
|
|
delay: 1000,
|
|
})
|
|
|
|
const emit = defineEmits(['update:items'])
|
|
|
|
const slots = useSlots()
|
|
const index = ref(0)
|
|
const slotsArray = ref<any>([])
|
|
const maxShowItems = 100
|
|
|
|
const itemsToShow = computed(() => {
|
|
const start = index.value - maxShowItems < 0 ? 0 : index.value - maxShowItems
|
|
return slotsArray.value.slice(start, index.value)
|
|
})
|
|
|
|
watch([itemsToShow], () => {
|
|
emit('update:items', itemsToShow.value.at(-1), props)
|
|
})
|
|
|
|
async function loadComponents() {
|
|
slotsArray.value = slots.default ? slots.default()?.[0]?.children : []
|
|
|
|
while (index.value < slotsArray.value.length) {
|
|
index.value++
|
|
await delay(props.delay)
|
|
}
|
|
}
|
|
|
|
async function delay(ms: number) {
|
|
return new Promise(resolve => setTimeout(resolve, ms))
|
|
}
|
|
|
|
function getInitial(idx: number) {
|
|
return idx === index.value - 1
|
|
? {
|
|
scale: 0,
|
|
opacity: 0,
|
|
}
|
|
: undefined
|
|
}
|
|
function getEnter(idx: number) {
|
|
return idx === index.value - 1
|
|
? {
|
|
scale: 1,
|
|
opacity: 1,
|
|
y: 0,
|
|
transition: {
|
|
type: 'spring',
|
|
stiffness: 250,
|
|
damping: 40,
|
|
},
|
|
}
|
|
: undefined
|
|
}
|
|
|
|
function getLeave() {
|
|
return {
|
|
scale: 0,
|
|
opacity: 0,
|
|
y: 0,
|
|
transition: {
|
|
type: 'spring',
|
|
stiffness: 350,
|
|
damping: 40,
|
|
},
|
|
}
|
|
};
|
|
|
|
onMounted(() => loadComponents())
|
|
</script>
|
|
|
|
<template>
|
|
<div :class="cn('overflow-auto', $props.class)">
|
|
<transition-group name="list" tag="div" class="flex flex-col-reverse items-center p-2" move-class="move">
|
|
<div
|
|
v-for="(item, idx) in itemsToShow"
|
|
:key="item.props.key"
|
|
v-motion
|
|
:initial="getInitial(idx)" :enter="getEnter(idx)" :leave="getLeave()"
|
|
:class="cn('mx-auto w-full')"
|
|
>
|
|
<component :is="item" />
|
|
</div>
|
|
</transition-group>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.move {
|
|
transition: transform 0.4s ease-out;
|
|
}
|
|
</style>
|