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
@@ -0,0 +1,72 @@
1
+ <script lang="ts" setup>
2
+ import { useSiteConfig } from 'valaxy'
3
+
4
+ const siteConfig = useSiteConfig()
5
+ </script>
6
+
7
+ <template>
8
+ <div class="site-info gap-1 items-center" flex="~ col" m="t-4">
9
+ <RouterLink
10
+ class="site-author-avatar inline-flex-center" to="/about"
11
+ >
12
+ <img class="rounded-full" :src="siteConfig.author.avatar" alt="avatar">
13
+ <!-- <span
14
+ v-if="siteConfig.author.status.emoji"
15
+ class="site-author-status absolute"
16
+ :title="siteConfig.author.status.message || undefined"
17
+ >{{ siteConfig.author.status.emoji }}</span> -->
18
+ </RouterLink>
19
+ <YunAuthorName />
20
+ <YunSiteTitle />
21
+ <h4
22
+ v-if="siteConfig.subtitle"
23
+ class="site-subtitle block"
24
+ text="xs"
25
+ >
26
+ {{ siteConfig.subtitle }}
27
+ </h4>
28
+ <div v-if="siteConfig.description" class="site-description">
29
+ {{ siteConfig.description }}
30
+ </div>
31
+ </div>
32
+ </template>
33
+
34
+ <style lang="scss">
35
+ .site-author-avatar {
36
+ position: relative;
37
+ line-height: 0;
38
+
39
+ img {
40
+ height: 96px;
41
+ width: 96px;
42
+ max-width: 100%;
43
+ margin: 0;
44
+ padding: 4px;
45
+ background-color: white;
46
+ box-shadow: 0 0 10px rgba(black, 0.2);
47
+ transition: 0.4s;
48
+
49
+ &:hover {
50
+ box-shadow: 0 0 30px rgba(var(--va-c-primary-rgb), 0.2);
51
+ }
52
+ }
53
+ }
54
+
55
+ .site-author-status {
56
+ height: 1.8rem;
57
+ width: 1.8rem;
58
+ bottom: 0;
59
+ right: 0;
60
+ line-height: 1.8rem;
61
+ border-radius: 50%;
62
+ box-shadow: 0 1px 2px rgb(0 0 0 / 0.2);
63
+ background-color: var(--va-c-bg-light);
64
+ border: 1px solid rgb(255 255 255 / 0.1);
65
+ }
66
+
67
+ .site-info {
68
+ &.fix-top {
69
+ margin-top: -1.5rem;
70
+ }
71
+ }
72
+ </style>
@@ -18,10 +18,13 @@ const sponsorBtnTitle = computed(() => {
18
18
  class="sponsor-button yun-icon-btn shadow hover:shadow-md"
19
19
  :title="sponsorBtnTitle" text="red-400" @click="showQr = !showQr"
20
20
  >
21
- <div i-ri-heart-line />
21
+ <div class="mt-2px" i-ri-heart-fill />
22
22
  </button>
23
23
 
24
- <div class="qrcode-container qrcode flex-center flex-col" m="y-4" :class="showQr && 'show'">
24
+ <div
25
+ class="qrcode-container qrcode flex-center flex-col" m="y-4"
26
+ :class="showQr && 'show'"
27
+ >
25
28
  <div v-if="siteConfig.sponsor.description" class="sponsor-description" mb="4" text="sm">
26
29
  {{ siteConfig.sponsor.description }}
27
30
  </div>
@@ -41,10 +44,12 @@ const sponsorBtnTitle = computed(() => {
41
44
  </template>
42
45
 
43
46
  <style lang="scss">
47
+ @use 'sass:map';
48
+ @use 'valaxy-theme-yun/styles/vars.scss' as *;
44
49
  @use "valaxy/client/styles/mixins/index.scss" as *;
45
50
 
46
51
  .sponsor-button {
47
- background-color: rgba(255, 255, 255, 0.1);
52
+ background-color: rgb(255 255 255 / 0.1);
48
53
 
49
54
  div {
50
55
  transform: scale(1.1);
@@ -52,7 +57,7 @@ const sponsorBtnTitle = computed(() => {
52
57
  }
53
58
 
54
59
  &:hover {
55
- background-color: rgba(255, 255, 255, 0.9);
60
+ background-color: rgb(255 255 255 / 0.9);
56
61
 
57
62
  div {
58
63
  transform: scale(1.2);
@@ -67,12 +72,22 @@ const sponsorBtnTitle = computed(() => {
67
72
  }
68
73
 
69
74
  .qrcode-container {
75
+ --height: 200px;
76
+
70
77
  overflow: hidden;
71
78
  height: 0;
72
- transition: height var(--va-transition-duration) ease-in-out;
79
+ opacity: 0;
80
+ transition: all var(--va-transition-duration) map.get($cubic-bezier, 'ease-in-out');
73
81
 
74
82
  &.show {
75
- height: 260px;
83
+ height: var(--height);
84
+ opacity: 1;
85
+ }
86
+ }
87
+
88
+ @include screen('md') {
89
+ .qrcode-container {
90
+ --height: 260px;
76
91
  }
77
92
  }
78
93
 
@@ -0,0 +1,75 @@
1
+ <script setup lang="ts">
2
+ // 播放完销毁 css 动画
3
+ import { onMounted, ref } from 'vue'
4
+
5
+ const props = defineProps<{
6
+ duration: number
7
+ }>()
8
+
9
+ const destroy = ref(false)
10
+ onMounted(() => {
11
+ setTimeout(() => {
12
+ destroy.value = true
13
+ }, props.duration)
14
+ })
15
+ </script>
16
+
17
+ <template>
18
+ <div
19
+ v-if="!destroy"
20
+ class="line-burst-effects absolute"
21
+ >
22
+ <div v-for="i in 8" :key="i" class="line">
23
+ <div>
24
+ <span />
25
+ </div>
26
+ </div>
27
+ </div>
28
+ </template>
29
+
30
+ <style lang="scss">
31
+ .line-burst-effects {
32
+ .line {
33
+ position: absolute;
34
+ top: 0;
35
+ left: calc(50% - 2px);
36
+ width: 4px;
37
+ height: 100%;
38
+ }
39
+
40
+ @for $i from 1 through 8 {
41
+ .line:nth-child(#{$i}) {
42
+ transform: rotate($i * 45deg);
43
+
44
+ div {
45
+ position: absolute;
46
+ top: 0;
47
+ left: 0;
48
+ width: 100%;
49
+ height: 20%;
50
+ overflow: hidden;
51
+ }
52
+
53
+ span {
54
+ display: block;
55
+ background-color: white;
56
+ content: '';
57
+ width: 100%;
58
+ height: 100%;
59
+ transform: translateY(100%);
60
+ animation: line-burst 0.8s forwards;
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ @keyframes line-burst {
67
+ 0% {
68
+ transform: translateY(100%);
69
+ }
70
+
71
+ 100% {
72
+ transform: translateY(-100%);
73
+ }
74
+ }
75
+ </style>
@@ -0,0 +1,12 @@
1
+ <script setup lang="ts">
2
+ import { useSiteConfig } from 'valaxy'
3
+
4
+ const siteConfig = useSiteConfig()
5
+ </script>
6
+
7
+ <template>
8
+ <img
9
+ class="rounded-full size-full"
10
+ :src="siteConfig.author.avatar" alt="avatar"
11
+ >
12
+ </template>
@@ -0,0 +1,11 @@
1
+ <script setup lang="ts">
2
+ import { useSiteConfig } from 'valaxy'
3
+
4
+ const siteConfig = useSiteConfig()
5
+ </script>
6
+
7
+ <template>
8
+ <div v-if="siteConfig.author.intro" class="site-author-intro" m="t-0 b-4">
9
+ {{ siteConfig.author.intro }}
10
+ </div>
11
+ </template>
@@ -0,0 +1,14 @@
1
+ <script setup lang="ts">
2
+ import { useSiteConfig } from 'valaxy'
3
+
4
+ const siteConfig = useSiteConfig()
5
+ </script>
6
+
7
+ <template>
8
+ <RouterLink
9
+ class="site-author-name font-black font-serif text-$va-c-text op-80 hover:op-100 flex-center"
10
+ to="/about"
11
+ >
12
+ {{ siteConfig.author.name }}
13
+ </RouterLink>
14
+ </template>
@@ -0,0 +1,37 @@
1
+ <script lang="ts" setup>
2
+ import { useI18n } from 'vue-i18n'
3
+ import { computed } from 'vue'
4
+ import { useAppStore } from 'valaxy'
5
+
6
+ const props = defineProps<{
7
+ transition?: boolean
8
+ }>()
9
+
10
+ const appStore = useAppStore()
11
+ const { t } = useI18n()
12
+
13
+ const themeTitle = computed(() => {
14
+ return appStore.isDark ? t('button.toggle_light') : t('button.toggle_dark')
15
+ })
16
+
17
+ const styles = computed(() => {
18
+ return {
19
+ color: appStore.isDark ? '' : '#f1cb64',
20
+ }
21
+ })
22
+
23
+ function toggle(e: MouseEvent) {
24
+ props.transition ? appStore.toggleDarkWithTransition(e) : appStore.toggleDark()
25
+ }
26
+ </script>
27
+
28
+ <template>
29
+ <button
30
+ class="yun-icon-btn"
31
+ :title="themeTitle"
32
+ :style="styles" @mousedown.prevent="() => { console.log('yes') }"
33
+ @click="toggle"
34
+ >
35
+ <div i="ri-sun-line dark:ri-moon-line" />
36
+ </button>
37
+ </template>
@@ -0,0 +1,12 @@
1
+ <script setup lang="ts">
2
+ import { useYunAppStore } from '../../stores'
3
+
4
+ const yun = useYunAppStore()
5
+ </script>
6
+
7
+ <template>
8
+ <div v-if="yun.size.isLg" flex="~ col" class="gap-4 sticky top-68px w-80">
9
+ <YunSidebarCard />
10
+ <YunAdBoard />
11
+ </div>
12
+ </template>
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ import { useFrontmatter } from 'valaxy'
3
+ import { useYunAppStore } from '../../stores'
4
+
5
+ const fm = useFrontmatter()
6
+ const yun = useYunAppStore()
7
+ </script>
8
+
9
+ <template>
10
+ <button
11
+ v-if="fm.aside !== false"
12
+ class="xl:hidden toc-btn shadow-md fixed yun-icon-btn z-350 bg-$va-c-bg-soft"
13
+ opacity="75" right="2" bottom="19"
14
+ @click="yun.rightSidebar.toggle()"
15
+ >
16
+ <div i-ri-file-list-line />
17
+ </button>
18
+
19
+ <YunOverlay :show="yun.rightSidebar.isOpen" @click="yun.rightSidebar.toggle()" />
20
+ <YunAside />
21
+ </template>
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ import { useYunAppStore } from '../../stores'
3
+
4
+ const yun = useYunAppStore()
5
+ </script>
6
+
7
+ <template>
8
+ <div
9
+ flex="~"
10
+ class="mt-24 md:mt-36 w-full max-w-screen-2xl m-auto justify-center items-start gap-4"
11
+ :class="{
12
+ 'flex-col': yun.size.isXs,
13
+ }"
14
+ >
15
+ <slot />
16
+ </div>
17
+ </template>
@@ -0,0 +1,22 @@
1
+ <script setup lang="ts">
2
+ import { useRouter } from 'vue-router'
3
+
4
+ defineProps<{
5
+ icon: string
6
+ to?: string
7
+ }>()
8
+
9
+ const router = useRouter()
10
+ </script>
11
+
12
+ <template>
13
+ <div
14
+ class="size-12 inline-flex-center cursor-pointer z-$yun-z-nav-menu"
15
+ text="$va-c-text"
16
+ hover="bg-white/80 dark:bg-black/80 op-100"
17
+ op-80
18
+ @click="to && router.push(to)"
19
+ >
20
+ <div class="size-6" :class="icon" />
21
+ </div>
22
+ </template>
@@ -0,0 +1,86 @@
1
+ <script setup lang="ts">
2
+ import { useFrontmatter, useSiteConfig } from 'valaxy'
3
+ import { ref, watch } from 'vue'
4
+ import { useRouter } from 'vue-router'
5
+ import { useYunAppStore } from '../../stores'
6
+
7
+ const yunApp = useYunAppStore()
8
+ const fm = useFrontmatter()
9
+ const siteConfig = useSiteConfig()
10
+
11
+ const showPostTitle = ref(false)
12
+ watch(() => yunApp.scrollY, () => {
13
+ showPostTitle.value = yunApp.scrollY > 200
14
+ })
15
+
16
+ const router = useRouter()
17
+ function goToLink() {
18
+ if (!showPostTitle.value)
19
+ router.push('/')
20
+ }
21
+ </script>
22
+
23
+ <template>
24
+ <div
25
+ v-motion
26
+ flex="~ center col h-full"
27
+ :class="{
28
+ 'cursor-pointer': !showPostTitle,
29
+ }"
30
+ :initial="{
31
+ opacity: 0,
32
+ y: 10,
33
+ }"
34
+ :enter="{
35
+ opacity: 1,
36
+ y: 0,
37
+ transition: {
38
+ duration: 400,
39
+ delay: 1200,
40
+ },
41
+ }"
42
+ @click="goToLink"
43
+ >
44
+ <div
45
+ v-if="fm.title && showPostTitle"
46
+ flex="~ col"
47
+ class="nav-menu-post-title text-xs font-bold flex items-center gap-1 lt-sm:max-w-40"
48
+ >
49
+ <div class="gap-1" flex="~">
50
+ <div
51
+ class="size-4"
52
+ :class="fm.icon || 'i-ri-article-line'"
53
+ />
54
+ <span class="truncate"> {{ fm.title }}</span>
55
+ </div>
56
+ <span v-if="fm.subtitle" class="font-light op-80">
57
+ {{ fm.subtitle }}
58
+ </span>
59
+ </div>
60
+ <span v-else class="font-light truncate">
61
+ {{ siteConfig.title }}
62
+ </span>
63
+ </div>
64
+ </template>
65
+
66
+ <style lang="scss">
67
+ .nav-menu-post-title {
68
+ // safari not support
69
+ animation: nav-menu-title-appear 0.3s linear forwards;
70
+
71
+ // animation-timeline: scroll();
72
+ // animation-range: 0 calc(30vh);
73
+ }
74
+
75
+ @keyframes nav-menu-title-appear {
76
+ from {
77
+ opacity: 0;
78
+ transform: translateY(10px);
79
+ }
80
+
81
+ to {
82
+ opacity: 1;
83
+ transform: translateY(0);
84
+ }
85
+ }
86
+ </style>
@@ -0,0 +1,30 @@
1
+ <script lang="ts" setup>
2
+ defineProps<{
3
+ icon: string
4
+ title: string
5
+ /**
6
+ * Total number
7
+ */
8
+ total: number
9
+ to: string
10
+ }>()
11
+ </script>
12
+
13
+ <template>
14
+ <RouterLink
15
+ flex="~ col center"
16
+ class="gap-1 w-20 p-2 rounded transition op-80"
17
+ :to="to" :title="title"
18
+ hover="bg-$va-c-bg-soft op-100"
19
+ >
20
+ <div flex="~ col" class="text-$va-c-text inline-flex-center gap-1">
21
+ <div class="text-2xl" :class="icon" />
22
+ <span class="text-xs">
23
+ {{ title }}
24
+ </span>
25
+ </div>
26
+ <span
27
+ class="count text-base dark:text-white/80"
28
+ >{{ total }}</span>
29
+ </RouterLink>
30
+ </template>
@@ -0,0 +1,38 @@
1
+ <script setup lang="ts">
2
+ import { useValaxyDark } from 'valaxy'
3
+ import { computed } from 'vue'
4
+ // background-image: linear-gradient(120deg, #a1c4fd 0%, #c2e9fb 100%);
5
+ const { isDark } = useValaxyDark()
6
+ const gradientStyles = computed(() => {
7
+ if (isDark.value) {
8
+ return {
9
+ '--gradient-from': '0 0 0',
10
+ '--gradient-to': '0 0 0',
11
+ }
12
+ }
13
+ return {
14
+ '--gradient-from': '161 196 253',
15
+ '--gradient-to': '194 233 251',
16
+ }
17
+ })
18
+ </script>
19
+
20
+ <template>
21
+ <div class="yun-page-header-gradient" :style="gradientStyles" />
22
+ </template>
23
+
24
+ <style lang="scss">
25
+ .yun-page-header-gradient {
26
+ position: absolute;
27
+ top: 0;
28
+ left: 0;
29
+ right: 0;
30
+ width: 100%;
31
+ height: 500px;
32
+ z-index: var(--yun-z-page-gradient);
33
+ pointer-events: none;
34
+ background: linear-gradient(to right,rgb(var(--gradient-from) / 0.2) 0,rgb(var(--gradient-to) / .2) 100%);
35
+ mask-image: linear-gradient(#000,#fff0 70%);
36
+ animation: fade-in 2s;
37
+ }
38
+ </style>
@@ -0,0 +1,94 @@
1
+ <script setup lang="ts">
2
+ import { type CSSProperties, computed } from 'vue'
3
+ import { TinyColor } from '@ctrl/tinycolor'
4
+ import type { ProjectItem } from '../../types'
5
+
6
+ const props = defineProps<{ project: ProjectItem }>()
7
+ const cardStyle = computed(() => {
8
+ const styles: CSSProperties = {
9
+ color: props.project.textColor,
10
+ }
11
+ if (props.project.color && (typeof props.project.gradient === 'undefined' || props.project.gradient)) {
12
+ const color = new TinyColor(props.project.color)
13
+ styles['--un-gradient-stops'] = `${color.spin(55).toHexString()}, ${props.project.color}`
14
+ if (!styles.color)
15
+ styles.color = color.isDark() ? 'white' : 'black'
16
+ }
17
+ else {
18
+ styles.backgroundColor = props.project.color || 'rgba(255,255,255,0.9)'
19
+ if (!styles.color)
20
+ styles.color = 'black'
21
+ }
22
+ return styles
23
+ })
24
+
25
+ const githubUrl = computed(() => {
26
+ if (props.project.github)
27
+ return `https://github.com/${props.project.github}`
28
+ else
29
+ return `https://github.com/YunYouJun/${props.project.name}`
30
+ })
31
+
32
+ const npmLink = computed(() => {
33
+ return props.project.npm ? `https://www.npmjs.com/package/${props.project.npm}` : ''
34
+ })
35
+
36
+ const projectUrl = computed(() => {
37
+ return props.project.url || githubUrl.value
38
+ })
39
+
40
+ const links = computed(() => [
41
+ {
42
+ url: projectUrl.value,
43
+ icon: 'i-ri-global-line',
44
+ color: '#6eb7f9',
45
+ },
46
+ {
47
+ url: props.project.docs,
48
+ icon: 'i-ri-book-line',
49
+ color: '#443ed1',
50
+ },
51
+ {
52
+ url: githubUrl.value,
53
+ icon: 'i-ri-github-line',
54
+ color: 'black',
55
+ },
56
+ {
57
+ url: npmLink.value,
58
+ icon: 'i-ri-npmjs-line',
59
+ color: 'red',
60
+ },
61
+ ])
62
+ </script>
63
+
64
+ <template>
65
+ <div
66
+ flex="~ col center"
67
+ class="m-2 w-90 transform rounded shadow-md grayscale-30 transition duration-400"
68
+ bg="opacity-80 gradient-to-br"
69
+ p="x-2 b-12"
70
+ hover="shadow-lg grayscale-0"
71
+ :style="cardStyle"
72
+ >
73
+ <div v-if="project.emoji" class="mt-4">
74
+ {{ project.emoji }}
75
+ </div>
76
+ <a :href="projectUrl" target="_blank" class="text-unset">
77
+ <h2 text="lg" font="bold" m="2">
78
+ {{ project.name || '忘记叫啥了' }}
79
+ </h2>
80
+ </a>
81
+ <small
82
+ class="block" p="2" text="center"
83
+ v-html="project.desc || '说点什么好呢'"
84
+ />
85
+ <div flex="~ center" class="absolute left-0 right-0 bottom-0 h-10">
86
+ <template v-for="item in links" :key="item.icon">
87
+ <YunProjectLinkItem
88
+ v-if="item.url"
89
+ :item="item"
90
+ />
91
+ </template>
92
+ </div>
93
+ </div>
94
+ </template>
@@ -0,0 +1,15 @@
1
+ <script lang="ts" setup>
2
+ import type { ProjectItem } from '../../types'
3
+
4
+ defineProps<{
5
+ title: string
6
+ projects: ProjectItem[]
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <div class="w-full flex justify-center" text="xl" font="black" m="b-2 t-4">
12
+ {{ title }}
13
+ </div>
14
+ <YunProjectCard v-for="project, i in projects" :key="i" :project="project" />
15
+ </template>
@@ -0,0 +1,60 @@
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ item: {
4
+ url: string
5
+ icon: string
6
+ color: string
7
+ }
8
+ }>()
9
+ </script>
10
+
11
+ <template>
12
+ <a
13
+ class="yun-project-link-item inline-flex items-center justify-center flex-1 p-2 h-full text-white"
14
+ :href="item.url" target="_blank"
15
+ :style="{
16
+ '--c-brand': item.color,
17
+ }"
18
+ >
19
+ <div :class="item.icon" />
20
+ </a>
21
+ </template>
22
+
23
+ <style lang="scss">
24
+ @use 'sass:map';
25
+ @use 'valaxy-theme-yun/styles/vars.scss' as *;
26
+
27
+ .yun-project-link-item {
28
+ position: relative;
29
+ background-color: rgb(0 0 0 / 0.05);
30
+ transition: background-color 0.3s;
31
+
32
+ &::before {
33
+ content: '';
34
+ position: absolute;
35
+ background-color: var(--c-brand);
36
+ transition: all 0.3s map.get($cubic-bezier, 'ease-in');
37
+ bottom: 0;
38
+ left: 0;
39
+ right: 0;
40
+ height: 0;
41
+ }
42
+
43
+ &::after {
44
+ content: '';
45
+ position: absolute;
46
+ background-color: var(--c-brand);
47
+ transition: all 0.3s map.get($cubic-bezier, 'ease-in');
48
+ inset: 0;
49
+ opacity: 0.1;
50
+ z-index: -1;
51
+ }
52
+
53
+ &:hover {
54
+ &::before {
55
+ height: 40px;
56
+ opacity: 0.8;
57
+ }
58
+ }
59
+ }
60
+ </style>