valaxy-theme-yun 0.19.13 → 0.20.0-beta.1

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.
Files changed (106) hide show
  1. package/App.vue +30 -4
  2. package/client/constants/index.ts +13 -0
  3. package/components/ValaxyMain.vue +48 -52
  4. package/components/YunAside.vue +66 -43
  5. package/components/YunBackToTop.vue +11 -4
  6. package/components/YunBanner.vue +31 -15
  7. package/components/YunBg.vue +2 -0
  8. package/components/YunCard.vue +5 -1
  9. package/components/YunCategories.vue +14 -34
  10. package/components/YunCategory.vue +42 -11
  11. package/components/YunClassifyPopover.vue +59 -0
  12. package/components/YunCloud.vue +5 -10
  13. package/components/YunConfig.vue +2 -19
  14. package/components/YunDebug.vue +47 -0
  15. package/components/YunDock.vue +223 -0
  16. package/components/YunFooter.vue +56 -4
  17. package/components/YunFullscreenMenu.vue +57 -0
  18. package/components/YunGirlItem.vue +98 -0
  19. package/components/YunGirls.vue +2 -73
  20. package/components/YunGoDown.vue +8 -15
  21. package/components/YunLinkItem.vue +62 -0
  22. package/components/YunLinks.vue +15 -47
  23. package/components/YunNavMenu.vue +125 -0
  24. package/components/YunOutline.vue +1 -2
  25. package/components/YunOverlay.vue +31 -0
  26. package/components/YunOverview.vue +2 -74
  27. package/components/YunPageHeader.vue +1 -1
  28. package/components/YunPagination.vue +105 -0
  29. package/components/YunPostCard.vue +32 -3
  30. package/components/YunPostCategories.vue +3 -3
  31. package/components/YunPostCollapse.vue +7 -72
  32. package/components/YunPostCollapseItem.vue +137 -0
  33. package/components/YunPostDateMeta.vue +30 -0
  34. package/components/YunPostList.vue +6 -11
  35. package/components/YunPostMeta.vue +31 -39
  36. package/components/YunPostTags.vue +2 -2
  37. package/components/YunPostsInfo.vue +41 -0
  38. package/components/YunPrologue.vue +24 -0
  39. package/components/YunSearchBtn.vue +8 -6
  40. package/components/YunSelect.vue +1 -11
  41. package/components/YunSidebar.vue +7 -4
  42. package/components/YunSidebarCard.vue +10 -0
  43. package/components/YunSidebarNav.vue +0 -1
  44. package/components/YunSiteInfo.vue +72 -0
  45. package/components/YunSponsor.vue +21 -6
  46. package/components/animation/LineBurstEffects.vue +75 -0
  47. package/components/author/YunAuthorAvatar.vue +12 -0
  48. package/components/author/YunAuthorIntro.vue +11 -0
  49. package/components/author/YunAuthorName.vue +14 -0
  50. package/components/config/YunToggleDark.vue +37 -0
  51. package/components/layout/YunLayoutLeft.vue +12 -0
  52. package/components/layout/YunLayoutRight.vue +21 -0
  53. package/components/layout/YunLayoutWrapper.vue +17 -0
  54. package/components/menu/YunNavMenuItem.vue +22 -0
  55. package/components/menu/YunNavMenuTitle.vue +86 -0
  56. package/components/menu/YunPostClassifyNavItem.vue +30 -0
  57. package/components/page/YunPageHeaderGradient.vue +38 -0
  58. package/components/project/YunProjectCard.vue +94 -0
  59. package/components/project/YunProjectCollection.vue +15 -0
  60. package/components/project/YunProjectLinkItem.vue +60 -0
  61. package/components/project/YunProjectToggleButton.vue +16 -0
  62. package/components/project/YunProjects.vue +48 -0
  63. package/components/prologue/PrologueSocialIcon.vue +58 -0
  64. package/components/prologue/PrologueSocialLinks.vue +16 -0
  65. package/components/prologue/PrologueSquare.vue +144 -0
  66. package/components/prologue/YunAEFrame.vue +155 -0
  67. package/components/prologue/YunAERect.vue +127 -0
  68. package/components/site/YunFullscreenMenuItem.vue +26 -0
  69. package/components/site/YunFullscreenMenuList.vue +19 -0
  70. package/components/site/YunSiteLinkItem.vue +26 -0
  71. package/components/site/YunSiteLinks.vue +19 -0
  72. package/components/site/YunSiteTitle.vue +59 -0
  73. package/components/third/YunWalineMeta.vue +17 -5
  74. package/docs/zh-CN/config.md +0 -3
  75. package/layouts/archives.vue +33 -21
  76. package/layouts/categories.vue +43 -31
  77. package/layouts/default.vue +10 -5
  78. package/layouts/empty.vue +3 -0
  79. package/layouts/home.vue +12 -5
  80. package/layouts/post.vue +23 -20
  81. package/layouts/posts.vue +10 -0
  82. package/layouts/projects.vue +25 -0
  83. package/layouts/tags.vue +45 -41
  84. package/node/config.ts +2 -5
  85. package/package.json +10 -5
  86. package/pages/page/[page].vue +3 -6
  87. package/pages/posts/index.vue +11 -0
  88. package/setup/main.ts +51 -9
  89. package/stores/app.ts +25 -3
  90. package/styles/animations/index.scss +36 -0
  91. package/styles/common/markdown.scss +4 -0
  92. package/styles/css-vars.scss +19 -1
  93. package/styles/global.scss +8 -0
  94. package/styles/index.scss +1 -0
  95. package/styles/layout/index.scss +3 -0
  96. package/styles/modules/prologue.scss +1 -0
  97. package/styles/primevue/index.ts +7 -0
  98. package/styles/primevue/tooltip.scss +55 -0
  99. package/styles/primevue/tooltip.ts +14 -0
  100. package/styles/vars.scss +23 -2
  101. package/styles/widgets/banner.scss +26 -6
  102. package/types/index.d.ts +53 -5
  103. package/types/projects.ts +48 -0
  104. package/unocss.config.ts +1 -1
  105. package/utils/animation.ts +33 -0
  106. package/utils/index.ts +2 -0
