-
Notifications
You must be signed in to change notification settings - Fork 84
/
Copy pathNavbar.vue
186 lines (183 loc) · 5.27 KB
/
Navbar.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
<template>
<div
ref="nav"
class="navbar row center bg-black color-white p-2xsmall pt-small pb-small">
<!-- section navigation -->
<button
v-for="item in $tm('navbar.items')"
:key="item.name"
:name="`go-to-${item.name}`"
class="pl-small pr-small color-white font-title type-uppercase type-no-underline border-right-white type-small"
@click="itemClick(item.id)">
{{ item.name }}
</button>
<!-- external links -->
<div class="relative" ref="dropdown">
<button
class="pl-small pr-small font-title type-uppercase type-small"
:class="linkDropdownOpen ? 'color-theme' : 'color-white'"
style="transform: translateY(2px);"
@click="linkDropdownOpen = !linkDropdownOpen">
{{ $t('navbar.dropdownName') }}
</button>
<transition name="fade">
<div
v-if="linkDropdownOpen"
class="dropdown-container bg-black color-white p-small card">
<div
v-for="{ name, url, description } in $tm('navbar.dropdown')"
:key="name">
<div class="flex end">
<a
:href="url"
target="_blank"
rel="noopener noreferrer">
{{ name }}
</a>
<new-tab-icon color="theme" class="ml-2xsmall" />
</div>
<p class="type-small mt-none type-right">
{{ description }}
</p>
</div>
</div>
</transition>
</div>
<button
class="border-left-white font-title type-uppercase pl-small relative line-height-body"
@click="langDropdownOpen = !langDropdownOpen">
<div
class="flex middle"
style="transform: translateY(2px);">
<globe-icon
:color="langDropdownOpen ? 'theme' : 'white'"
style="transform: translateY(-1px);" />
<div
class="pl-3xsmall type-small"
:class="langDropdownOpen ? 'color-theme' : 'color-white'"
style="transform: translateY(-2px);">
{{ langNames.find(({ lang }) => lang === $i18n.locale).name }}
</div>
</div>
<transition name="fade">
<div
v-if="langDropdownOpen"
class="dropdown-container bg-black color-white p-small">
<div
v-for="({ lang, name }, i) in langNames"
:key="lang">
<button
class="type-uppercase"
:class="[lang === $i18n.locale ? 'color-theme' : 'color-white', {['mb-2xsmall'] : i !== langNames.length - 1}]"
@click="setLang(lang)">
{{ name }}
</button>
</div>
</div>
</transition>
</button>
<transition name="opacity">
<div
v-if="navSticky"
class="tiny-logo-container">
<img :src="`${publicPath}img/RF-white.svg`" />
</div>
</transition>
</div>
</template>
<script>
import NewTabIcon from './icons/NewTabIcon.vue'
import GlobeIcon from './icons/GlobeIcon.vue'
export default {
name: 'Navbar',
components: {
NewTabIcon,
GlobeIcon
},
data: () => ({
navSticky: false,
publicPath: process.env.BASE_URL,
linkDropdownOpen: false,
langDropdownOpen: false
}),
computed: {
langNames() {
return Object.keys(this.$i18n.messages)
.map((lang) => ({ lang, name: this.$i18n.messages[lang].langName }))
}
},
methods: {
itemClick(itemId) {
const el = document.getElementById(itemId)
if (!el) return
// setting window.location.hash causes instant page scroll to that position and we dont want that
// lets strip urlParams and hash from url and append new hash
history.replaceState(null, null, `${location.href.split('?')[0].split('#')[0]}#${itemId}`)
window.scrollTo({
top: el.offsetTop,
behavior: 'smooth'
})
window.plausible('Nav click', { props: { section: itemId } })
},
setLang(lang) {
this.$i18n.locale = lang
window.localStorage.setItem('lang', lang)
},
onClick(ev) {
// close link dropdown if clicked outside
if (this.linkDropdownOpen && this.$refs.dropdown && !this.$refs.dropdown.contains(ev.target)) this.linkDropdownOpen = false
}
},
mounted() {
const observer = new IntersectionObserver((e) => {
this.navSticky = !e[0].isIntersecting
}, { threshold: 1 })
observer.observe(this.$refs.nav)
document.addEventListener('click', this.onClick)
},
beforeUnmount() {
document.removeEventListener('click', this.onClick)
},
watch: {
linkDropdownOpen() {
if (this.linkDropdownOpen) this.langDropdownOpen = false
},
langDropdownOpen() {
if (this.langDropdownOpen) this.linkDropdownOpen = false
}
}
}
</script>
<style scoped>
button {
transition: color 0.2s;
}
button:hover {
color: var(--color-theme) !important;
}
.navbar {
position: sticky;
top: -1px;
z-index: 2;
}
.tiny-logo-container {
position: absolute;
top: 0;
left: 0;
}
.tiny-logo-container > img {
margin-top: 0.1rem;
width: 3.5rem;
height: 3.5rem;
}
.dropdown-container {
position: absolute;
top: calc(100% + 2rem);
right: 0;
width: max-content;
}
.dropdown-container a {
display: block;
line-height: 1;
}
</style>