Skip to content

Commit a0ceab5

Browse files
committed
feat: #5
Close: #5 Change-Id: Ieb7221d033cdfdaf4b19ed8401d42d1ebbcf7a27
1 parent 4155a20 commit a0ceab5

8 files changed

Lines changed: 149 additions & 0 deletions

File tree

docs/.vitepress/config.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export default defineConfig({
107107
darkModeSwitchLabel: locale.darkModeSwitchLabel,
108108
outlineTitle: locale.outlineTitle,
109109
returnToTopLabel: locale.returnToTopLabel,
110+
translateMenuLabel: locale.translateMenuLabel,
110111
docFooter: locale.docFooter,
111112
nav: locale.nav,
112113
sidebar: locale.sidebar

docs/.vitepress/locales.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export type SiteLocale = {
99
darkModeSwitchLabel?: string
1010
outlineTitle?: string
1111
returnToTopLabel?: string
12+
translateMenuLabel?: string
1213
docFooter?: {
1314
prev?: string
1415
next?: string
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<script setup lang="ts">
2+
import { ref, onMounted, onUnmounted } from 'vue'
3+
import { useData } from 'vitepress'
4+
5+
const { theme } = useData()
6+
const desktopReady = ref(false)
7+
const mobileReady = ref(false)
8+
9+
let observer: MutationObserver | null = null
10+
11+
function checkTargets() {
12+
desktopReady.value = !!document.querySelector('.VPNavBarTranslations .VPMenu')
13+
mobileReady.value = !!document.querySelector('.VPNavScreenTranslations .list')
14+
}
15+
16+
onMounted(() => {
17+
checkTargets()
18+
if (!mobileReady.value) {
19+
observer = new MutationObserver(() => {
20+
checkTargets()
21+
if (mobileReady.value) {
22+
observer?.disconnect()
23+
observer = null
24+
}
25+
})
26+
observer.observe(document.body, { childList: true, subtree: true })
27+
}
28+
})
29+
30+
onUnmounted(() => {
31+
observer?.disconnect()
32+
})
33+
</script>
34+
35+
<template>
36+
<Teleport v-if="desktopReady" to=".VPNavBarTranslations .VPMenu">
37+
<div class="vp-translate-desktop">
38+
<a
39+
href="https://hosted.weblate.org/engage/databackup/"
40+
target="_blank"
41+
rel="noopener noreferrer"
42+
class="vp-translate-link"
43+
>
44+
{{ theme.translateMenuLabel ?? 'Translate' }}
45+
<svg class="vp-translate-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
46+
<path d="M15 3h6v6"/>
47+
<path d="M10 14 21 3"/>
48+
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
49+
</svg>
50+
</a>
51+
</div>
52+
</Teleport>
53+
54+
<Teleport v-if="mobileReady" to=".VPNavScreenTranslations .list">
55+
<li class="item vp-translate-item-mobile">
56+
<a
57+
href="https://hosted.weblate.org/engage/databackup/"
58+
target="_blank"
59+
rel="noopener noreferrer"
60+
class="vp-translate-link-mobile"
61+
>
62+
{{ theme.translateMenuLabel ?? 'Translate' }}
63+
<svg class="vp-translate-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
64+
<path d="M15 3h6v6"/>
65+
<path d="M10 14 21 3"/>
66+
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
67+
</svg>
68+
</a>
69+
</li>
70+
</Teleport>
71+
</template>
72+
73+
<style>
74+
.vp-translate-desktop {
75+
margin: 12px -12px 0;
76+
border-top: 1px solid var(--vp-c-divider);
77+
padding: 12px 12px 0;
78+
}
79+
80+
.vp-translate-link {
81+
display: flex;
82+
align-items: center;
83+
border-radius: 6px;
84+
padding: 0 12px;
85+
line-height: 32px;
86+
font-size: 14px;
87+
font-weight: 500;
88+
color: var(--vp-c-text-1);
89+
white-space: nowrap;
90+
transition: background-color 0.25s, color 0.25s;
91+
}
92+
93+
.vp-translate-link:hover {
94+
color: var(--vp-c-brand-1);
95+
background-color: var(--vp-c-default-soft);
96+
}
97+
98+
.vp-translate-item-mobile {
99+
margin-top: 4px;
100+
border-top: 1px solid var(--vp-c-divider);
101+
padding-top: 4px;
102+
}
103+
104+
.vp-translate-link-mobile {
105+
display: flex;
106+
align-items: center;
107+
line-height: 32px;
108+
font-size: 13px;
109+
color: var(--vp-c-text-1);
110+
transition: color 0.25s;
111+
}
112+
113+
.vp-translate-link-mobile:hover {
114+
color: var(--vp-c-brand-1);
115+
}
116+
117+
.vp-translate-icon {
118+
flex-shrink: 0;
119+
width: 11px;
120+
height: 11px;
121+
margin-left: 6px;
122+
opacity: 0.6;
123+
}
124+
</style>

docs/.vitepress/theme/custom.css

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.VPButton.alt[target="_blank"]::after {
2+
content: '';
3+
display: inline-block;
4+
width: 13px;
5+
height: 13px;
6+
margin-left: 6px;
7+
margin-bottom: -1px;
8+
vertical-align: middle;
9+
background-color: currentColor;
10+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 3h6v6'/%3E%3Cpath d='M10 14 21 3'/%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3C/svg%3E") no-repeat center;
11+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 3h6v6'/%3E%3Cpath d='M10 14 21 3'/%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3C/svg%3E") no-repeat center;
12+
-webkit-mask-size: 100%;
13+
mask-size: 100%;
14+
}

docs/.vitepress/theme/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
import { h } from 'vue'
12
import type { Theme } from 'vitepress'
23
import DefaultTheme from 'vitepress/theme'
34

5+
import './custom.css'
46
import RedirectPage from './RedirectPage.vue'
7+
import TranslateLink from './TranslateLink.vue'
58

69
export default {
710
extends: DefaultTheme,
11+
Layout: () => h(DefaultTheme.Layout, null, {
12+
'nav-bar-content-after': () => h(TranslateLink),
13+
}),
814
enhanceApp(ctx) {
915
DefaultTheme.enhanceApp?.(ctx)
1016
ctx.app.component('RedirectPage', RedirectPage)

docs/en/locale.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"darkModeSwitchLabel": "Appearance",
66
"outlineTitle": "On this page",
77
"returnToTopLabel": "Return to top",
8+
"translateMenuLabel": "Translate",
89
"docFooter": {
910
"prev": "Previous page",
1011
"next": "Next page"

docs/hr/locale.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"darkModeSwitchLabel": "Izgled",
66
"outlineTitle": "Na ovoj stranici",
77
"returnToTopLabel": "Povratak na vrh",
8+
"translateMenuLabel": "Prevedi",
89
"docFooter": {
910
"prev": "Prethodna stranica",
1011
"next": "Sljedeća stranica"

docs/zh/locale.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"darkModeSwitchLabel": "外观",
66
"outlineTitle": "本页内容",
77
"returnToTopLabel": "返回顶部",
8+
"translateMenuLabel": "翻译",
89
"docFooter": {
910
"prev": "上一页",
1011
"next": "下一页"

0 commit comments

Comments
 (0)