package/App.vue CHANGED
@@ -1,8 +1,10 @@
1
1
  <script lang="ts" setup>
2
2
  import { useHead } from '@unhead/vue'
3
- import { useAppStore, useSiteConfig } from 'valaxy'
4
- import { onMounted } from 'vue'
3
+ import { useAppStore } from 'valaxy'
4
+ import { onMounted, watch } from 'vue'
5
+ import { useRoute } from 'vue-router'
5
6
  import { useThemeConfig } from './composables'
7
+ import { useYunAppStore } from './stores'
6
8
 
7
9
  const appStore = useAppStore()
8
10
 
@@ -26,23 +28,47 @@ useHead({
26
28
  ],
27
29
  })
28
30
 
29
- const siteConfig = useSiteConfig()
30
31
  const themeConfig = useThemeConfig()
31
32
 
32
33
  const app = useAppStore()
34
+ const yunStore = useYunAppStore()
35
+ const route = useRoute()
36
+
37
+ watch(
38
+ () => route.meta.layout,
39
+ () => {
40
+ if (route.meta.layout === 'home' || app.isMobile)
41
+ yunStore.leftSidebar.isOpen = false
42
+ else
43
+ yunStore.leftSidebar.isOpen = true
44
+ },
45
+ { immediate: true },
46
+ )
47
+
33
48
  onMounted(() => {
49
+ // for mobile vh
50
+ document.documentElement.style.setProperty('--vh', `${window.innerHeight * 0.01}px`)
34
51
  app.showLoading = false
35
52
  })
53
+
54
+ const isDev = import.meta.env.DEV
36
55
  </script>
