Skip to content

Commit 90be27a

Browse files
authored
Add vuemastery pinia weekend promotion banner (#1320)
1 parent c758ee7 commit 90be27a

File tree

8 files changed

+997
-0
lines changed

8 files changed

+997
-0
lines changed
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
<template>
2+
<div
3+
:class="vueMasteryClass"
4+
class="vuemastery-banner-wrapper"
5+
ref="$vueMasteryBanner"
6+
role="banner"
7+
v-if="isVisible"
8+
>
9+
<a
10+
id="vm-pinia-weekend"
11+
href="https://www.vuemastery.com/pinia"
12+
target="_blank"
13+
>
14+
<img
15+
id="vm-logo-full"
16+
src="/vuemastery/vuemastery-white.svg"
17+
alt="vuemastery"
18+
/>
19+
<img
20+
id="vm-logo-small"
21+
src="https://firebasestorage.googleapis.com/v0/b/vue-mastery.appspot.com/o/flamelink%2Fmedia%2Fvue-mastery-logo-small.png?alt=media&token=941fcc3a-2b6f-40e9-b4c8-56b3890da108"
22+
alt="vuemastery"
23+
/>
24+
<div class="vm-pinia-weekend-wrapper">
25+
<div class="vm-pinia-weekend-content">
26+
<h1 class="vm-pinia-weekend-title">
27+
PINIA WEEKEND <span>MARCH 24-26</span>
28+
</h1>
29+
<p class="vm-pinia-weekend-sub">
30+
Watch all 4 premium courses for free
31+
</p>
32+
</div>
33+
<button id="vm-banner-cta">Secure your spot</button>
34+
</div>
35+
<button id="vm-banner-close" @click.prevent="closeBanner">X</button>
36+
</a>
37+
</div>
38+
</template>
39+
40+
<script setup lang="ts">
41+
import { ref, onMounted, computed, nextTick } from 'vue'
42+
43+
const isVisible = ref<Boolean>(true)
44+
const isMenuFixed = ref<Boolean>(false)
45+
const $vueMasteryBanner = ref<HTMLElement | null>(null)
46+
47+
const nameStorage = 'VUEMASTERY-BANNER--PINIA-WEEKEND-MARCH-2023'
48+
49+
const getMenuPosition = () => {
50+
return $vueMasteryBanner.value?.clientHeight || 0
51+
}
52+
53+
const isUnderBanner = () => {
54+
return window.pageYOffset > getMenuPosition()
55+
}
56+
57+
const fixMenuAfterBanner = () => {
58+
if (isUnderBanner()) {
59+
if (!isMenuFixed.value) {
60+
// The menu will be fixed
61+
isMenuFixed.value = true
62+
}
63+
} else if (isMenuFixed.value) {
64+
// The menu stay under the banner
65+
isMenuFixed.value = false
66+
}
67+
}
68+
69+
const toggleBannerEvents = (on: Boolean) => {
70+
// Add or remove event listerners attached to the DOM
71+
let method: 'addEventListener' | 'removeEventListener' = on
72+
? 'addEventListener'
73+
: 'removeEventListener'
74+
window[method]('resize', getMenuPosition)
75+
window[method]('scroll', fixMenuAfterBanner)
76+
}
77+
78+
const closeBanner = () => {
79+
console.log('closeBanner => ')
80+
// Remove events
81+
toggleBannerEvents(false)
82+
// Hide the banner
83+
isVisible.value = false
84+
// Save action in the local storage
85+
localStorage.setItem(nameStorage, String(true))
86+
}
87+
88+
const initBanner = () => {
89+
// Add event listeners
90+
toggleBannerEvents(true)
91+
// Get the menu position
92+
getMenuPosition()
93+
// Check current page offset position
94+
isMenuFixed.value = isUnderBanner()
95+
}
96+
97+
const vueMasteryClass = computed(() => {
98+
return {
99+
'vuemastery-menu-fixed': !isMenuFixed.value,
100+
}
101+
})
102+
103+
onMounted(() => {
104+
isVisible.value = !localStorage.getItem(nameStorage)
105+
if (isVisible.value) {
106+
nextTick(initBanner)
107+
}
108+
})
109+
</script>
110+
<style scoped>
111+
.vuemastery-banner-wrapper {
112+
position: relative;
113+
top: 0;
114+
bottom: 0;
115+
left: 0;
116+
right: 0;
117+
z-index: 60;
118+
width: 100%;
119+
height: 100%;
120+
max-height: 70px;
121+
background: url(/vuemastery/background-vuemastery.svg) left center no-repeat;
122+
overflow: hidden;
123+
padding: 12px;
124+
margin: 0;
125+
background-size: cover;
126+
}
127+
.vuemastery-banner-wrapper:before {
128+
content: '';
129+
background: url(/vuemastery/background-bubbles-vuemastery.svg) left center
130+
no-repeat;
131+
background-size: cover;
132+
position: absolute;
133+
top: 0;
134+
bottom: 0;
135+
left: 0;
136+
right: 0;
137+
transition: all 0.3s ease-out 0.1s;
138+
transform: scale(1.1);
139+
width: 100%;
140+
height: 100%;
141+
}
142+
143+
.vuemastery-banner-wrapper:after {
144+
content: '';
145+
background: url(/vuemastery/lock-vuemastery.svg) right center no-repeat;
146+
background-size: auto 100%;
147+
position: absolute;
148+
width: 100%;
149+
height: 100%;
150+
top: 0;
151+
left: 0;
152+
pointer-events: none;
153+
}
154+
155+
.vuemastery-banner-wrapper:hover {
156+
background-size: 150% auto;
157+
}
158+
.vuemastery-banner-wrapper:hover:before {
159+
transform: scale(1);
160+
}
161+
.vuemastery-banner-wrapper:hover:after {
162+
background-image: url(/vuemastery/unlock-vuemastery.svg);
163+
}
164+
165+
#vm-pinia-weekend {
166+
position: relative;
167+
width: 100%;
168+
height: 100%;
169+
text-decoration: none;
170+
color: white;
171+
display: flex;
172+
justify-content: center;
173+
align-items: center;
174+
overflow: hidden;
175+
}
176+
177+
#vm-logo-full {
178+
position: absolute;
179+
left: 15px;
180+
width: 120px;
181+
}
182+
183+
#vm-logo-small {
184+
display: none;
185+
}
186+
187+
#vm-banner-close {
188+
position: absolute;
189+
right: 12px;
190+
color: #fff;
191+
font-size: 20px;
192+
font-weight: bold;
193+
display: flex;
194+
align-items: center;
195+
justify-content: center;
196+
}
197+
#vm-banner-close:hover {
198+
color: #9d9c9c;
199+
}
200+
201+
.vm-pinia-weekend-wrapper {
202+
display: flex;
203+
align-items: center;
204+
}
205+
206+
.vm-pinia-weekend-title {
207+
margin: 0;
208+
padding: 0;
209+
font-weight: bold;
210+
font-size: 16px;
211+
text-align: center;
212+
background: linear-gradient(145deg, #c3ffac, #86ec87, #38a56a);
213+
background-clip: text;
214+
-webkit-background-clip: text;
215+
-webkit-text-fill-color: transparent;
216+
}
217+
.vm-pinia-weekend-sub {
218+
margin: 0;
219+
padding: 0;
220+
font-size: 14px;
221+
text-align: center;
222+
color: #fff;
223+
}
224+
225+
#vm-banner-cta {
226+
position: relative;
227+
margin-left: 10px;
228+
padding: 12px;
229+
background: linear-gradient(to top right, #41b782, #86d169);
230+
border: none;
231+
border-radius: 30px;
232+
color: #fff;
233+
font-size: 12px;
234+
font-weight: bold;
235+
text-decoration: none;
236+
text-transform: uppercase;
237+
box-shadow: 0px 17px 10px -10px rgba(0, 0, 0, 0.4);
238+
}
239+
#vm-banner-cta:hover {
240+
background: linear-gradient(to bottom right, #41b782, #86d169);
241+
}
242+
243+
@media (max-width: 850px) {
244+
.vuemastery-banner-wrapper:after {
245+
background: none;
246+
}
247+
}
248+
249+
@media (max-width: 767px) {
250+
#vm-logo-full {
251+
left: 10px;
252+
width: 100px;
253+
}
254+
#vm-banner-cta {
255+
display: none;
256+
}
257+
}
258+
259+
@media (max-width: 767px) {
260+
#vm-logo-full {
261+
display: none;
262+
}
263+
#vm-logo-small {
264+
position: absolute;
265+
display: block;
266+
left: 10px;
267+
width: 40px;
268+
}
269+
.vm-pinia-weekend-title {
270+
font-size: 14px;
271+
}
272+
.vm-pinia-weekend-sub {
273+
font-size: 12px;
274+
}
275+
}
276+
</style>
277+
278+
<style>
279+
.Layout:has(.vuemastery-menu-fixed) > .VPNav {
280+
position: relative;
281+
}
282+
283+
.Layout:has(.vuemastery-menu-fixed) > .VPSidebar {
284+
margin-top: 4em;
285+
}
286+
</style>

docs/.vitepress/theme/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import { h } from 'vue'
66
import FirebaseExample from '../components/FirebaseExample.vue'
77
import RtdbLogo from '../components/RtdbLogo.vue'
88
import FirestoreLogo from '../components/FirestoreLogo.vue'
9+
import VueMasteryBanner from './components/VueMasteryBanner.vue'
910

1011
export default {
1112
...Theme,
1213
Layout() {
1314
return h(Theme.Layout, null, {
1415
// 'home-features-after': () => h(HomeSponsors),
1516
// 'aside-ads-before': () => h(AsideSponsors),
17+
'layout-top': () => h(VueMasteryBanner),
1618
})
1719
},
1820
enhanceApp({ app, router, siteData }) {

0 commit comments

Comments
 (0)