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 CHANGED
@@ -1 +1 @@
1
- export const targetPadding = -40
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
- <router-view />
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
- <router-link :to="nextPost.href">
73
+ <RouterLink :to="nextPost.href">
74
74
  {{ nextPost.title }}
75
- </router-link>
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
- <router-link :to="prevPost.href">
83
+ <RouterLink :to="prevPost.href">
84
84
  {{ prevPost.title }}
85
- </router-link>
85
+ </RouterLink>
86
86
  </div>
87
87
  </div>
88
88
  <div class="pt-8">
89
- <router-link class="link" to="/">
89
+ <RouterLink class="link" to="/">
90
90
  ← Back to Home
91
- </router-link>
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
- <router-link class="text-gray-900" :to="post.path || ''">
15
+ <RouterLink class="text-gray-900" :to="post.path || ''">
16
16
  {{ post.title }}
17
- </router-link>
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
- <router-link class="link" aria-label="read more" :to="post.path || ''">
26
+ <RouterLink class="link" aria-label="read more" :to="post.path || ''">
27
27
  Read more →
28
- </router-link>
28
+ </RouterLink>
29
29
  </div>
30
30
  </div>
31
31
  </article>
@@ -5,9 +5,9 @@ defineProps<{
5
5
  </script>
6
6
 
7
7
  <template>
8
- <transition name="fade">
8
+ <Transition name="fade">
9
9
  <div v-if="show" class="press-backdrop" />
10
- </transition>
10
+ </Transition>
11
11
  </template>
12
12
 
13
13
  <style scoped lang="scss">
@@ -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" opacity="50" @click="collapsable = !collapsable"
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
- <router-link v-if="categoryItem.title" :to="categoryItem.path || ''" class="inline-flex items-center" active-class="active">
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
- </router-link>
59
+ </RouterLink>
57
60
  </template>
58
61
 
59
62
  <PressCategory v-else :category="categoryItem" :display-category="displayCategory" :collapsable="collapsable" />
@@ -13,7 +13,7 @@
13
13
  <slot name="home-features-after" />
14
14
 
15
15
  <slot>
16
- <router-view />
16
+ <RouterView />
17
17
  </slot>
18
18
  </div>
19
19
  </template>
@@ -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
- <router-link class="text-xl flex justify-center items-center" to="/" :aria-label="siteConfig.title">
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
- </router-link>
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" />
@@ -14,7 +14,7 @@ const { t } = useI18n()
14
14
 
15
15
  <template>
16
16
  <AppLink
17
- class="press-nav-item-link"
17
+ class="press-nav-item-link flex justify-center items-center"
18
18
  :class="{
19
19
  active: route.path === item.link,
20
20
  }"
@@ -11,7 +11,7 @@ const { lockBodyScroll, unlockBodyScroll } = useBodyScrollLock(screen)
11
11
  </script>
12
12
 
13
13
  <template>
14
- <transition
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
- </transition>
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 v-for="{ children, link, title, lang } in headers" :key="link" class="va-toc-item" :lang="lang || locale">
17
- <a class="outline-link" :href="link" @click="onClick">{{ title }}</a>
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, useCategory, usePageList, useSidebar } from 'valaxy'
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 = useCategory('', pages.value)
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
- <PressCategories :categories="categories.children" :collapsable="false" />
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, toggleDark } from 'valaxy'
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="toggleDark()">
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 text-4xl>
16
- <div i-ri-alarm-warning-line inline-block />
15
+ <div class="not-found" title="404" font="mono">
16
+ 404
17
17
  </div>
18
- <router-view />
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>
@@ -1,5 +1,5 @@
1
1
  <template>
2
2
  <Layout>
3
- <router-view />
3
+ <RouterView />
4
4
  </Layout>
5
5
  </template>
@@ -15,7 +15,7 @@ const layout = useLayout()
15
15
  <PressBackdrop :show="isSidebarOpen" @click="closeSidebar" />
16
16
 
17
17
  <slot>
18
- <router-view v-slot="{ Component }">
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
- </router-view>
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.9",
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.9"
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: Array<NavItem>
45
- sidebar: string[]
87
+ nav: NavItem[]
88
+ sidebar?: Sidebar
46
89
 
47
90
  editLink: EditLink
48
91