37
56
 
38
57
  <template>
58
+ <YunDebug v-if="isDev" />
59
+
60
+ <YunPageHeaderGradient />
61
+ <YunNavMenu />
62
+ <YunFullscreenMenu />
39
63
  <YunFireworks v-if="themeConfig.fireworks.enable" />
40
64
  <slot name="bg">
41
65
  <YunBg v-if="themeConfig.bg_image.enable" />
42
66
  </slot>
43
- <YunSearchTrigger v-if="siteConfig.search.enable" />
44
67
  <Transition name="fade">
45
68
  <YunLoading v-if="app.showLoading" />
46
69
  </Transition>
47
70
  <YunBackToTop />
71
+
72
+ <!-- TODO -->
73
+ <!-- <YunDock /> -->
48
74
  </template>
@@ -0,0 +1,13 @@
1
+ /**
2
+ * custom easing
3
+ */
4
+ export const cubicBezier: Record<string, [number, number, number, number]> = {
5
+ /**
6
+ * 慢速开始,然后加速
7
+ */
8
+ easeIn: [0.18, 0.66, 0.05, 0.96],
9
+ /**
10
+ * 慢速开始和结束
11
+ */
12
+ easeInOut: [0.77, 0, 0.17, 1.02],
13
+ }
@@ -37,64 +37,60 @@ onContentUpdated(() => {
37
37
  </script>
38
38
 
39
39
  <template>
40
- <main class="yun-main md:pl-$va-sidebar-width lt-md:ml-0" flex="~">
41
- <div w="full" flex="~">
42
- <slot name="main">
43
- <div class="content" :class="!aside && 'no-aside'" flex="~ col grow" w="full" p="l-4 lt-md:0">
44
- <YunCard :cover="frontmatter.cover" m="0" class="relative" :style="styles as StyleValue">
45
- <slot name="main-header">
46
- <YunPageHeader
47
- class="mb-2"
48
- :title="title"
49
- :icon="frontmatter.icon || icon"
50
- :color="frontmatter.color || color"
51
- :cover="frontmatter.cover"
52
- :page-title-class="frontmatter.pageTitleClass"
53
- />
40
+ <main class="yun-main lt-md:w-full" flex="~ center">
41
+ <slot name="main">
42
+ <div
43
+ class="content w-full md:w-3xl lg:w-2xl xl:w-2xl 2xl:w-4xl" :class="{
44
+ 'no-aside': !aside,
45
+ }" flex="~ col grow"
46
+ p="lt-md:0"
47
+ >
48
+ <YunCard :cover="frontmatter.cover" m="0" class="relative" :style="styles as StyleValue">
49
+ <slot name="main-header">
50
+ <YunPageHeader
51
+ class="mb-2 mt-8"
52
+ :title="title"
53
+ :icon="frontmatter.icon || icon"
54
+ :color="frontmatter.color || color"
55
+ :cover="frontmatter.cover"
56
+ :page-title-class="frontmatter.pageTitleClass"
57
+ />
58
+ </slot>
59
+ <slot name="main-header-after" />
60
+
61
+ <div p="x-4 b-8" class="sm:px-6 lg:px-12 xl:px-16" w="full">
62
+ <slot name="main-content">
63
+ <!-- <Transition appear> -->
64
+ <ValaxyMd :frontmatter="frontmatter">
65
+ <YunAiExcerpt v-if="frontmatter.excerpt_type === 'ai' && frontmatter.excerpt" />
66
+ <YunMdTimeWarning />
67
+
68
+ <slot name="main-content-md" />
69
+ <slot />
70
+ </ValaxyMd>
71
+ <!-- </Transition> -->
54
72
  </slot>
55
- <slot name="main-header-after" />
56
-
57
- <div p="x-4 b-8" class="sm:px-6 lg:px-12 xl:px-16" w="full">
58
- <slot name="main-content">
59
- <!-- <Transition appear> -->
60
- <ValaxyMd :frontmatter="frontmatter">
61
- <YunAiExcerpt v-if="frontmatter.excerpt_type === 'ai' && frontmatter.excerpt" />
62
- <YunMdTimeWarning />
63
73
 
64
- <slot name="main-content-md" />
65
- <slot />
66
- </ValaxyMd>
67
- <!-- </Transition> -->
68
- </slot>
74
+ <slot name="main-content-after" />
75
+ </div>
76
+ </YunCard>
69
77
 
70
- <slot name="main-content-after" />
71
- </div>
72
- </YunCard>
78
+ <slot name="main-nav-before" />
73
79
 
74
- <slot name="main-nav-before" />
80
+ <slot name="main-nav">
81
+ <YunPostNav v-if="frontmatter.nav !== false" />
82
+ </slot>
75
83
 
76
- <slot name="main-nav">
77
- <YunPostNav v-if="frontmatter.nav !== false" />
78
- </slot>
84
+ <slot name="main-nav-after" />
79
85
 
80
- <slot name="main-nav-after" />
81
-
82
- <slot v-if="siteConfig.comment.enable && frontmatter.comment !== false" name="comment">
83
- <YunComment :class="frontmatter.nav === false ? 'mt-4' : 0" />
84
- </slot>
86
+ <slot v-if="siteConfig.comment.enable && frontmatter.comment !== false" name="comment">
87
+ <YunComment :class="frontmatter.nav === false ? 'mt-4' : 0" />
88
+ </slot>
85
89
 
86
- <slot name="main-footer-before" />
87
- <YunFooter />
88
- <slot name="main-footer-after" />
89
- </div>
90
- </slot>
91
-
92
- <slot name="aside">
93
- <YunAside v-if="aside">
94
- <slot name="aside-custom" />
95
- </YunAside>
96
- </slot>
97
- </div>
90
+ <slot name="main-footer-before" />
91
+ <slot name="main-footer-after" />
92
+ </div>
93
+ </slot>
98
94
  </main>
99
95
  </template>
100
96
 
@@ -112,7 +108,7 @@ onContentUpdated(() => {
112
108
  @include screen('xl') {
113
109
  .content{
114
110
  // 8px scrollbar width
115
- max-width: calc(100vw - 2 * var(--va-sidebar-width) - 1rem - 8px);
111
+ // max-width: calc(100vw - 2 * var(--va-sidebar-width) - 1rem - 8px);
116
112
 
117
113
  &.no-aside {
118
114
  max-width: calc(100vw - var(--va-sidebar-width));
@@ -1,73 +1,97 @@
1
1
  <script lang="ts" setup>
2
2
  import { useI18n } from 'vue-i18n'
3
- import { useAppStore, useFrontmatter } from 'valaxy'
3
+ import { useFrontmatter } from 'valaxy'
4
+ import { computed, nextTick, ref, watch } from 'vue'
5
+ import { useYunAppStore } from '../stores'
4
6
 
5
- const frontmatter = useFrontmatter()
7
+ const fm = useFrontmatter()
6
8
  const { t } = useI18n()
7
- const app = useAppStore()
8
- </script>
9
+ const yun = useYunAppStore()
9
10
 
10
- <template>
11
- <button
12
- class="xl:hidden toc-btn shadow fixed yun-icon-btn z-350"
13
- opacity="75" right="2" bottom="19"
14
- @click="app.toggleRightSidebar()"
15
- >
16
- <div i-ri-file-list-line />
17
- </button>
11
+ const show = ref(false)
12
+ const showToc = computed(() => {
13
+ return fm.value.toc !== false
14
+ })
18
15
 
19
- <ValaxyOverlay :show="app.isRightSidebarOpen" @click="app.toggleRightSidebar()" />
16
+ // aside float
17
+ const isAsideFloat = ref(false)
18
+
19
+ watch(() => [yun.rightSidebar.isOpen, yun.size.isXl], async () => {
20
+ await nextTick()
21
+ isAsideFloat.value = !yun.size.isXl
22
+ show.value = (yun.rightSidebar.isOpen || !isAsideFloat.value) && fm.value.aside !== false
23
+ }, {
24
+ immediate: true,
25
+ })
26
+ </script>
20
27
 
21
- <!-- -->
28
+ <template>
22
29
  <aside
23
- class="va-card yun-aside"
24
- :class="app.isRightSidebarOpen && 'open'" m="l-4" text="center"
30
+ flex="~ col"
31
+ class="va-card yun-aside sticky top-0 lg:top-68px min-h-sm"
32
+ :class="{
33
+ float: isAsideFloat,
34
+ show,
35
+ open: yun.rightSidebar.isOpen,
36
+ }"
37
+ text="center"
25
38
  overflow="auto"
26
39
  >
27
- <div class="aside-container" flex="~ col">
28
- <h2 v-if="frontmatter.toc !== false" m="t-6 b-2" font="serif black">
29
- {{ t('sidebar.toc') }}
30
- </h2>
31
-
32
- <YunOutline v-if="frontmatter.toc !== false" />
40
+ <Transition name="fade" :delay="100">
41
+ <div v-show="show" class="w-full" flex="~ col">
42
+ <template v-if="showToc">
43
+ <h2
44
+ m="t-6 b-2"
45
+ font="serif black"
46
+ >
47
+ {{ t('sidebar.toc') }}
48
+ </h2>
49
+ <YunOutline />
50
+ </template>
33
51
 
34
- <div class="flex-grow" />
52
+ <div class="flex-grow" />
35
53
 
36
- <div v-if="$slots.default" class="custom-container">
37
- <slot />
54
+ <div v-if="$slots.default" class="custom-container">
55
+ <slot />
56
+ </div>
38
57
  </div>
39
- </div>
58
+ </Transition>
40
59
  </aside>
41
60
  </template>
42
61
 
43
62
  <style lang="scss">
63
+ @use 'sass:map';
44
64
  @use 'valaxy/client/styles/mixins/index.scss' as *;
65
+ @use 'valaxy-theme-yun/styles/vars.scss' as *;
45
66
 
46
67
  .yun-aside {
47
- position: fixed;
48
- right: 0;
49
- top: 0;
50
- bottom: 0;
51
-
52
68
  // need fixed width
53
- width: var(--va-sidebar-width, 300px);
69
+ // width: var(--va-sidebar-width, 300px);
70
+ width: 0;
54
71
  transform: translateX(100%);
55
- transition: box-shadow var(--va-transition-duration),
56
- background-color var(--va-transition-duration), opacity 0.25s,
57
- transform var(--va-transition-duration) cubic-bezier(0.19, 1, 0.22, 1);
72
+ transition: all 0.2s map.get($cubic-bezier, 'ease-in-out');
73
+ max-height: calc(100vh - 68px);
74
+
75
+ // float panel
76
+ &.float {
77
+ position: fixed;
78
+ top: 0;
79
+ right: 0;
80
+ bottom: 0;
81
+ z-index: var(--yun-z-aside);
82
+ max-height: 100vh;
83
+ }
84
+
85
+ &.show {
86
+ width: 320px;
87
+ }
58
88
 
59
89
  &.open {
90
+ width: 320px;
60
91
  right: 0;
61
92
  display: block;
62
- z-index: 10;
63
93
  transform: translateX(0);
64
94
  }
65
-
66
- &-container {
67
- position: sticky;
68
- top: 0;
69
- height: 100vh;
70
- }
71
95
  }
72
96
 
73
97
  @include screen('xl') {
@@ -78,7 +102,6 @@ const app = useAppStore()
78
102
 
79
103
  .toc-btn {
80
104
  color: var(--va-c-primary);
81
- background-color: white;
82
105
  z-index: var(--yun-z-toc-btn);
83
106
  }
84
107
  </style>
@@ -16,12 +16,19 @@ const strokeOffset = computed(() => {
16
16
 
17
17
  <template>
18
18
  <a
19
- href="#" class="back-to-top yun-icon-btn"
20
- :class="show && 'show'" @click="backToTop"
19
+ href="#"
20
+ class="back-to-top yun-icon-btn bg-$va-c-bg-soft shadow-md"
21
+ :class="show && 'show'"
22
+ @click="backToTop"
21
23
  >
22
- <div w="8" h="8" i-ri-arrow-up-s-line />
24
+ <div class="size-8" i-ri-arrow-up-s-line />
23
25
  <svg class="progress-circle-container" viewBox="0 0 100 100">
24
- <circle :stroke-dasharray="`${circumference} ${circumference}`" :stroke-dashoffset="strokeOffset" class="progress-circle" cx="50" cy="50" :r="radius" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
26
+ <circle
27
+ :stroke-dasharray="`${circumference} ${circumference}`"
28
+ :stroke-dashoffset="strokeOffset"
29
+ stroke="currentColor" stroke-width="2" stroke-linecap="round"
30
+ class="progress-circle" cx="50" cy="50" :r="radius" fill="none"
31
+ />
25
32
  </svg>
26
33
  </a>
27
34
  </template>
@@ -6,8 +6,8 @@
6
6
  */
7
7
 
8
8
  import type { CSSProperties } from 'vue'
9
- import { computed, ref } from 'vue'
10
- import { random } from 'valaxy'
9
+ import { computed, onMounted, ref } from 'vue'
10
+ import { random, sleep } from 'valaxy'
11
11
  import { useThemeConfig } from '../composables'
12
12
 
13
13
  const themeConfig = useThemeConfig()
@@ -21,15 +21,33 @@ const chars = computed(() => {
21
21
  return arr
22
22
  })
23
23
  // height of top/bottom line
24
- const lineH = computed(() => chars.value.reduce((a, b) => a + b, 0) / 2)
24
+ const totalCharHeight = computed(() => chars.value.reduce((a, b) => a + b, 0))
25
25
 
26
26
  const bannerStyles = computed<CSSProperties>(() => {
27
27
  return {
28
- '--banner-line-height': `calc(var(--banner-height, 100vh) / 2 - ${lineH.value}rem)`,
28
+ '--total-char-height': `${totalCharHeight.value}rem`,
29
+ '--banner-line-height': `calc(var(--banner-height, 100 * var(--vh)) / 2 - ${totalCharHeight.value / 2}rem)`,
30
+ 'justify-content': 'space-between',
29
31
  }
30
32
  })
31
33
 
32
- const playExtendLine = ref(true)
34
+ const lineStatus = ref<
35
+ 'enter' | 'active' | 'exit' | ''
36
+ >('enter')
37
+ const lineStatusClass = computed(() => {
38
+ return lineStatus.value
39
+ })
40
+
41
+ const animationStatus = ref('banner')
42
+
43
+ onMounted(async () => {
44
+ await sleep(500)
45
+ lineStatus.value = 'active'
46
+ await sleep(500)
47
+ lineStatus.value = 'exit'
48
+
49
+ animationStatus.value = 'prologue'
50
+ })
33
51
  </script>
34
52
 
35
53
  <template>
@@ -37,12 +55,10 @@ const playExtendLine = ref(true)
37
55
  <div class="banner-line-container">
38
56
  <div
39
57
  class="banner-line vertical-line-top"
40
- :class="{
41
- active: playExtendLine,
42
- }"
58
+ :class="lineStatusClass"
43
59
  />
44
60
  </div>
45
- <div class="banner-char-container">
61
+ <div v-if="animationStatus === 'banner'" class="banner-char-container">
46
62
  <div v-for="c, i in themeConfig.banner.title" :key="i" class="char-box">
47
63
  <span
48
64
  :class="[i % 2 !== 0 ? 'char-right' : 'char-left']" :style="{
@@ -55,27 +71,27 @@ const playExtendLine = ref(true)
55
71
  </span>
56
72
  </div>
57
73
  </div>
74
+ <PrologueSquare v-else class="z-1" />
75
+
58
76
  <div class="banner-line-container bottom">
59
77
  <div
60
78
  class="banner-line vertical-line-bottom"
61
- :class="{
62
- active: playExtendLine,
63
- }"
79
+ :class="lineStatusClass"
64
80
  />
65
81
  </div>
66
- <YunCloud v-if="themeConfig.banner.cloud?.enable" />
67
82
  <YunGoDown />
68
83
  </div>
69
84
  </template>
70
85
 
71
86
  <style lang="scss">
72
87
  @use "../styles/widgets/banner.scss" as *;
88
+ @use "../styles/modules/prologue.scss" as *;
73
89
 
74
90
  :root {
75
91
  // banner
76
92
  --banner-line-color: black;
77
93
  --banner-char-color: black;
78
- --banner-char-bg-color: rgba(255, 255, 255, 0.5);
94
+ --banner-char-bg-color: rgb(255 255 255 / 0.5);
79
95
  --banner-char-hover-color: white;
80
96
  }
81
97
 
@@ -83,7 +99,7 @@ const playExtendLine = ref(true)
83
99
  // banner
84
100
  --banner-line-color: white;
85
101
  --banner-char-color: white;
86
- --banner-char-bg-color: rgba(0, 0, 0, 0.5);
102
+ --banner-char-bg-color: rgb(0 0 0 / 0.5);
87
103
  --banner-char-hover-color: black;
88
104
  }
89
105
  </style>
@@ -38,6 +38,8 @@ if (typeof themeConfig.value.bg_image.url !== 'undefined') {
38
38
  width: 100%;
39
39
  height: 100%;
40
40
  z-index: -1;
41
+ top: 0;
42
+ left: 0;
41
43
  background-image: var(--yun-bg-img);
42
44
  background-size: cover;
43
45
  background-position: center;
@@ -3,7 +3,11 @@ defineProps<{ cover?: string }>()
3
3
  </script>
4
4
 
5
5
  <template>
6
- <div class="yun-card flex-center rounded" flex="col" min-h="100px" bg="$va-c-bg-light">
6
+ <div
7
+ class="yun-card flex-center rounded" flex="col"
8
+ min-h="100px"
9
+ bg="$va-c-bg-light"
10
+ >
7
11
  <img
8
12
  v-if="cover"
9
13
  width="640" height="360"
@@ -24,38 +24,18 @@ const categoryList = computed(() => {
24
24
  </script>
25
25
 
26
26
  <template>
27
- <ul v-for="category in categories.values()" :key="category.name" class="category-list" m="l-4">
28
- <YunCategory
29
- :parent-key="category.name"
30
- :category="category"
31
- :level="level + 1"
32
- :collapsable="!categoryList.includes(category.name)"
33
- />
34
- </ul>
27
+ <div flex="~ col">
28
+ <ul
29
+ v-for="category in categories.values()"
30
+ :key="category.name"
31
+ class="category-list"
32
+ >
33
+ <YunCategory
34
+ :parent-key="category.name"
35
+ :category="category"
36
+ :level="level + 1"
37
+ :collapsable="!categoryList.includes(category.name)"
38
+ />
39
+ </ul>
40
+ </div>
35
41
  </template>
36
-
37
- <style lang="scss">
38
- .post-list-item {
39
- a {
40
- color: var(--va-c-text-lighter);
41
-
42
- &:hover {
43
- color: var(--va-c-primary);
44
- }
45
- }
46
- }
47
-
48
- .category-list-item {
49
- .folder-action {
50
- &:hover {
51
- color: var(--va-c-primary);
52
- }
53
- }
54
-
55
- .category-name {
56
- &:hover {
57
- color: var(--va-c-primary);
58
- }
59
- }
60
- }
61
- </style>
@@ -45,6 +45,8 @@ const { show } = useInvisibleElement(postCollapseElRef)
45
45
  * @param category
46
46
  */
47
47
  function jumpToDisplayCategory(category: string) {
48
+ collapse.value = false
49
+
48
50
  router.push({
49
51
  query: {
50
52
  category,
@@ -62,19 +64,44 @@ onMounted(() => {
62
64
  </script>
63
65
 
64
66
  <template>
65
- <li class="category-list-item inline-flex items-center cursor-pointer">
66
- <span class="folder-action inline-flex" @click="collapse = !collapse">
67
+ <li
68
+ class="category-list-item inline-flex items-center cursor-pointer w-full gap-2 transition px-3 py-2 rounded"
69
+ hover="bg-black/5"
70
+ >
71
+ <span
72
+ class="folder-action inline-flex"
73
+ hover="text-$va-c-primary-lighter"
74
+ @click="collapse = !collapse"
75
+ >
67
76
  <div v-if="collapse" i-ri-folder-add-line />
68
- <div v-else style="color:var(--va-c-primary)" i-ri-folder-reduce-line />
77
+ <div v-else class="text-$va-c-primary dark:text-$va-c-primary-lighter" i-ri-folder-reduce-line />
69
78
  </span>
70
- <span class="category-name" m="l-1" @click="jumpToDisplayCategory(parentKey)">
71
- {{ category.name === 'Uncategorized' ? t('category.uncategorized') : category.name }} [{{ category.total }}]
79
+ <span
80
+ class="category-name inline-flex items-center gap-2 w-full"
81
+ @click="jumpToDisplayCategory(parentKey)"
82
+ >
83
+ <span>
84
+ {{ category.name === 'Uncategorized' ? t('category.uncategorized') : category.name }}
85
+ </span>
86
+ <span class="rounded-full px-1.5 bg-black/5 shadow-sm" text="xs black/55">
87
+ {{ category.total }}
88
+ </span>
72
89
  </span>
73
90
  </li>
74
91
 
75
- <template v-if="!collapse">
76
- <ul>
77
- <li v-for="categoryItem, i in category.children.values()" :key="i" class="post-list-item" m="l-4">
92
+ <Transition
93
+ enter-active-class="v-enter-active"
94
+ enter-from-class="v-enter-from"
95
+ leave-active-class="v-leave-active"
96
+ leave-to-class="v-leave-to"
97
+ :duration="{ enter: 200, leave: 0 }"
98
+ >
99
+ <ul v-if="!collapse">
100
+ <li
101
+ v-for="categoryItem, i in category.children.values()" :key="i"
102
+ class="post-list-item text-$va-c-text" m="l-4"
103
+ hover="text-$va-c-primary-lighter"
104
+ >
78
105
  <template v-if="isCategoryList(categoryItem)">
79
106
  <YunCategory
80
107
  :parent-key="parentKey ? `${parentKey}/${categoryItem.name}` : categoryItem.name"
@@ -84,12 +111,16 @@ onMounted(() => {
84
111
  </template>
85
112
 
86
113
  <template v-else>
87
- <RouterLink v-if="categoryItem.title" :to="categoryItem.path || ''" class="inline-flex items-center">
114
+ <RouterLink
115
+ v-if="categoryItem.title" :to="categoryItem.path || ''"
116
+ class="inline-flex items-center gap-2 px-3 py-2 w-full rounded transition"
117
+ hover="bg-black/5"
118
+ >
88
119
  <div i-ri-file-text-line />
89
- <span m="l-1" font="serif black">{{ getTitle(categoryItem) }}</span>
120
+ <span font="serif black">{{ getTitle(categoryItem) }}</span>
90
121
  </RouterLink>
91
122
  </template>
92
123
  </li>
93
124
  </ul>
94
- </template>
125
+ </Transition>
95
126
  </template>