valaxy-theme-press 0.15.9 → 0.15.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/config.ts +1 -1
- package/components/PressArticle.vue +7 -7
- package/components/PressArticleCard.vue +4 -4
- package/components/PressBackdrop.vue +2 -2
- package/components/PressCategory.vue +7 -4
- package/components/PressHome.vue +1 -1
- package/components/PressNavBar.vue +2 -2
- package/components/PressNavItemLink.vue +1 -1
- package/components/PressNavScreen.vue +2 -2
- package/components/PressOutlineItem.vue +8 -2
- package/components/PressSidebar.vue +73 -3
- package/components/PressSidebarItem.vue +218 -0
- package/components/PressSwitchAppearance.vue +2 -2
- package/composables/sidebar.ts +172 -0
- package/layouts/404.vue +12 -3
- package/layouts/default.vue +1 -1
- package/layouts/layout.vue +2 -2
- package/package.json +2 -2
- package/setup/main.ts +0 -47
- package/types/index.d.ts +45 -2
package/client/config.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const targetPadding =
|
|
1
|
+
export const targetPadding = 0
|
|
@@ -53,7 +53,7 @@ const localeTitle = computed(() => getLocaleTitle(locale.value, frontmatter.valu
|
|
|
53
53
|
>
|
|
54
54
|
<PressAuthor v-if="frontmatter.author" :frontmatter="frontmatter" />
|
|
55
55
|
<div class="divide-y divide-gray-200 xl:pb-0 xl:col-span-8 xl:row-span-2">
|
|
56
|
-
<
|
|
56
|
+
<RouterView />
|
|
57
57
|
</div>
|
|
58
58
|
|
|
59
59
|
<footer
|
|
@@ -70,9 +70,9 @@ const localeTitle = computed(() => getLocaleTitle(locale.value, frontmatter.valu
|
|
|
70
70
|
Next Article
|
|
71
71
|
</h2>
|
|
72
72
|
<div class="link">
|
|
73
|
-
<
|
|
73
|
+
<RouterLink :to="nextPost.href">
|
|
74
74
|
{{ nextPost.title }}
|
|
75
|
-
</
|
|
75
|
+
</RouterLink>
|
|
76
76
|
</div>
|
|
77
77
|
</div>
|
|
78
78
|
<div v-if="prevPost && prevPost.href" class="py-8">
|
|
@@ -80,15 +80,15 @@ const localeTitle = computed(() => getLocaleTitle(locale.value, frontmatter.valu
|
|
|
80
80
|
Previous Article
|
|
81
81
|
</h2>
|
|
82
82
|
<div class="link">
|
|
83
|
-
<
|
|
83
|
+
<RouterLink :to="prevPost.href">
|
|
84
84
|
{{ prevPost.title }}
|
|
85
|
-
</
|
|
85
|
+
</RouterLink>
|
|
86
86
|
</div>
|
|
87
87
|
</div>
|
|
88
88
|
<div class="pt-8">
|
|
89
|
-
<
|
|
89
|
+
<RouterLink class="link" to="/">
|
|
90
90
|
← Back to Home
|
|
91
|
-
</
|
|
91
|
+
</RouterLink>
|
|
92
92
|
</div>
|
|
93
93
|
</footer>
|
|
94
94
|
</div>
|
|
@@ -12,9 +12,9 @@ defineProps<{
|
|
|
12
12
|
<div class="space-y-5 xl:col-span-3">
|
|
13
13
|
<div class="space-y-6">
|
|
14
14
|
<h2 class="text-2xl leading-8 font-bold tracking-tight">
|
|
15
|
-
<
|
|
15
|
+
<RouterLink class="text-gray-900" :to="post.path || ''">
|
|
16
16
|
{{ post.title }}
|
|
17
|
-
</
|
|
17
|
+
</RouterLink>
|
|
18
18
|
</h2>
|
|
19
19
|
<div
|
|
20
20
|
v-if="post.excerpt"
|
|
@@ -23,9 +23,9 @@ defineProps<{
|
|
|
23
23
|
/>
|
|
24
24
|
</div>
|
|
25
25
|
<div class="text-base leading-6 font-medium">
|
|
26
|
-
<
|
|
26
|
+
<RouterLink class="link" aria-label="read more" :to="post.path || ''">
|
|
27
27
|
Read more →
|
|
28
|
-
</
|
|
28
|
+
</RouterLink>
|
|
29
29
|
</div>
|
|
30
30
|
</div>
|
|
31
31
|
</article>
|
|
@@ -32,7 +32,8 @@ function getTitle(post: Post | any) {
|
|
|
32
32
|
v-if="category.total"
|
|
33
33
|
p="t-2"
|
|
34
34
|
w="full" border="t t-$pr-c-divider-light"
|
|
35
|
-
class="category-list-item inline-flex items-center justify-between"
|
|
35
|
+
class="press-sidebar-item category-list-item inline-flex items-center justify-between"
|
|
36
|
+
text-14px
|
|
36
37
|
tabindex="0"
|
|
37
38
|
>
|
|
38
39
|
<span class="category-name" font="bold" m="l-1" @click="displayCategory ? displayCategory(category.name) : null">
|
|
@@ -41,7 +42,9 @@ function getTitle(post: Post | any) {
|
|
|
41
42
|
</span>
|
|
42
43
|
<button
|
|
43
44
|
tabindex="0" role="button" aria-label="toggle section"
|
|
44
|
-
class="folder-action inline-flex cursor-pointer"
|
|
45
|
+
class="caret folder-action inline-flex cursor-pointer"
|
|
46
|
+
text-base
|
|
47
|
+
@click="collapsable = !collapsable"
|
|
45
48
|
>
|
|
46
49
|
<div v-if="collapsable" i-ri-folder-add-line />
|
|
47
50
|
<div v-else i-ri-folder-reduce-line />
|
|
@@ -51,9 +54,9 @@ function getTitle(post: Post | any) {
|
|
|
51
54
|
<ul v-if="!collapsable">
|
|
52
55
|
<li v-for="categoryItem, i in category.children" :key="i" class="post-list-item">
|
|
53
56
|
<template v-if="!isCategoryList(categoryItem)">
|
|
54
|
-
<
|
|
57
|
+
<RouterLink v-if="categoryItem.title" :to="categoryItem.path || ''" class="inline-flex items-center" active-class="active">
|
|
55
58
|
<span m="l-1" text="sm">{{ getTitle(categoryItem) }}</span>
|
|
56
|
-
</
|
|
59
|
+
</RouterLink>
|
|
57
60
|
</template>
|
|
58
61
|
|
|
59
62
|
<PressCategory v-else :category="categoryItem" :display-category="displayCategory" :collapsable="collapsable" />
|
package/components/PressHome.vue
CHANGED
|
@@ -18,10 +18,10 @@ const themeConfig = useThemeConfig()
|
|
|
18
18
|
|
|
19
19
|
<template>
|
|
20
20
|
<div class="pr-navbar flex justify-between items-center pl-4 pr-2" :class="{ 'has-sidebar': hasSidebar }">
|
|
21
|
-
<
|
|
21
|
+
<RouterLink class="text-xl flex justify-center items-center" to="/" :aria-label="siteConfig.title">
|
|
22
22
|
<img v-if="themeConfig.logo" class="logo" :src="themeConfig.logo" alt="LOGO">
|
|
23
23
|
<span class="inline-flex">{{ siteConfig.title }}</span>
|
|
24
|
-
</
|
|
24
|
+
</RouterLink>
|
|
25
25
|
<div class="self-stretch flex justify-center items-center text-sm leading-5">
|
|
26
26
|
<PressNavBarSearch p="x-2" />
|
|
27
27
|
<PressNavBarMenu p="x-2" />
|
|
@@ -11,7 +11,7 @@ const { lockBodyScroll, unlockBodyScroll } = useBodyScrollLock(screen)
|
|
|
11
11
|
</script>
|
|
12
12
|
|
|
13
13
|
<template>
|
|
14
|
-
<
|
|
14
|
+
<Transition
|
|
15
15
|
name="fade"
|
|
16
16
|
@enter="lockBodyScroll"
|
|
17
17
|
@after-leave="unlockBodyScroll"
|
|
@@ -27,7 +27,7 @@ const { lockBodyScroll, unlockBodyScroll } = useBodyScrollLock(screen)
|
|
|
27
27
|
<slot name="nav-screen-content-after" />
|
|
28
28
|
</div>
|
|
29
29
|
</div>
|
|
30
|
-
</
|
|
30
|
+
</Transition>
|
|
31
31
|
</template>
|
|
32
32
|
|
|
33
33
|
<style scoped>
|
|
@@ -13,8 +13,14 @@ const { locale } = useI18n()
|
|
|
13
13
|
|
|
14
14
|
<template>
|
|
15
15
|
<ul :class="root ? 'root' : 'nested'" class="va-toc css-i18n-toc">
|
|
16
|
-
<li
|
|
17
|
-
|
|
16
|
+
<li
|
|
17
|
+
v-for="{ children, link, title, lang } in headers"
|
|
18
|
+
:key="link" class="va-toc-item"
|
|
19
|
+
:lang="lang || locale"
|
|
20
|
+
>
|
|
21
|
+
<RouterLink class="outline-link" :to="link" @click="onClick">
|
|
22
|
+
{{ title }}
|
|
23
|
+
</RouterLink>
|
|
18
24
|
<template v-if="children?.length">
|
|
19
25
|
<PressOutlineItem :headers="children" :on-click="onClick" />
|
|
20
26
|
</template>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import { computed } from 'vue'
|
|
3
|
-
import { removeItemFromCategory,
|
|
3
|
+
import { removeItemFromCategory, useCategories, usePageList, useSidebar } from 'valaxy'
|
|
4
4
|
import { useThemeConfig } from '../composables'
|
|
5
5
|
|
|
6
6
|
defineProps<{
|
|
@@ -10,8 +10,9 @@ defineProps<{
|
|
|
10
10
|
const pages = usePageList()
|
|
11
11
|
const themeConfig = useThemeConfig()
|
|
12
12
|
|
|
13
|
+
const sidebar = computed(() => themeConfig.value.sidebar)
|
|
13
14
|
const categories = computed(() => {
|
|
14
|
-
const cs =
|
|
15
|
+
const cs = useCategories('', pages.value)
|
|
15
16
|
const cList = cs.value
|
|
16
17
|
removeItemFromCategory(cList, 'Uncategorized')
|
|
17
18
|
|
|
@@ -25,6 +26,10 @@ const categories = computed(() => {
|
|
|
25
26
|
return cList
|
|
26
27
|
})
|
|
27
28
|
|
|
29
|
+
function getCategoryByName(name: string) {
|
|
30
|
+
return categories.value.children.find(c => c.name === name)
|
|
31
|
+
}
|
|
32
|
+
|
|
28
33
|
const { hasSidebar } = useSidebar()
|
|
29
34
|
</script>
|
|
30
35
|
|
|
@@ -35,7 +40,22 @@ const { hasSidebar } = useSidebar()
|
|
|
35
40
|
@click.stop
|
|
36
41
|
>
|
|
37
42
|
<div text="left" m="2">
|
|
38
|
-
<
|
|
43
|
+
<ul v-for="item in sidebar" :key="item" class="category-list">
|
|
44
|
+
<template v-if="typeof item === 'string'">
|
|
45
|
+
<PressCategory
|
|
46
|
+
v-if="getCategoryByName(item)"
|
|
47
|
+
:category="getCategoryByName(item)"
|
|
48
|
+
:collapsable="false"
|
|
49
|
+
/>
|
|
50
|
+
</template>
|
|
51
|
+
<PressSidebarItem
|
|
52
|
+
v-else
|
|
53
|
+
p="t-2"
|
|
54
|
+
border="t t-$pr-c-divider-light"
|
|
55
|
+
:item="item"
|
|
56
|
+
:depth="0"
|
|
57
|
+
/>
|
|
58
|
+
</ul>
|
|
39
59
|
</div>
|
|
40
60
|
</aside>
|
|
41
61
|
</template>
|
|
@@ -85,4 +105,54 @@ const { hasSidebar } = useSidebar()
|
|
|
85
105
|
top: 0;
|
|
86
106
|
}
|
|
87
107
|
}
|
|
108
|
+
|
|
109
|
+
.category-list {
|
|
110
|
+
&:first-child {
|
|
111
|
+
.category-list-item {
|
|
112
|
+
border-top: 0;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.post-list-item {
|
|
118
|
+
a {
|
|
119
|
+
color: var(--va-c-text-light);
|
|
120
|
+
transition: all 0.2s;
|
|
121
|
+
|
|
122
|
+
&:hover {
|
|
123
|
+
color: var(--va-c-primary);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
&.active {
|
|
127
|
+
color: var(--va-c-primary);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.press-sidebar-item {
|
|
133
|
+
.caret {
|
|
134
|
+
display: flex;
|
|
135
|
+
justify-content: center;
|
|
136
|
+
align-items: center;
|
|
137
|
+
margin-right: -7px;
|
|
138
|
+
width: 32px;
|
|
139
|
+
height: 32px;
|
|
140
|
+
color: var(--vp-c-text-3);
|
|
141
|
+
cursor: pointer;
|
|
142
|
+
transition: color 0.25s;
|
|
143
|
+
flex-shrink: 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
&:hover .caret {
|
|
147
|
+
color: var(--vp-c-text-2);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
&:hover .caret:hover {
|
|
151
|
+
color: var(--vp-c-text-1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.category-list+.category-list {
|
|
156
|
+
margin-top: 1rem;
|
|
157
|
+
}
|
|
88
158
|
</style>
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import type { DefaultTheme } from 'vitepress/theme'
|
|
4
|
+
import { useI18n } from 'vue-i18n'
|
|
5
|
+
import { useSidebarControl } from '../composables/sidebar'
|
|
6
|
+
|
|
7
|
+
const props = defineProps<{
|
|
8
|
+
item: DefaultTheme.SidebarItem
|
|
9
|
+
depth: number
|
|
10
|
+
}>()
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
collapsed,
|
|
14
|
+
collapsible,
|
|
15
|
+
isLink,
|
|
16
|
+
isActiveLink,
|
|
17
|
+
hasActiveLink,
|
|
18
|
+
hasChildren,
|
|
19
|
+
toggle,
|
|
20
|
+
} = useSidebarControl(computed(() => props.item))
|
|
21
|
+
|
|
22
|
+
const sectionTag = computed(() => (hasChildren.value ? 'section' : `div`))
|
|
23
|
+
|
|
24
|
+
const linkTag = computed(() => (isLink.value ? 'a' : 'div'))
|
|
25
|
+
|
|
26
|
+
const textTag = computed(() => {
|
|
27
|
+
return !hasChildren.value
|
|
28
|
+
? 'p'
|
|
29
|
+
: props.depth + 2 === 7
|
|
30
|
+
? 'p'
|
|
31
|
+
: `h${props.depth + 2}`
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const itemRole = computed(() => (isLink.value ? undefined : 'button'))
|
|
35
|
+
|
|
36
|
+
const classes = computed(() => [
|
|
37
|
+
[`level-${props.depth}`],
|
|
38
|
+
{ collapsible: collapsible.value },
|
|
39
|
+
{ collapsed: collapsed.value },
|
|
40
|
+
{ 'is-link': isLink.value },
|
|
41
|
+
{ 'is-active': isActiveLink.value },
|
|
42
|
+
{ 'has-active': hasActiveLink.value },
|
|
43
|
+
])
|
|
44
|
+
|
|
45
|
+
function onItemInteraction(e: MouseEvent | Event) {
|
|
46
|
+
if ('key' in e && e.key !== 'Enter')
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
!props.item.link && toggle()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function onCaretClick() {
|
|
53
|
+
props.item.link && toggle()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const { t } = useI18n()
|
|
57
|
+
|
|
58
|
+
const htmlText = computed(() => t(props.item.text || ''))
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<template>
|
|
62
|
+
<component
|
|
63
|
+
:is="sectionTag"
|
|
64
|
+
class="VPSidebarItem" :class="classes"
|
|
65
|
+
>
|
|
66
|
+
<div
|
|
67
|
+
v-if="item.text"
|
|
68
|
+
class="press-sidebar-item item"
|
|
69
|
+
:role="itemRole"
|
|
70
|
+
:tabindex="item.items && 0"
|
|
71
|
+
v-on="
|
|
72
|
+
item.items
|
|
73
|
+
? { click: onItemInteraction, keydown: onItemInteraction }
|
|
74
|
+
: {}
|
|
75
|
+
"
|
|
76
|
+
>
|
|
77
|
+
<div class="indicator" />
|
|
78
|
+
|
|
79
|
+
<AppLink
|
|
80
|
+
v-if="item.link"
|
|
81
|
+
:tag="linkTag"
|
|
82
|
+
class="link"
|
|
83
|
+
:href="item.link"
|
|
84
|
+
:rel="item.rel"
|
|
85
|
+
:target="item.target"
|
|
86
|
+
>
|
|
87
|
+
<!-- eslint-disable-next-line vue/no-v-text-v-html-on-component -->
|
|
88
|
+
<component :is="textTag" class="text ml-1" v-html="htmlText" />
|
|
89
|
+
</AppLink>
|
|
90
|
+
<!-- eslint-disable-next-line vue/no-v-text-v-html-on-component -->
|
|
91
|
+
<component :is="textTag" v-else class="text ml-1" v-html="htmlText" />
|
|
92
|
+
|
|
93
|
+
<button
|
|
94
|
+
v-if="item.collapsed != null"
|
|
95
|
+
tabindex="0" role="button" aria-label="toggle section"
|
|
96
|
+
class="caret folder-action inline-flex cursor-pointer"
|
|
97
|
+
@click="onCaretClick" @keydown.enter="onCaretClick"
|
|
98
|
+
>
|
|
99
|
+
<div v-if="collapsed" i-ri-folder-add-line />
|
|
100
|
+
<div v-else i-ri-folder-reduce-line />
|
|
101
|
+
</button>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<div v-if="item.items && item.items.length" class="items">
|
|
105
|
+
<template v-if="depth < 5">
|
|
106
|
+
<PressSidebarItem
|
|
107
|
+
v-for="i in item.items"
|
|
108
|
+
:key="i.text"
|
|
109
|
+
:item="i"
|
|
110
|
+
:depth="depth + 1"
|
|
111
|
+
/>
|
|
112
|
+
</template>
|
|
113
|
+
</div>
|
|
114
|
+
</component>
|
|
115
|
+
</template>
|
|
116
|
+
|
|
117
|
+
<style scoped>
|
|
118
|
+
.item {
|
|
119
|
+
position: relative;
|
|
120
|
+
display: flex;
|
|
121
|
+
width: 100%;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.VPSidebarItem.collapsible > .item {
|
|
125
|
+
cursor: pointer;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.indicator {
|
|
129
|
+
position: absolute;
|
|
130
|
+
top: 6px;
|
|
131
|
+
bottom: 6px;
|
|
132
|
+
left: -17px;
|
|
133
|
+
width: 2px;
|
|
134
|
+
border-radius: 2px;
|
|
135
|
+
transition: background-color 0.25s;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.VPSidebarItem.level-2.is-active > .item > .indicator,
|
|
139
|
+
.VPSidebarItem.level-3.is-active > .item > .indicator,
|
|
140
|
+
.VPSidebarItem.level-4.is-active > .item > .indicator,
|
|
141
|
+
.VPSidebarItem.level-5.is-active > .item > .indicator {
|
|
142
|
+
background-color: var(--vp-c-brand-1);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.link {
|
|
146
|
+
display: flex;
|
|
147
|
+
align-items: center;
|
|
148
|
+
flex-grow: 1;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.text {
|
|
152
|
+
flex-grow: 1;
|
|
153
|
+
padding: 4px 0;
|
|
154
|
+
line-height: 24px;
|
|
155
|
+
font-size: 14px;
|
|
156
|
+
transition: color 0.25s;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.VPSidebarItem.level-0 .text {
|
|
160
|
+
font-weight: 700;
|
|
161
|
+
color: var(--vp-c-text-1);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.VPSidebarItem.level-1 .text,
|
|
165
|
+
.VPSidebarItem.level-2 .text,
|
|
166
|
+
.VPSidebarItem.level-3 .text,
|
|
167
|
+
.VPSidebarItem.level-4 .text,
|
|
168
|
+
.VPSidebarItem.level-5 .text {
|
|
169
|
+
font-weight: 500;
|
|
170
|
+
color: var(--vp-c-text-2);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.VPSidebarItem.level-0.is-link > .item > .link:hover .text,
|
|
174
|
+
.VPSidebarItem.level-1.is-link > .item > .link:hover .text,
|
|
175
|
+
.VPSidebarItem.level-2.is-link > .item > .link:hover .text,
|
|
176
|
+
.VPSidebarItem.level-3.is-link > .item > .link:hover .text,
|
|
177
|
+
.VPSidebarItem.level-4.is-link > .item > .link:hover .text,
|
|
178
|
+
.VPSidebarItem.level-5.is-link > .item > .link:hover .text {
|
|
179
|
+
color: var(--vp-c-brand-1);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.VPSidebarItem.level-0.has-active > .item > .text,
|
|
183
|
+
.VPSidebarItem.level-1.has-active > .item > .text,
|
|
184
|
+
.VPSidebarItem.level-2.has-active > .item > .text,
|
|
185
|
+
.VPSidebarItem.level-3.has-active > .item > .text,
|
|
186
|
+
.VPSidebarItem.level-4.has-active > .item > .text,
|
|
187
|
+
.VPSidebarItem.level-5.has-active > .item > .text,
|
|
188
|
+
.VPSidebarItem.level-0.has-active > .item > .link > .text,
|
|
189
|
+
.VPSidebarItem.level-1.has-active > .item > .link > .text,
|
|
190
|
+
.VPSidebarItem.level-2.has-active > .item > .link > .text,
|
|
191
|
+
.VPSidebarItem.level-3.has-active > .item > .link > .text,
|
|
192
|
+
.VPSidebarItem.level-4.has-active > .item > .link > .text,
|
|
193
|
+
.VPSidebarItem.level-5.has-active > .item > .link > .text {
|
|
194
|
+
color: var(--vp-c-text-1);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.VPSidebarItem.level-0.is-active > .item .link > .text,
|
|
198
|
+
.VPSidebarItem.level-1.is-active > .item .link > .text,
|
|
199
|
+
.VPSidebarItem.level-2.is-active > .item .link > .text,
|
|
200
|
+
.VPSidebarItem.level-3.is-active > .item .link > .text,
|
|
201
|
+
.VPSidebarItem.level-4.is-active > .item .link > .text,
|
|
202
|
+
.VPSidebarItem.level-5.is-active > .item .link > .text {
|
|
203
|
+
color: var(--vp-c-brand-1);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.VPSidebarItem.level-1 .items,
|
|
207
|
+
.VPSidebarItem.level-2 .items,
|
|
208
|
+
.VPSidebarItem.level-3 .items,
|
|
209
|
+
.VPSidebarItem.level-4 .items,
|
|
210
|
+
.VPSidebarItem.level-5 .items {
|
|
211
|
+
border-left: 1px solid var(--vp-c-divider);
|
|
212
|
+
padding-left: 16px;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.VPSidebarItem.collapsed .items {
|
|
216
|
+
display: none;
|
|
217
|
+
}
|
|
218
|
+
</style>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { isDark,
|
|
2
|
+
import { isDark, toggleDarkWithTransition } from 'valaxy'
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<template>
|
|
6
|
-
<button class="switch switch-appearance" type="button" aria-label="Toggle Dark Mode" @click="
|
|
6
|
+
<button class="switch switch-appearance" type="button" aria-label="Toggle Dark Mode" @click="toggleDarkWithTransition">
|
|
7
7
|
<span class="check">
|
|
8
8
|
<span class="icon-wrap">
|
|
9
9
|
<div v-if="!isDark" class="icon" i-ri-sun-line />
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { isClient } from '@vueuse/core'
|
|
2
|
+
import type { DefaultTheme } from 'vitepress/theme'
|
|
3
|
+
import {
|
|
4
|
+
type ComputedRef,
|
|
5
|
+
type Ref,
|
|
6
|
+
computed,
|
|
7
|
+
onMounted,
|
|
8
|
+
onUnmounted,
|
|
9
|
+
ref,
|
|
10
|
+
watch,
|
|
11
|
+
watchEffect,
|
|
12
|
+
watchPostEffect,
|
|
13
|
+
} from 'vue'
|
|
14
|
+
|
|
15
|
+
import { useRoute } from 'vue-router'
|
|
16
|
+
|
|
17
|
+
import type { PressTheme } from 'valaxy-theme-press'
|
|
18
|
+
|
|
19
|
+
export interface SidebarControl {
|
|
20
|
+
collapsed: Ref<boolean>
|
|
21
|
+
collapsible: ComputedRef<boolean>
|
|
22
|
+
isLink: ComputedRef<boolean>
|
|
23
|
+
isActiveLink: Ref<boolean>
|
|
24
|
+
hasActiveLink: ComputedRef<boolean>
|
|
25
|
+
hasChildren: ComputedRef<boolean>
|
|
26
|
+
toggle(): void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const HASH_RE = /#.*$/
|
|
30
|
+
export const EXT_RE = /(index)?\.(md|html)$/
|
|
31
|
+
export function normalize(path: string): string {
|
|
32
|
+
return decodeURI(path).replace(HASH_RE, '').replace(EXT_RE, '')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function isActive(
|
|
36
|
+
currentPath: string,
|
|
37
|
+
matchPath?: string,
|
|
38
|
+
asRegex: boolean = false,
|
|
39
|
+
): boolean {
|
|
40
|
+
if (matchPath === undefined)
|
|
41
|
+
return false
|
|
42
|
+
|
|
43
|
+
currentPath = normalize(`/${currentPath}`)
|
|
44
|
+
|
|
45
|
+
if (asRegex)
|
|
46
|
+
return new RegExp(matchPath).test(currentPath)
|
|
47
|
+
|
|
48
|
+
if (normalize(matchPath) !== currentPath)
|
|
49
|
+
return false
|
|
50
|
+
|
|
51
|
+
const hashMatch = matchPath.match(HASH_RE)
|
|
52
|
+
|
|
53
|
+
if (hashMatch)
|
|
54
|
+
return (isClient ? location.hash : '') === hashMatch[0]
|
|
55
|
+
|
|
56
|
+
return true
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* a11y: cache the element that opened the Sidebar (the menu button) then
|
|
61
|
+
* focus that button again when Menu is closed with Escape key.
|
|
62
|
+
*/
|
|
63
|
+
export function useCloseSidebarOnEscape(
|
|
64
|
+
isOpen: Ref<boolean>,
|
|
65
|
+
close: () => void,
|
|
66
|
+
) {
|
|
67
|
+
let triggerElement: HTMLButtonElement | undefined
|
|
68
|
+
|
|
69
|
+
watchEffect(() => {
|
|
70
|
+
triggerElement = isOpen.value
|
|
71
|
+
? (document.activeElement as HTMLButtonElement)
|
|
72
|
+
: undefined
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
onMounted(() => {
|
|
76
|
+
window.addEventListener('keyup', onEscape)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
onUnmounted(() => {
|
|
80
|
+
window.removeEventListener('keyup', onEscape)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
function onEscape(e: KeyboardEvent) {
|
|
84
|
+
if (e.key === 'Escape' && isOpen.value) {
|
|
85
|
+
close()
|
|
86
|
+
triggerElement?.focus()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const hashRef = ref(isClient ? location.hash : '')
|
|
92
|
+
if (isClient) {
|
|
93
|
+
window.addEventListener('hashchange', () => {
|
|
94
|
+
hashRef.value = location.hash
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check if the given sidebar item contains any active link.
|
|
100
|
+
*/
|
|
101
|
+
export function containsActiveLink(
|
|
102
|
+
path: string,
|
|
103
|
+
items: PressTheme.SidebarItem | PressTheme.SidebarItem[],
|
|
104
|
+
): boolean {
|
|
105
|
+
if (Array.isArray(items))
|
|
106
|
+
return items.some(item => containsActiveLink(path, item))
|
|
107
|
+
|
|
108
|
+
return isActive(path, items.link)
|
|
109
|
+
? true
|
|
110
|
+
: items.items
|
|
111
|
+
? containsActiveLink(path, items.items)
|
|
112
|
+
: false
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function useSidebarControl(
|
|
116
|
+
item: ComputedRef<DefaultTheme.SidebarItem>,
|
|
117
|
+
): SidebarControl {
|
|
118
|
+
const collapsed = ref(false)
|
|
119
|
+
|
|
120
|
+
const collapsible = computed(() => {
|
|
121
|
+
return item.value.collapsed != null
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
const isLink = computed(() => {
|
|
125
|
+
return !!item.value.link
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const isActiveLink = ref(false)
|
|
129
|
+
const route = useRoute()
|
|
130
|
+
const updateIsActiveLink = () => {
|
|
131
|
+
isActiveLink.value = route.path === item.value.link
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
watch([route, item, hashRef], updateIsActiveLink)
|
|
135
|
+
onMounted(updateIsActiveLink)
|
|
136
|
+
|
|
137
|
+
const hasActiveLink = computed(() => {
|
|
138
|
+
if (isActiveLink.value)
|
|
139
|
+
return true
|
|
140
|
+
|
|
141
|
+
return item.value.items
|
|
142
|
+
? containsActiveLink(route.path, item.value.items)
|
|
143
|
+
: false
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
const hasChildren = computed(() => {
|
|
147
|
+
return !!(item.value.items && item.value.items.length)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
watchEffect(() => {
|
|
151
|
+
collapsed.value = !!(collapsible.value && item.value.collapsed)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
watchPostEffect(() => {
|
|
155
|
+
;(isActiveLink.value || hasActiveLink.value) && (collapsed.value = false)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
function toggle() {
|
|
159
|
+
if (collapsible.value)
|
|
160
|
+
collapsed.value = !collapsed.value
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
collapsed,
|
|
165
|
+
collapsible,
|
|
166
|
+
isLink,
|
|
167
|
+
isActiveLink,
|
|
168
|
+
hasActiveLink,
|
|
169
|
+
hasChildren,
|
|
170
|
+
toggle,
|
|
171
|
+
}
|
|
172
|
+
}
|
package/layouts/404.vue
CHANGED
|
@@ -12,10 +12,12 @@ const { t } = useI18n()
|
|
|
12
12
|
<div />
|
|
13
13
|
</template>
|
|
14
14
|
<div text="center" m="t-20">
|
|
15
|
-
<div
|
|
16
|
-
|
|
15
|
+
<div class="not-found" title="404" font="mono">
|
|
16
|
+
404
|
|
17
17
|
</div>
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
<RouterView />
|
|
20
|
+
<RouterView />
|
|
19
21
|
<div>
|
|
20
22
|
<button btn text-sm m="3 t8" @click="router.back()">
|
|
21
23
|
{{ t('button.back') }}
|
|
@@ -24,3 +26,10 @@ const { t } = useI18n()
|
|
|
24
26
|
</div>
|
|
25
27
|
</Layout>
|
|
26
28
|
</template>
|
|
29
|
+
|
|
30
|
+
<style lang="scss" scoped>
|
|
31
|
+
.not-found {
|
|
32
|
+
font-size: 10rem;
|
|
33
|
+
text-shadow: 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15);
|
|
34
|
+
}
|
|
35
|
+
</style>
|
package/layouts/default.vue
CHANGED
package/layouts/layout.vue
CHANGED
|
@@ -15,7 +15,7 @@ const layout = useLayout()
|
|
|
15
15
|
<PressBackdrop :show="isSidebarOpen" @click="closeSidebar" />
|
|
16
16
|
|
|
17
17
|
<slot>
|
|
18
|
-
<
|
|
18
|
+
<RouterView v-slot="{ Component }">
|
|
19
19
|
<component :is="asAny(Component)">
|
|
20
20
|
<template #main-header>
|
|
21
21
|
<slot name="main-header" />
|
|
@@ -49,7 +49,7 @@ const layout = useLayout()
|
|
|
49
49
|
<slot name="footer" />
|
|
50
50
|
</template>
|
|
51
51
|
</component>
|
|
52
|
-
</
|
|
52
|
+
</RouterView>
|
|
53
53
|
</slot>
|
|
54
54
|
|
|
55
55
|
<PressFooter />
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "valaxy-theme-press",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.11",
|
|
4
4
|
"description": "Docs Theme for Valaxy",
|
|
5
5
|
"author": {
|
|
6
6
|
"email": "me@yunyoujun.cn",
|
|
@@ -23,6 +23,6 @@
|
|
|
23
23
|
"@docsearch/js": "^3.5.2"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"valaxy": "0.15.
|
|
26
|
+
"valaxy": "0.15.11"
|
|
27
27
|
}
|
|
28
28
|
}
|
package/setup/main.ts
CHANGED
|
@@ -12,57 +12,11 @@ import 'vitepress/dist/client/theme-default/styles/components/custom-block.css'
|
|
|
12
12
|
|
|
13
13
|
// import 'vitepress/dist/client/theme-default/styles/components/vp-sponsor.css'
|
|
14
14
|
|
|
15
|
-
import { targetPadding } from '../client'
|
|
16
|
-
|
|
17
15
|
export default defineAppSetup((ctx) => {
|
|
18
16
|
const { router, isClient } = ctx
|
|
19
17
|
if (!isClient)
|
|
20
18
|
return
|
|
21
19
|
|
|
22
|
-
window.addEventListener(
|
|
23
|
-
'click',
|
|
24
|
-
async (e) => {
|
|
25
|
-
const link = (e.target as Element).closest('a')
|
|
26
|
-
if (link) {
|
|
27
|
-
const { protocol, hostname, pathname, hash, target } = link
|
|
28
|
-
const currentUrl = window.location
|
|
29
|
-
const extMatch = pathname.match(/\.\w+$/)
|
|
30
|
-
// only intercept inbound links
|
|
31
|
-
if (
|
|
32
|
-
!e.ctrlKey
|
|
33
|
-
&& !e.shiftKey
|
|
34
|
-
&& !e.altKey
|
|
35
|
-
&& !e.metaKey
|
|
36
|
-
&& target !== '_blank'
|
|
37
|
-
&& protocol === currentUrl.protocol
|
|
38
|
-
&& hostname === currentUrl.hostname
|
|
39
|
-
&& !(extMatch && extMatch[0] !== '.html')
|
|
40
|
-
) {
|
|
41
|
-
if (pathname === currentUrl.pathname) {
|
|
42
|
-
e.preventDefault()
|
|
43
|
-
// scroll between hash anchors in the same page
|
|
44
|
-
if (hash && hash !== currentUrl.hash) {
|
|
45
|
-
await router.push({ hash })
|
|
46
|
-
history.replaceState({ ...history.state }, '')
|
|
47
|
-
|
|
48
|
-
// still emit the event so we can listen to it in themes
|
|
49
|
-
window.dispatchEvent(new Event('hashchange'))
|
|
50
|
-
// use smooth scroll when clicking on header anchor links
|
|
51
|
-
scrollTo(link, hash, {
|
|
52
|
-
smooth: link.classList.contains('header-anchor'),
|
|
53
|
-
})
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
{ capture: true },
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
window.addEventListener('hashchange', (e) => {
|
|
63
|
-
e.preventDefault()
|
|
64
|
-
})
|
|
65
|
-
|
|
66
20
|
router.beforeEach((to, from) => {
|
|
67
21
|
if (to.path !== from.path)
|
|
68
22
|
return
|
|
@@ -70,7 +24,6 @@ export default defineAppSetup((ctx) => {
|
|
|
70
24
|
nextTick(() => {
|
|
71
25
|
scrollTo(document.body, to.hash, {
|
|
72
26
|
smooth: true,
|
|
73
|
-
targetPadding,
|
|
74
27
|
})
|
|
75
28
|
})
|
|
76
29
|
})
|
package/types/index.d.ts
CHANGED
|
@@ -30,6 +30,49 @@ export namespace PressTheme {
|
|
|
30
30
|
text?: string
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export type Sidebar = SidebarItem[] | SidebarMulti
|
|
34
|
+
export interface SidebarMulti {
|
|
35
|
+
[path: string]: SidebarItem[] | { items: SidebarItem[]; base: string }
|
|
36
|
+
}
|
|
37
|
+
export interface SidebarItem {
|
|
38
|
+
/**
|
|
39
|
+
* The text label of the item.
|
|
40
|
+
*/
|
|
41
|
+
text?: string
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The link of the item.
|
|
45
|
+
*/
|
|
46
|
+
link?: string
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The children of the item.
|
|
50
|
+
*/
|
|
51
|
+
items?: SidebarItem[]
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* If not specified, group is not collapsible.
|
|
55
|
+
*
|
|
56
|
+
* If `true`, group is collapsible and collapsed by default
|
|
57
|
+
*
|
|
58
|
+
* If `false`, group is collapsible but expanded by default
|
|
59
|
+
*/
|
|
60
|
+
collapsed?: boolean
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Base path for the children items.
|
|
64
|
+
*/
|
|
65
|
+
base?: string
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Customize text that appears on the footer of previous/next page.
|
|
69
|
+
*/
|
|
70
|
+
docFooterText?: string
|
|
71
|
+
|
|
72
|
+
rel?: string
|
|
73
|
+
target?: string
|
|
74
|
+
}
|
|
75
|
+
|
|
33
76
|
export interface Config {
|
|
34
77
|
logo: string
|
|
35
78
|
|
|
@@ -41,8 +84,8 @@ export namespace PressTheme {
|
|
|
41
84
|
primary: string
|
|
42
85
|
}
|
|
43
86
|
|
|
44
|
-
nav:
|
|
45
|
-
sidebar
|
|
87
|
+
nav: NavItem[]
|
|
88
|
+
sidebar?: Sidebar
|
|
46
89
|
|
|
47
90
|
editLink: EditLink
|
|
48
91
|
|