11<script setup lang="ts">
22import type { DefaultTheme } from ' vitepress/theme'
3- import { ref , watch , onMounted } from ' vue'
3+ import { ref , watch , onMounted , onUnmounted , computed } from ' vue'
44import { useData } from ' vitepress'
55
6- const { page } = useData ()
6+ const currentSponsorIndex = ref (0 )
7+ let intervalId: ReturnType <typeof setInterval > | null = null
8+ const INTERVAL_DURATION = 10000 // Cycle every 10 seconds
9+
710const props = defineProps <{
811 sponsors: DefaultTheme .sponsors
912}>()
1013
11- const sponsors = props .sponsors
12-
13- const container = ref ()
14-
15- let currentCard = ref (0 )
16- // Cycle through the ads on every new page
17- function refresh() {
18- if (sponsors && sponsors .cards ) {
19- const cards = sponsors .cards
20- const index = (currentCard .value ) % cards .length
21- const card = cards [index ]
22- currentCard .value = index + 1
23- // Clear existing content
24- container .value .innerHTML = ' '
25-
26- const link = document .createElement (' a' )
27- link .href = card .href
28- link .target = ' _blank'
29- link .rel = ' noopener'
30-
31- const img = document .createElement (' img' )
32- img .src = card .image
33- img .alt = card .alt
34-
35- const cardContent = document .createElement (' div' )
36- cardContent .className = ' card-content'
37-
38- link .appendChild (img )
39- cardContent .appendChild (link )
40-
41- const title = document .createElement (' a' )
42- title .href = card .href
43- title .target = ' _blank'
44- title .rel = ' noopener'
45- title .className = ' carbon-text'
46- title .textContent = card .text
47- cardContent .appendChild (title )
48-
49- const poweredBy = document .createElement (" a" )
50- poweredBy .className = " carbon-poweredby"
51- poweredBy .textContent = " Featured Sponsor"
52- poweredBy .href = " https://github.com/sponsors/ravitemer"
53- poweredBy .target = ' _blank'
54- poweredBy .rel = ' noopener'
55- cardContent .appendChild (poweredBy )
56-
57- container .value .appendChild (cardContent )
14+ const { page } = useData ()
15+
16+ const sponsorCards = computed (() => props .sponsors ?.cards || [])
17+
18+ const displaySponsor = computed (() => {
19+ if (sponsorCards .value .length === 0 ) {
20+ return null
21+ }
22+ return sponsorCards .value [currentSponsorIndex .value ]
23+ })
24+
25+ function startCycling() {
26+ stopCycling () // Clear any existing interval before starting a new one
27+
28+ if (sponsorCards .value .length > 1 ) { // Only cycle if there's more than one sponsor
29+ intervalId = setInterval (() => {
30+ currentSponsorIndex .value = (currentSponsorIndex .value + 1 ) % sponsorCards .value .length
31+ }, INTERVAL_DURATION )
5832 }
5933}
6034
61- let isInitialized = false
62- function init () {
63- if (! isInitialized ) {
64- isInitialized = true
65- refresh ()
35+ // Function to stop the ad cycling interval
36+ function stopCycling () {
37+ if (intervalId ) {
38+ clearInterval ( intervalId )
39+ intervalId = null
6640 }
6741}
68- watch (() => page .value .relativePath , () => {
69- if (isInitialized ) {
70- refresh ()
42+
43+ // Lifecycle hook: When the component is mounted, start cycling
44+ onMounted (() => {
45+ if (props .sponsors ?.enabled ) {
46+ startCycling ()
7147 }
7248})
7349
74- // no need to account for option changes during dev, we can just
75- // refresh the page
76- if (sponsors && sponsors .enabled ) {
77- onMounted (() => {
78- init ()
79- })
80- }
50+ // Lifecycle hook: When the component is unmounted, stop cycling
51+ onUnmounted (() => {
52+ stopCycling ()
53+ })
54+
8155 </script >
8256
8357<template >
84- <div class =" VpSponsorCard" ref =" container" />
58+ <div class =" VpSponsorCard" >
59+ <!-- Use Vue's Transition component for smooth fade effects -->
60+ <Transition name =" sponsor-fade" mode =" out-in" >
61+ <div v-if =" sponsors?.enabled && displaySponsor" :key =" currentSponsorIndex" class =" card-content" >
62+ <a :href =" displaySponsor.href" target =" _blank" rel =" noopener" >
63+ <img :src =" displaySponsor.image" :alt =" displaySponsor.alt" >
64+ </a >
65+ <a :href =" displaySponsor.href" target =" _blank" rel =" noopener" class =" carbon-text" >
66+ {{ displaySponsor.text }}
67+ </a >
68+ <a href =" https://github.com/sponsors/ravitemer" target =" _blank" rel =" noopener" class =" carbon-poweredby" >
69+ Featured Sponsor
70+ </a >
71+ </div >
72+ <!-- Optional: Placeholder or empty state when no sponsor is displayed -->
73+ <div v-else >
74+ <!-- Content to display when no sponsor is active or enabled -->
75+ </div >
76+ </Transition >
77+ </div >
8578</template >
8679
8780<style scoped>
8881.VpSponsorCard {
8982 display : flex ;
9083 margin-top : 10px ;
91- /* margin-left: 10px; */
9284 justify-content : center ;
9385 align-items : center ;
9486 padding : 24px ;
9587 border-radius : 12px ;
96- min-height : 256px ;
88+ min-height : 256px ; /* Maintain height during transitions to prevent layout shifts */
9789 text-align : center ;
9890 line-height : 18px ;
9991 font-size : 12px ;
@@ -111,7 +103,7 @@ if (sponsors && sponsors.enabled) {
111103 margin : 0 auto ;
112104 padding-top : 12px ;
113105 color : var (--vp-carbon-ads-text-color );
114- transition : color 0.25 s ;
106+ transition : color 0.2 s ;
115107}
116108
117109.VpSponsorCard :deep(.carbon-text :hover ) {
@@ -125,24 +117,31 @@ if (sponsors && sponsors.enabled) {
125117 font-weight : 500 ;
126118 color : var (--vp-carbon-ads-poweredby-color );
127119 text-transform : uppercase ;
128- transition : color 0.25 s ;
120+ transition : color 0.2 s ;
129121}
130122
131123.VpSponsorCard :deep(.carbon-poweredby :hover ) {
132124 color : var (--vp-carbon-ads-hover-poweredby-color );
133125}
134126
135- .VpSponsorCard :deep(> div ) {
136- display : none ;
137- }
138-
139- .VpSponsorCard :deep(> div :first-of-type ) {
140- display : block ;
141- }
142-
127+ /* Base styles for the content container */
143128.card-content {
144129 display : flex ;
145130 flex-direction : column ;
146131 align-items : center ;
132+ width : 100% ; /* Ensure content fills available space within VpSponsorCard */
133+ /* Opacity and transition handled by Vue's Transition classes */
147134}
135+
136+ /* Vue Transition classes for fade effect */
137+ .sponsor-fade-enter-active ,
138+ .sponsor-fade-leave-active {
139+ transition : opacity 0.2s ease ; /* Adjust transition duration as desired */
140+ }
141+
142+ .sponsor-fade-enter-from ,
143+ .sponsor-fade-leave-to {
144+ opacity : 0 ;
145+ }
146+
148147 </style >
0 commit comments