valaxy-theme-press 0.0.2 → 0.0.3

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.
@@ -0,0 +1 @@
1
+ export * from '../composables'
@@ -10,9 +10,9 @@ const { t } = useI18n()
10
10
 
11
11
  <ul>
12
12
  <li>
13
- <a href="/docs" :title="t('docs.view_docs')">
13
+ <router-link to="/docs" :title="t('docs.view_docs')">
14
14
  {{ t('docs.view_docs') }}
15
- </a>
15
+ </router-link>
16
16
  </li>
17
17
  <li>
18
18
  <router-link class="flex justify-center" to="/examples">
@@ -17,15 +17,14 @@ const prevPost = computed(() => posts.value[findCurrentIndex() + 1])
17
17
  </script>
18
18
 
19
19
  <template>
20
- <article class="xl:divide-y xl:divide-gray-200">
21
- <header class="pt-6 xl:pb-10 space-y-1 text-center">
20
+ <article class="xl:divide-y xl:divide-gray-200 max-w-7xl m-auto" p="x-6" w="full">
21
+ <header class="pt-20 xl:pb-10 space-y-1 text-center">
22
22
  <PressDate :date="frontmatter.date" />
23
23
  <h1
24
24
  class="
25
25
  text-3xl
26
26
  leading-9
27
27
  font-extrabold
28
- text-gray-900
29
28
  tracking-tight
30
29
  sm:text-4xl sm:leading-10
31
30
  md:text-5xl md:leading-14
@@ -40,14 +39,14 @@ const prevPost = computed(() => posts.value[findCurrentIndex() + 1])
40
39
  divide-y
41
40
  xl:divide-y-0
42
41
  divide-gray-200
43
- xl:grid xl:grid-cols-4 xl:gap-x-10
42
+ xl:grid xl:grid-cols-12 xl:gap-x-6
44
43
  pb-16
45
44
  xl:pb-20
46
45
  "
47
46
  style="grid-template-rows: auto 1fr"
48
47
  >
49
48
  <PressAuthor v-if="frontmatter.author" :frontmatter="frontmatter" />
50
- <div class="divide-y divide-gray-200 xl:pb-0 xl:col-span-3 xl:row-span-2">
49
+ <div class="divide-y divide-gray-200 xl:pb-0 xl:col-span-8 xl:row-span-2">
51
50
  <router-view />
52
51
  </div>
53
52
 
@@ -57,7 +56,7 @@ const prevPost = computed(() => posts.value[findCurrentIndex() + 1])
57
56
  font-medium
58
57
  leading-5
59
58
  divide-y divide-gray-200
60
- xl:col-start-1 xl:row-start-2
59
+ xl:col-start-1 xl:row-start-2 xl:col-span-2
61
60
  "
62
61
  >
63
62
  <div v-if="nextPost" class="py-8">
@@ -65,19 +64,25 @@ const prevPost = computed(() => posts.value[findCurrentIndex() + 1])
65
64
  Next Article
66
65
  </h2>
67
66
  <div class="link">
68
- <a :href="nextPost.href">{{ nextPost.title }}</a>
67
+ <router-link :to="nextPost.href">
68
+ {{ nextPost.title }}
69
+ </router-link>
69
70
  </div>
70
71
  </div>
71
- <div v-if="prevPost" class="py-8">
72
+ <div v-if="prevPost && prevPost.href" class="py-8">
72
73
  <h2 class="text-xs tracking-wide uppercase text-gray-500">
73
74
  Previous Article
74
75
  </h2>
75
76
  <div class="link">
76
- <a :href="prevPost.href">{{ prevPost.title }}</a>
77
+ <router-link :to="prevPost.href">
78
+ {{ prevPost.title }}
79
+ </router-link>
77
80
  </div>
78
81
  </div>
79
82
  <div class="pt-8">
80
- <a class="link" href="/">← Back to the blog</a>
83
+ <router-link class="link" to="/">
84
+ ← Back to Home
85
+ </router-link>
81
86
  </div>
82
87
  </footer>
83
88
  </div>
@@ -12,7 +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
- <a class="text-gray-900" :href="post.path">{{ post.title }}</a>
15
+ <router-link class="text-gray-900" :to="post.path || ''">
16
+ {{ post.title }}
17
+ </router-link>
16
18
  </h2>
17
19
  <div
18
20
  v-if="post.excerpt"
@@ -21,7 +23,9 @@ defineProps<{
21
23
  />
22
24
  </div>
23
25
  <div class="text-base leading-6 font-medium">
24
- <a class="link" aria-label="read more" :href="post.path">Read more →</a>
26
+ <router-link class="link" aria-label="read more" :to="post.path || ''">
27
+ Read more →
28
+ </router-link>
25
29
  </div>
26
30
  </div>
27
31
  </article>
@@ -1,18 +1,16 @@
1
1
  <script lang="ts" setup>
2
- // import { useI18n } from 'vue-i18n'
3
- import { useData, useFrontmatter } from 'valaxy'
2
+ import { useFrontmatter } from 'valaxy'
4
3
  import { useAppStore } from 'valaxy/client/stores/app'
4
+ import PressOutline from './PressOutline.vue'
5
5
 
6
6
  const frontmatter = useFrontmatter()
7
- const data = useData()
8
- // const { t } = useI18n()
9
7
  const app = useAppStore()
10
8
  </script>
11
9
 
12
10
  <template>
13
11
  <button
14
- class="xl:hidden toc-btn shadow fixed press-icon-btn z-350"
15
- opacity="75" right="2" bottom="19"
12
+ class="toc-btn shadow-lg fixed press-icon-btn z-99 xl:hidden!"
13
+ right="5" bottom="8"
16
14
  @click="app.toggleRightSidebar()"
17
15
  >
18
16
  <div i-ri-file-list-line />
@@ -21,14 +19,14 @@ const app = useAppStore()
21
19
  <ValaxyOverlay :show="app.isRightSidebarOpen" @click="app.toggleRightSidebar()" />
22
20
 
23
21
  <aside
24
- class="press-aside lt-xl:fixed
25
- press-card xl:(shadow-none hover:shadow-none) shadow hover:shadow-lg"
26
- p="l-0 xl:l-4" text="center"
27
- z="10"
22
+ class="press-aside lt-xl:fixed shadow
23
+ press-card xl:(shadow-none hover:shadow-none) hover:shadow-lg"
24
+ p="l-0 xl:l-8" text="center"
25
+ z="5"
28
26
  :class="app.isRightSidebarOpen && 'open'"
29
27
  >
30
28
  <div class="aside-container lt-xl:fixed" flex="~ col">
31
- <PressToc v-if="frontmatter.toc !== false" :headers="data.headers || []" />
29
+ <PressOutline v-if="frontmatter.toc !== false" />
32
30
  <div class="flex-grow" />
33
31
  <div v-if="$slots.default" class="custom-container">
34
32
  <slot />
@@ -41,23 +39,24 @@ const app = useAppStore()
41
39
  @use 'valaxy/client/styles/mixins' as *;
42
40
 
43
41
  .press-card{
44
- box-shadow: none;
45
42
  background-color: var(--va-c-bg);
46
43
  }
47
44
 
48
45
  .press-aside {
49
- position: relative;
50
- min-width: 272px;
51
- transform: translateX(100%);
52
46
  top: 0;
53
47
  bottom: 0;
54
48
  right: 0;
55
- z-index: 10;
49
+ z-index: var(--pr-z-index-aside);
50
+ width: var(--va-aside-width);
51
+
52
+ transform: translateX(100%);
56
53
 
57
54
  transition: box-shadow var(--va-transition-duration), opacity 0.25s,
58
55
  transform var(--va-transition-duration) cubic-bezier(0.19, 1, 0.22, 1);
59
56
 
60
57
  &.open {
58
+ position: fixed;
59
+ right: 0;
61
60
  display: block;
62
61
  transform: translateX(0);
63
62
  }
@@ -65,10 +64,10 @@ const app = useAppStore()
65
64
 
66
65
  .aside-container {
67
66
  position: sticky;
68
- top: calc(var(--pr-nav-height) + 32px);
69
- margin-top: calc(var(--pr-nav-height) * -1 - 32px);
70
- padding-top: calc(var(--pr-nav-height) + 32px);
71
- height: 100vh;
67
+ top: 0;
68
+ margin-top: calc(var(--pr-nav-height) * -1 - 20px);
69
+ padding-top: calc(var(--pr-nav-height) + 20px);
70
+ height: calc(100vh - var(--pr-nav-height) - 32px);
72
71
  }
73
72
 
74
73
  @include media('xl') {
@@ -82,7 +81,7 @@ const app = useAppStore()
82
81
  }
83
82
 
84
83
  .toc-btn {
85
- color: var(--va-c-primary);
86
- background-color: var(--va-c-bg);
84
+ color: var(--va-c-bg);
85
+ background-color: var(--va-c-primary);
87
86
  }
88
87
  </style>
@@ -1,7 +1,5 @@
1
1
  <script lang="ts" setup>
2
2
  import { computed } from 'vue'
3
- import { useRouter } from 'vue-router'
4
- import { EXTERNAL_URL_RE } from 'valaxy'
5
3
 
6
4
  const props = defineProps<{
7
5
  theme: 'brand' | 'alt'
@@ -9,9 +7,6 @@ const props = defineProps<{
9
7
  text: string
10
8
  }>()
11
9
 
12
- const isUrl = computed(() => EXTERNAL_URL_RE.test(props.link))
13
- const router = useRouter()
14
-
15
10
  const classes = computed(() => {
16
11
  const arr = []
17
12
  if (props.theme === 'brand')
@@ -24,15 +19,13 @@ const classes = computed(() => {
24
19
  </script>
25
20
 
26
21
  <template>
27
- <component
28
- :is="isUrl ? 'a' : 'button'"
22
+ <AppLink
23
+ :to="link"
29
24
  m="2"
30
25
  :class="classes"
31
- class="sese-btn btn" bg="gradient-to-r"
32
- :href="isUrl ? link : undefined"
33
- :target="isUrl ? '_blank' : undefined"
34
- @click="!isUrl && router.push(link)"
26
+ class="sese-btn btn rounded-full hover:shadow-lg" bg="gradient-to-r"
27
+ p="x-6"
35
28
  >
36
29
  {{ text }}
37
- </component>
30
+ </AppLink>
38
31
  </template>
@@ -1,6 +1,7 @@
1
1
  <script lang="ts" setup>
2
+ import { useThemeConfig } from 'valaxy'
2
3
  import type { Categories } from 'valaxy'
3
- import { ref } from 'vue'
4
+ import { computed, ref } from 'vue'
4
5
 
5
6
  const props = withDefaults(defineProps<{
6
7
  categories: Categories
@@ -16,15 +17,31 @@ const props = withDefaults(defineProps<{
16
17
  })
17
18
 
18
19
  const collapsable = ref(props.collapsable)
20
+
21
+ const themeConfig = useThemeConfig()
22
+ const sidebar = computed(() => themeConfig.value.sidebar)
19
23
  </script>
20
24
 
21
25
  <template>
22
- <ul v-for="category, key in Object.fromEntries(categories)" :key="key" class="category-list" m="l-4">
23
- <PressCategory :name="key.toString()" :category="category" :level="level + 1" :display-category="displayCategory" :collapsable="collapsable" />
26
+ <ul v-for="item in sidebar" :key="item" class="category-list">
27
+ <PressCategory
28
+ v-if="categories.get(item)"
29
+ :name="item" :category="categories.get(item)"
30
+ :level="level + 1"
31
+ :display-category="displayCategory"
32
+ :collapsable="collapsable"
33
+ />
24
34
  </ul>
25
35
  </template>
26
36
 
27
37
  <style lang="scss">
38
+ .category-list {
39
+ &:first-child {
40
+ .category-list-item {
41
+ border-top: 0px;
42
+ }
43
+ }
44
+ }
28
45
  .post-list-item {
29
46
  a {
30
47
  color: var(--va-c-text-light);
@@ -47,4 +64,8 @@ const collapsable = ref(props.collapsable)
47
64
  }
48
65
  }
49
66
  }
67
+
68
+ .category-list+.category-list {
69
+ margin-top: 1rem;
70
+ }
50
71
  </style>
@@ -23,24 +23,31 @@ const collapsable = ref(props.collapsable)
23
23
  const { t, locale } = useI18n()
24
24
 
25
25
  const getTitle = (post: Post | any) => {
26
- const lang = locale.value === 'zh-CN' ? 'zh' : locale.value
26
+ const lang = locale.value
27
27
  return post[`title_${lang}`] ? post[`title_${lang}`] : post.title
28
28
  }
29
29
  </script>
30
30
 
31
31
  <template>
32
- <li v-if="category.total" class="category-list-item inline-flex items-center">
33
- <span class="folder-action inline-flex cursor-pointer" @click="collapsable = !collapsable">
32
+ <li
33
+ v-if="category.total"
34
+ p="t-2"
35
+ w="full" border="t t-$pr-c-divider-light"
36
+ class="category-list-item inline-flex items-center justify-between"
37
+ >
38
+ <span class="category-name" font="bold" m="l-1" @click="displayCategory ? displayCategory(name) : null">
39
+ {{ name === 'Uncategorized' ? t('category.uncategorized') : name }}
40
+ <!-- <sup font="normal">[{{ category.total }}]</sup> -->
41
+ </span>
42
+ <span class="folder-action inline-flex cursor-pointer" opacity="50" @click="collapsable = !collapsable">
34
43
  <div v-if="collapsable" i-ri-folder-add-line />
35
- <div v-else style="color:var(--va-c-primary)" i-ri-folder-reduce-line /></span>
36
- <span class="category-name" m="l-1" @click="displayCategory ? displayCategory(name) : null">
37
- {{ name === 'Uncategorized' ? t('category.uncategorized') : name }} [{{ category.total }}]
44
+ <div v-else i-ri-folder-reduce-line />
38
45
  </span>
39
46
  </li>
40
47
 
41
48
  <template v-if="!collapsable">
42
49
  <ul v-if="!isParentCategory(category)">
43
- <li v-for="post, i in category.posts" :key="i" class="post-list-item" m="l-4">
50
+ <li v-for="post, i in category.posts" :key="i" class="post-list-item">
44
51
  <router-link v-if="post.title" :to="post.path || ''" class="inline-flex items-center" active-class="active">
45
52
  <span m="l-1" text="sm">{{ getTitle(post) }}</span>
46
53
  </router-link>
@@ -0,0 +1,15 @@
1
+ <script lang="ts" setup>
2
+ import { useEditLink } from '../composables'
3
+
4
+ const editLink = useEditLink()
5
+ </script>
6
+
7
+ <template>
8
+ <div flex justify="between" text="sm">
9
+ <a flex items="center" :href="editLink.url" target="_blank">
10
+ <div i-ri-external-link-line />
11
+ <span ml-1>{{ editLink.text }}</span>
12
+ </a>
13
+ <PressDocFooterLastUpdated />
14
+ </div>
15
+ </template>
@@ -0,0 +1,44 @@
1
+ <script setup lang="ts">
2
+ import { computed, onMounted, ref, watchEffect } from 'vue'
3
+ import { useData, useThemeConfig } from 'valaxy'
4
+
5
+ const data = useData()
6
+ const themeConfig = useThemeConfig()
7
+
8
+ const date = computed(() => new Date(data.lastUpdated!))
9
+ const isoDatetime = computed(() => date.value.toISOString())
10
+ const datetime = ref('')
11
+
12
+ // set time on mounted hook because the locale string might be different
13
+ // based on end user and will lead to potential hydration mismatch if
14
+ // calculated at build time
15
+ onMounted(() => {
16
+ watchEffect(() => {
17
+ datetime.value = date.value.toLocaleString(window.navigator.language)
18
+ })
19
+ })
20
+ </script>
21
+
22
+ <template>
23
+ <p class="press-lastUpdated">
24
+ {{ themeConfig.lastUpdatedText ?? 'Last updated' }}:
25
+ <time :datetime="isoDatetime">{{ datetime }}</time>
26
+ </p>
27
+ </template>
28
+
29
+ <style scoped>
30
+ .press-lastUpdated {
31
+ line-height: 24px;
32
+ font-size: 14px;
33
+ font-weight: 500;
34
+ color: var(--va-c-text-light);
35
+ }
36
+
37
+ @media (min-width: 640px) {
38
+ .press-lastUpdated {
39
+ line-height: 32px;
40
+ font-size: 14px;
41
+ font-weight: 500;
42
+ }
43
+ }
44
+ </style>
@@ -56,6 +56,6 @@ defineProps<{
56
56
  line-height: 24px;
57
57
  font-size: 14px;
58
58
  font-weight: 500;
59
- color: var(--va-c-text-light);
59
+ color: var(--va-c-text-lighter);
60
60
  }
61
61
  </style>
@@ -0,0 +1,53 @@
1
+ <script setup lang="ts">
2
+ import { useSidebar } from 'valaxy'
3
+ import { useThemeConfig } from '../composables'
4
+
5
+ const themeConfig = useThemeConfig()
6
+ const { hasSidebar } = useSidebar()
7
+ </script>
8
+
9
+ <template>
10
+ <footer v-if="themeConfig.footer" class="press-footer" :class="{ 'has-sidebar': hasSidebar }">
11
+ <div flex="~ col" class="container">
12
+ <p v-if="themeConfig.footer.message" class="message" v-html="themeConfig.footer.message" />
13
+ <p v-if="themeConfig.footer.copyright" class="copyright" v-html="themeConfig.footer.copyright" />
14
+ </div>
15
+ </footer>
16
+ </template>
17
+
18
+ <style lang="scss" scoped>
19
+ .press-footer {
20
+ position: relative;
21
+ z-index: var(--pr-z-index-footer);
22
+ border-top: 1px solid var(--pr-c-divider-light);
23
+ padding: 32px 24px;
24
+ background-color: var(--va-c-bg);
25
+
26
+ &.has-sidebar {
27
+ display: none;
28
+ }
29
+ }
30
+
31
+ @media (min-width: 768px) {
32
+ .VPFooter {
33
+ padding: 32px;
34
+ }
35
+ }
36
+
37
+ .container {
38
+ margin: 0 auto;
39
+ max-width: var(--pr-layout-max-width);
40
+ text-align: center;
41
+ }
42
+
43
+ .message,
44
+ .copyright {
45
+ line-height: 24px;
46
+ font-size: 14px;
47
+ font-weight: 500;
48
+ color: var(--va-c-text-lighter);
49
+ }
50
+
51
+ .message { order: 2; }
52
+ .copyright { order: 1; }
53
+ </style>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="press-home">
2
+ <div class="press-home" p="x-6">
3
3
  <slot name="home-hero-before" />
4
4
  <PressHomeHero />
5
5
  <slot name="home-hero-after" />
@@ -11,11 +11,5 @@
11
11
  <slot>
12
12
  <router-view />
13
13
  </slot>
14
-
15
- <p align="center">
16
- <a href="https://sponsors.yunyoujun.cn">
17
- <img src="https://sponsors.yunyoujun.cn/sponsors.svg">
18
- </a>
19
- </p>
20
14
  </div>
21
15
  </template>
@@ -1,38 +1,25 @@
1
1
  <script setup lang="ts">
2
- import { computed, ref } from 'vue'
3
- import { useI18n } from 'vue-i18n'
4
- import type { Header } from 'valaxy'
2
+ import { ref } from 'vue'
5
3
  import {
6
- resolveHeaders,
7
4
  useActiveAnchor,
8
- useFrontmatter,
5
+ useOutline,
9
6
  } from 'valaxy'
7
+ import { useI18n } from 'vue-i18n'
10
8
  import { useThemeConfig } from '../composables'
11
9
 
12
- const props = defineProps<{ headers: Header[] }>()
13
-
14
- const frontmatter = useFrontmatter()
10
+ const { t } = useI18n()
15
11
  const themeConfig = useThemeConfig()
16
12
 
17
- const { locale, t } = useI18n()
18
13
  const container = ref()
19
14
  const marker = ref()
20
15
 
21
16
  useActiveAnchor(container, marker)
22
17
 
23
- const resolvedHeaders = computed(() => {
24
- return resolveHeaders(props.headers || [])
25
- })
26
-
27
- function handleClick({ target: el }: Event) {
28
- const id = `#${(el as HTMLAnchorElement).href!.split('#')[1]}`
29
- const heading = document.querySelector(decodeURIComponent(id)) as HTMLAnchorElement
30
- heading?.focus()
31
- }
18
+ const { headers, handleClick } = useOutline()
32
19
  </script>
33
20
 
34
21
  <template>
35
- <div v-show="headers.length" ref="container" p="t-6">
22
+ <div v-show="headers.length" ref="container">
36
23
  <div class="content">
37
24
  <div class="outline-title">
38
25
  {{ themeConfig.outlineTitle || t('sidebar.toc') }}
@@ -45,26 +32,12 @@ function handleClick({ target: el }: Event) {
45
32
  Table of Contents for current page
46
33
  </span>
47
34
 
48
- <ul class="va-toc relative z-1">
49
- <li
50
- v-for="{ text, link, children, hidden, lang } in resolvedHeaders"
51
- v-show="!hidden"
52
- :key="link"
53
- class="va-toc-item"
54
- :lang="lang || locale"
55
- >
56
- <a class="outline-link" :href="link" @click="handleClick">
57
- {{ text }}
58
- </a>
59
- <ul v-if="children && frontmatter.outline === 'deep'">
60
- <li v-for="item in children" v-show="!item.hidden" :key="item.link" :lang="lang || locale">
61
- <a class="outline-link" p="l-3" :href="link" @click="handleClick">
62
- {{ item.text }}
63
- </a>
64
- </li>
65
- </ul>
66
- </li>
67
- </ul>
35
+ <PressOutlineItem
36
+ class="va-toc relative z-1"
37
+ :headers="headers"
38
+ :on-click="handleClick"
39
+ root
40
+ />
68
41
  </nav>
69
42
  </div>
70
43
  </div>
@@ -106,6 +79,7 @@ function handleClick({ target: el }: Event) {
106
79
  line-height: 28px;
107
80
  font-size: 14px;
108
81
  font-weight: 600;
82
+ color: var(--pr-c-text-1);
109
83
  }
110
84
 
111
85
  .outline-link {
@@ -0,0 +1,48 @@
1
+ <script setup lang="ts">
2
+ import type { MenuItem } from 'valaxy'
3
+ import { useI18n } from 'vue-i18n'
4
+
5
+ defineProps<{
6
+ headers: MenuItem[]
7
+ onClick: (e: MouseEvent) => void
8
+ root?: boolean
9
+ }>()
10
+
11
+ const { locale } = useI18n()
12
+ </script>
13
+
14
+ <template>
15
+ <ul :class="root ? 'root' : 'nested'">
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>
18
+ <template v-if="children?.length">
19
+ <PressOutlineItem :headers="children" :on-click="onClick" />
20
+ </template>
21
+ </li>
22
+ </ul>
23
+ </template>
24
+
25
+ <style lang="scss" scoped>
26
+ .va-toc {
27
+ .va-toc-item {
28
+ .outline-link {
29
+ color: var(--va-c-text-light);
30
+ white-space: nowrap;
31
+ overflow: hidden;
32
+ text-overflow: ellipsis;
33
+ transition: color 0.5s;
34
+
35
+ &:hover,
36
+ &.active {
37
+ color: var(--va-c-brand);
38
+ transition: color 0.25s;
39
+ }
40
+
41
+ }
42
+
43
+ .nested {
44
+ padding-left: 0.8rem;
45
+ }
46
+ }
47
+ }
48
+ </style>
@@ -1,13 +1,28 @@
1
1
  <script lang="ts" setup>
2
+ import { computed } from 'vue'
2
3
  import { useCategory, usePageList, useSidebar } from 'valaxy'
4
+ import { useThemeConfig } from '../composables'
3
5
 
4
6
  defineProps<{
5
7
  open: boolean
6
8
  }>()
7
9
 
8
10
  const pages = usePageList()
9
- const categories = useCategory('', pages.value)
10
- categories.children.delete('Uncategorized')
11
+ const themeConfig = useThemeConfig()
12
+
13
+ const categories = computed(() => {
14
+ const cs = useCategory('', pages.value)
15
+ cs.children.delete('Uncategorized')
16
+
17
+ const sidebar = themeConfig.value.sidebar
18
+ if (sidebar) {
19
+ cs.children.forEach((_, key) => {
20
+ if (!themeConfig.value.sidebar.includes(key))
21
+ cs.children.delete(key)
22
+ })
23
+ }
24
+ return cs
25
+ })
11
26
 
12
27
  const { hasSidebar } = useSidebar()
13
28
  </script>
@@ -32,8 +47,8 @@ const { hasSidebar } = useSidebar()
32
47
  top: 0;
33
48
  bottom: 0;
34
49
  left: 0;
35
- padding: 1.5rem 1rem;
36
- padding-top: var(--pr-nav-height);
50
+ padding: 1rem;
51
+ top: var(--pr-nav-height);
37
52
  z-index: var(--pr-z-index-sidebar);
38
53
  width: calc(100vw - 64px);
39
54
  max-width: 320px;
@@ -41,6 +56,7 @@ const { hasSidebar } = useSidebar()
41
56
  opacity: 0;
42
57
  overflow-x: hidden;
43
58
  overflow-y: auto;
59
+ overflow-y: overlay;
44
60
  transform: translateX(-100%);
45
61
  transition: opacity 0.5s, transform 0.25s ease;
46
62
 
@@ -55,8 +71,6 @@ const { hasSidebar } = useSidebar()
55
71
  @include media('md') {
56
72
  .press-sidebar {
57
73
  z-index: 1;
58
- padding: 1.5rem 1rem;
59
- padding-top: var(--pr-nav-height);
60
74
  width: var(--va-sidebar-width);
61
75
  max-width: 100%;
62
76
  background-color: var(--va-c-bg-alt);
@@ -65,4 +79,10 @@ const { hasSidebar } = useSidebar()
65
79
  transform: translateX(0);
66
80
  }
67
81
  }
82
+
83
+ @include mobile {
84
+ .press-sidebar {
85
+ top: 0;
86
+ }
87
+ }
68
88
  </style>
@@ -20,23 +20,28 @@ const isHome = useLayout('home')
20
20
  'has-sidebar': hasSidebar,
21
21
  }"
22
22
  >
23
- <div w="full" flex="~" p="t-8 x-6 md:x-8">
23
+ <div
24
+ w="full" flex="~" :class="{
25
+ 'px-6 md:pl-12': hasSidebar,
26
+ }" p="t-4"
27
+ >
24
28
  <slot name="main">
25
- <div class="content" m="auto y-0" flex="~ col grow" w="full" p="x-12 lt-md:0">
29
+ <div class="content" w="full" :class="{ 'm-auto': !hasSidebar }" flex="~ col grow" p="lt-md:0">
26
30
  <slot name="main-header" />
27
31
  <slot name="main-header-after" />
28
32
 
29
33
  <slot name="main-content">
30
- <div class="markdown-body prose max-w-none pb-8">
31
- <ValaxyMd :frontmatter="frontmatter">
32
- <h1 v-if="!isHome && frontmatter.title" :id="frontmatter.title" tabindex="-1">
33
- {{ frontmatter.title }}
34
- <a class="header-anchor" :href="`#${frontmatter.title}`" aria-hidden="true">#</a>
35
- </h1>
36
- <slot name="main-content-md" />
37
- <slot />
38
- </ValaxyMd>
39
- </div>
34
+ <ValaxyMd class="prose mx-auto w-full max-w-4xl" :frontmatter="frontmatter">
35
+ <h1 v-if="hasSidebar && !isHome && frontmatter.title" :id="frontmatter.title" tabindex="-1">
36
+ {{ frontmatter.title }}
37
+ <a class="header-anchor" :href="`#${frontmatter.title}`" aria-hidden="true">#</a>
38
+ </h1>
39
+ <slot name="main-content-md" />
40
+ <slot />
41
+ </ValaxyMd>
42
+
43
+ <PressDocFooter v-if="!isHome" class="pb-8 max-w-4xl" w="full" m="auto" />
44
+
40
45
  <slot name="main-content-after" />
41
46
  </slot>
42
47
  </div>
@@ -2,6 +2,8 @@
2
2
  import { useConfig, useSidebar } from 'valaxy'
3
3
  import { useThemeConfig } from '../../composables'
4
4
  import PressSwitchAppearance from './PressSwitchAppearance.vue'
5
+ import PressNavItemLink from './PressNavItemLink.vue'
6
+ import PressNavItemGroup from './PressNavItemGroup.vue'
5
7
 
6
8
  defineProps<{
7
9
  isScreenOpen?: boolean
@@ -19,36 +21,38 @@ const themeConfig = useThemeConfig()
19
21
 
20
22
  <template>
21
23
  <div class="press-navbar flex justify-between items-center px-6 py-4" :class="{ 'has-sidebar': hasSidebar }">
22
- <a class="text-xl" href="/" :aria-label="config.title">
24
+ <router-link class="text-xl" to="/" :aria-label="config.title">
23
25
  <span class="md:inline">{{ config.title }}</span>
24
- </a>
25
- <div class="flex justify-center items-centertext-sm leading-5">
26
- <template v-for="(item, i) in themeConfig.nav" :key="i">
27
- <a
28
- class="hover:text-gray-700"
29
- :href="item.link"
30
- target="_blank"
31
- rel="noopener"
32
- >{{ item.text }}</a>
33
-
34
- <span v-if="i !== themeConfig.nav.length - 1" class="mr-2 ml-2">·</span>
26
+ </router-link>
27
+ <div class="self-stretch flex justify-center items-center text-sm leading-5">
28
+ <template v-for="item in themeConfig.nav" :key="item.text">
29
+ <PressNavItemLink v-if="'link' in item" class="px-2" :item="item" />
30
+ <PressNavItemGroup v-else class="px-2" :item="item" />
35
31
  </template>
36
-
37
- <PressToggleLocale m="x-2" />
32
+ <PressToggleLocale p="x-2" />
38
33
  <PressSwitchAppearance m="l-2" />
39
34
  </div>
40
35
  </div>
41
36
  </template>
42
37
 
43
- <style lang="scss" scoped>
38
+ <style lang="scss">
44
39
  @use 'valaxy/client/styles/mixins' as *;
45
40
 
41
+ :root {
42
+ --pr-navbar-c-bg: rgba(255, 255, 255, 0.8);
43
+ }
44
+
45
+ .dark {
46
+ --pr-navbar-c-bg: rgba(24, 24, 24, 0.8);
47
+ }
48
+
46
49
  .press-navbar {
47
50
  position: relative;
48
51
  border-bottom: 1px solid var(--pr-c-divider-light);
49
52
  padding: 0 8px 0 24px;
50
53
  height: var(--pr-nav-height);
51
- transition: border-color 0.5s, background-color 0.5s;
54
+ transition: border-color 0.5s;
55
+ background-color: var(--pr-navbar-c-bg);
52
56
  }
53
57
 
54
58
  @include media('md') {
@@ -63,11 +67,6 @@ const themeConfig = useThemeConfig()
63
67
  padding-right: 32px;
64
68
  -webkit-backdrop-filter: saturate(50%) blur(8px);
65
69
  backdrop-filter: saturate(50%) blur(8px);
66
- background: rgba(255, 255, 255, 0.7);
67
- }
68
-
69
- .dark .press-navbar.has-sidebar .content {
70
- background: rgba(36, 36, 36, 0.7);
71
70
  }
72
71
 
73
72
  @supports not (backdrop-filter: saturate(50%) blur(8px)) {
@@ -88,13 +87,6 @@ const themeConfig = useThemeConfig()
88
87
  max-width: calc(var(--pr-layout-max-width) - 64px);
89
88
  }
90
89
 
91
- .content {
92
- display: flex;
93
- justify-content: flex-end;
94
- align-items: center;
95
- flex-grow: 1;
96
- }
97
-
98
90
  .menu + .translations::before,
99
91
  .menu + .appearance::before,
100
92
  .menu + .social-links::before,
@@ -116,8 +108,4 @@ const themeConfig = useThemeConfig()
116
108
  .appearance + .social-links::before {
117
109
  margin-left: 16px;
118
110
  }
119
-
120
- .social-links {
121
- margin-right: -8px;
122
- }
123
111
  </style>
@@ -0,0 +1,101 @@
1
+ <script lang="ts" setup>
2
+ import { ref } from 'vue'
3
+ import type { NavItemGroup } from '../../types'
4
+ defineProps<{
5
+ item: NavItemGroup
6
+ }>()
7
+
8
+ const open = ref(false)
9
+ </script>
10
+
11
+ <template>
12
+ <div
13
+ ref="el"
14
+ class="flex self-stretch relative group"
15
+ :aria-expanded="open"
16
+ aria-haspopup="true"
17
+ @mouseenter="open = true"
18
+ @mouseleave="open = false"
19
+ >
20
+ <button
21
+ type="button"
22
+ class="button flex items-center"
23
+ @click="open = !open"
24
+ >
25
+ <span class="text">
26
+ {{ item.text }}
27
+ </span>
28
+ <div i-ri-arrow-drop-down-line />
29
+ </button>
30
+
31
+ <div class="menu grow" flex="~ col" items="start">
32
+ <AppLink v-for="itemLink in item.items" :key="itemLink.text" class="menu-item" p="x-3" :to="itemLink.link">
33
+ {{ itemLink.text }}
34
+ </AppLink>
35
+ </div>
36
+ </div>
37
+ </template>
38
+
39
+ <style lang="scss" scoped>
40
+ .group .button{
41
+ color: var(--pr-nav-text);
42
+ font-weight: 500;
43
+ font-size: 14px;
44
+ }
45
+
46
+ .group[aria-expanded="true"] .button{
47
+ color: rgba(60, 60, 60, 0.70);
48
+ transition: color 0.25s;
49
+
50
+ .dark &{
51
+ color: rgba(235, 235, 235, 0.6)
52
+ }
53
+ }
54
+
55
+ .menu {
56
+ position: absolute;
57
+ left: 50%;
58
+
59
+ min-width: 128px;
60
+ opacity: 0;
61
+ visibility: hidden;
62
+ transition: opacity 0.25s, visibility 0.25s, transform 0.25s;
63
+ transform: translateX(-50%) translateY(calc(var(--pr-nav-height) / 2));
64
+
65
+ border-radius: 12px;
66
+ padding: 12px;
67
+ border: 1px solid rgba(60, 60, 60, 0.12);
68
+ background-color: #ffffff;
69
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.1), 0 2px 6px rgba(0, 0, 0, 0.08);
70
+ .dark &{
71
+ background-color: #242424;
72
+ }
73
+
74
+ &-item{
75
+ display: flex;
76
+ width: 100%;
77
+ border-radius: 6px;
78
+ color: var(--pr-nav-text);
79
+ line-height: 32px;
80
+ font-size: 14px;
81
+ font-weight: 500;
82
+ white-space: nowrap;
83
+ transition: background-color .25s,color .25s;
84
+
85
+ &:hover{
86
+ background-color: #f1f1f1;
87
+ color: var(--va-c-brand);
88
+
89
+ .dark &{
90
+ background-color: #2f2f2f;
91
+ }
92
+ }
93
+
94
+ }
95
+ }
96
+
97
+ .group[aria-expanded="true"] > .menu {
98
+ opacity: 1;
99
+ visibility: visible;
100
+ }
101
+ </style>
@@ -0,0 +1,39 @@
1
+ <script lang="ts" setup>
2
+ import { useRoute } from 'vue-router'
3
+ import type { NavItemLink } from '../../types'
4
+ defineProps<{
5
+ item: NavItemLink
6
+ }>()
7
+
8
+ const route = useRoute()
9
+ </script>
10
+
11
+ <template>
12
+ <AppLink
13
+ class="press-nav-item-link"
14
+ :class="{
15
+ active: route.path === item.link,
16
+ }"
17
+ :to="item.link"
18
+ rel="noopener"
19
+ show-external-icon
20
+ >
21
+ {{ item.text }}
22
+ </AppLink>
23
+ </template>
24
+
25
+ <style lang="scss" scoped>
26
+ .press-nav-item-link{
27
+ font-size: 14px;
28
+ font-weight: 500;
29
+ color: var(--pr-nav-text);
30
+ transition: color 0.25s;
31
+
32
+ &:hover{
33
+ color: var(--va-c-brand);
34
+ }
35
+ }
36
+ .active{
37
+ color: var(--va-c-brand);
38
+ }
39
+ </style>
@@ -0,0 +1,14 @@
1
+ import { useData } from 'valaxy'
2
+ import { computed } from 'vue'
3
+ import { useThemeConfig } from '.'
4
+
5
+ export function useEditLink() {
6
+ const themeConfig = useThemeConfig()
7
+ return computed(() => {
8
+ const { text = 'Edit this page', pattern } = themeConfig.value.editLink || {}
9
+ const { relativePath } = useData()
10
+ const url = pattern.replace(/:path/g, relativePath)
11
+
12
+ return { url, text }
13
+ })
14
+ }
@@ -1 +1,2 @@
1
1
  export * from './config'
2
+ export * from './edit-link'
package/config/index.ts CHANGED
@@ -12,7 +12,13 @@ export const defaultThemeConfig: ThemeConfig = {
12
12
  primary: '#0078E7',
13
13
  },
14
14
 
15
+ sidebar: [],
15
16
  nav: [],
16
- }
17
17
 
18
- export default defaultThemeConfig
18
+ editLink: {
19
+ pattern: 'https://github.com/YunYouJun/valaxy/edit/main/docs/:path',
20
+ text: 'Edit this page on GitHub',
21
+ },
22
+
23
+ footer: {},
24
+ }
@@ -1,7 +1,8 @@
1
1
  <script lang="ts" setup>
2
- import { useSidebar } from 'valaxy/client'
2
+ import { useLayout, useSidebar } from 'valaxy'
3
3
 
4
4
  const { isOpen: isSidebarOpen, open: openSidebar, close: closeSidebar } = useSidebar()
5
+ const layout = useLayout()
5
6
  </script>
6
7
 
7
8
  <template>
@@ -9,7 +10,7 @@ const { isOpen: isSidebarOpen, open: openSidebar, close: closeSidebar } = useSid
9
10
  <PressNav />
10
11
  <PressLocalNav :open="isSidebarOpen" @open-menu="openSidebar()" />
11
12
  <slot name="sidebar">
12
- <PressSidebar :open="isSidebarOpen" />
13
+ <PressSidebar v-if="layout !== 'post'" :open="isSidebarOpen" />
13
14
  </slot>
14
15
  <PressBackdrop :show="isSidebarOpen" @click="closeSidebar" />
15
16
 
@@ -50,6 +51,8 @@ const { isOpen: isSidebarOpen, open: openSidebar, close: closeSidebar } = useSid
50
51
  </component>
51
52
  </router-view>
52
53
  </slot>
54
+
55
+ <PressFooter />
53
56
  </div>
54
57
  </template>
55
58
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valaxy-theme-press",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Docs Theme for Valaxy",
5
5
  "author": {
6
6
  "email": "me@yunyoujun.cn",
@@ -19,10 +19,20 @@
19
19
  "main": "node/index.ts",
20
20
  "types": "types/index.d.ts",
21
21
  "dependencies": {
22
- "@docsearch/css": "^3.1.1",
23
- "@docsearch/js": "^3.1.1"
22
+ "@docsearch/css": "^3.3.0",
23
+ "@docsearch/js": "^3.3.0"
24
24
  },
25
25
  "devDependencies": {
26
- "valaxy": "0.10.0"
26
+ "valaxy": "workspace:*"
27
+ },
28
+ "pnpm": {
29
+ "peerDependencyRules": {
30
+ "ignoreMissing": [
31
+ "@algolia/client-search",
32
+ "@types/react",
33
+ "react",
34
+ "react-dom"
35
+ ]
36
+ }
27
37
  }
28
- }
38
+ }
package/setup/main.ts CHANGED
@@ -58,11 +58,10 @@ export default defineAppSetup((ctx) => {
58
58
 
59
59
  function scrollTo(el: HTMLElement, hash: string, smooth = false) {
60
60
  let target: Element | null = null
61
-
62
61
  try {
63
62
  target = el.classList.contains('header-anchor')
64
63
  ? el
65
- : document.querySelector(decodeURIComponent(hash))
64
+ : (decodeURIComponent(hash) && document.querySelector(decodeURIComponent(hash))) || null
66
65
  }
67
66
  catch (e) {
68
67
  console.warn(e)
@@ -4,10 +4,11 @@
4
4
 
5
5
  :root {
6
6
  --pr-c-text-code: #374562;
7
+ --pr-c-text-1: #213547;
7
8
 
8
9
  // aside
9
- --pr-aside-text-1: #213547;
10
- --pr-aside-text-2: #3c3c3cb3;
10
+ --pr-aside-text-1: var(--pr-c-text-1);
11
+ --pr-aside-text-2: rgba(60, 60, 60, 0.702);
11
12
 
12
13
  --pr-aside-divider: rgba(60, 60, 60, 0.122);
13
14
 
@@ -16,13 +17,15 @@
16
17
  // nav
17
18
  --pr-nav-height: var(--pr-nav-height-mobile);
18
19
  --pr-nav-height-mobile: 60px;
20
+ --pr-nav-text: var(--pr-c-text-1);
19
21
 
20
22
  // z-index
21
23
  --va-z-overlay: var(--pr-z-index-backdrop);
22
- --pr-z-index-nav: 8;
23
- --pr-z-index-local-nav: 9;
24
+ --pr-z-index-local-nav: 8;
25
+ --pr-z-index-nav: 9;
24
26
  --pr-z-index-backdrop: 10;
25
- --pr-z-index-sidebar: 11;
27
+ --pr-z-index-aside: 11;
28
+ --pr-z-index-sidebar: 12;
26
29
 
27
30
  // switch
28
31
  --pr-switch-divider: rgba(60, 60, 60, 0.29);
@@ -31,9 +34,9 @@
31
34
 
32
35
  .dark {
33
36
  --pr-c-text-code: var(--pr-c-indigo-lighter);
37
+ --pr-c-text-1: #ffffffde;
34
38
 
35
39
  // aside
36
- --pr-aside-text-1: #ffffffde;
37
40
  --pr-aside-text-2: #ebebeb99;
38
41
  --pr-aside-divider: rgba(84, 84, 84, 0.48);
39
42
 
@@ -28,17 +28,7 @@
28
28
 
29
29
  border-radius: 50%;
30
30
 
31
- transition: background-color var(--va-transition-duration);
32
-
33
31
  div {
34
32
  font-size: 1.2rem;
35
33
  }
36
-
37
- &:hover {
38
- background-color: rgba(var(--va-c-primary-rgb), 0.08);
39
- }
40
-
41
- &:active {
42
- background-color: rgba(var(--va-c-primary-rgb), 0.16);
43
- }
44
34
  }
@@ -13,6 +13,7 @@
13
13
  }
14
14
 
15
15
  a {
16
+ display: inline-block;
16
17
  text-decoration: none;
17
18
  border-bottom: 1px solid transparent;
18
19
  transition: all 0.4s;
@@ -28,6 +29,14 @@
28
29
  padding: 3px 6px;
29
30
  border-radius: 4px;
30
31
  font-weight: 500;
32
+
33
+ &::before {
34
+ content: none;
35
+ }
36
+
37
+ &::after {
38
+ content: none;
39
+ }
31
40
  }
32
41
  }
33
42
 
@@ -37,4 +46,4 @@
37
46
  margin: 0 -1.5rem;
38
47
  }
39
48
  }
40
- }
49
+ }
package/types/index.d.ts CHANGED
@@ -1,33 +1,71 @@
1
1
  export * from '../composables'
2
2
  export * from './home.d'
3
3
 
4
- export namespace DocsTheme {
5
- export type Config = ThemeConfig
4
+ export namespace PressTheme {
6
5
  export type Sidebar = any
7
- }
8
6
 
9
- /**
10
- * Theme Config
11
- */
12
- export interface ThemeConfig {
13
- /**
14
- * toc title
15
- * @default 'On this page'
16
- */
17
- outlineTitle: string
18
-
19
- colors: {
7
+ export interface Footer {
8
+ message?: string;
9
+ copyright?: string;
10
+ }
11
+
12
+ export interface EditLink {
13
+ /**
14
+ * Pattern for edit link.
15
+ *
16
+ * @example 'https://github.com/YunYouJun/valaxy/edit/main/docs/:path'
17
+ */
18
+ pattern: string
19
+
20
+ /**
21
+ * Custom text for edit link.
22
+ *
23
+ * @default 'Edit this page'
24
+ */
25
+ text?: string
26
+ }
27
+
28
+ export interface Config {
20
29
  /**
21
- * primary color
22
- * @default '#0078E7'
30
+ * toc title
31
+ * @default 'On this page'
23
32
  */
24
- primary: string
33
+ outlineTitle: string
34
+
35
+ colors: {
36
+ /**
37
+ * primary color
38
+ * @default '#0078E7'
39
+ */
40
+ primary: string
41
+ }
42
+
43
+ nav: Array<NavItem>
44
+ sidebar: string[]
45
+
46
+ editLink: EditLink
47
+
48
+ footer: Footer
25
49
  }
50
+ }
51
+
52
+ export interface NavItemLink {
53
+ link: string
54
+ text: string
55
+ active?: string
56
+ }
26
57
 
27
- nav: {
28
- link: string
29
- text: string
30
- }[]
58
+ export interface NavItemGroup {
59
+ text: string
60
+ items: NavItemLink[]
31
61
  }
32
62
 
63
+ export type NavItem = NavItemLink | NavItemGroup
64
+
65
+
66
+
67
+ /**
68
+ * Theme Config
69
+ */
70
+ export type ThemeConfig = PressTheme.Config
33
71
  export type ThemeUserConfig = Partial<ThemeConfig>
package/valaxy.config.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { ResolvedValaxyOptions } from 'valaxy'
2
2
  import { defineTheme } from 'valaxy'
3
3
  import type { Plugin } from 'vite'
4
+ import { defaultThemeConfig } from './config'
4
5
  import type { ThemeConfig } from './types'
5
6
 
6
7
  function ThemeVitePlugin(options: ResolvedValaxyOptions<ThemeConfig>): Plugin {
@@ -29,6 +30,7 @@ function ThemeVitePlugin(options: ResolvedValaxyOptions<ThemeConfig>): Plugin {
29
30
 
30
31
  export default defineTheme<ThemeConfig>((options) => {
31
32
  return {
33
+ themeConfig: defaultThemeConfig,
32
34
  vite: {
33
35
  plugins: [ThemeVitePlugin(options)],
34
36
  },
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2022 云游君 YunYouJun <me@yunyoujun.cn>
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.