valaxy-theme-yun 0.19.13 → 0.20.0-beta.2
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/App.vue +30 -4
- package/bump.config.ts +7 -0
- package/client/constants/index.ts +13 -0
- package/components/ValaxyMain.vue +48 -52
- package/components/YunAdBoard.vue +4 -0
- package/components/YunAside.vue +66 -43
- package/components/YunBackToTop.vue +11 -4
- package/components/YunBanner.vue +31 -15
- package/components/YunBg.vue +2 -0
- package/components/YunCard.vue +5 -1
- package/components/YunCategories.vue +14 -34
- package/components/YunCategory.vue +42 -11
- package/components/YunClassifyPopover.vue +59 -0
- package/components/YunCloud.vue +5 -10
- package/components/YunConfig.vue +2 -19
- package/components/YunDebug.vue +47 -0
- package/components/YunDock.vue +223 -0
- package/components/YunFooter.vue +56 -4
- package/components/YunFullscreenMenu.vue +57 -0
- package/components/YunGirlItem.vue +98 -0
- package/components/YunGirls.vue +2 -73
- package/components/YunGoDown.vue +8 -15
- package/components/YunLinkItem.vue +62 -0
- package/components/YunLinks.vue +15 -47
- package/components/YunNavMenu.vue +125 -0
- package/components/YunOutline.vue +1 -2
- package/components/YunOverlay.vue +31 -0
- package/components/YunOverview.vue +2 -74
- package/components/YunPageHeader.vue +1 -1
- package/components/YunPagination.vue +105 -0
- package/components/YunPostCard.vue +32 -3
- package/components/YunPostCategories.vue +3 -3
- package/components/YunPostCollapse.vue +7 -72
- package/components/YunPostCollapseItem.vue +137 -0
- package/components/YunPostDateMeta.vue +30 -0
- package/components/YunPostList.vue +6 -11
- package/components/YunPostMeta.vue +31 -39
- package/components/YunPostTags.vue +2 -2
- package/components/YunPostsInfo.vue +41 -0
- package/components/YunPrologue.vue +24 -0
- package/components/YunSearchBtn.vue +8 -6
- package/components/YunSelect.vue +1 -11
- package/components/YunSidebar.vue +7 -4
- package/components/YunSidebarCard.vue +10 -0
- package/components/YunSidebarNav.vue +0 -1
- package/components/YunSiteInfo.vue +72 -0
- package/components/YunSponsor.vue +21 -6
- package/components/animation/LineBurstEffects.vue +75 -0
- package/components/author/YunAuthorAvatar.vue +12 -0
- package/components/author/YunAuthorIntro.vue +11 -0
- package/components/author/YunAuthorName.vue +14 -0
- package/components/config/YunToggleDark.vue +37 -0
- package/components/layout/YunLayoutLeft.vue +12 -0
- package/components/layout/YunLayoutRight.vue +21 -0
- package/components/layout/YunLayoutWrapper.vue +17 -0
- package/components/menu/YunNavMenuItem.vue +22 -0
- package/components/menu/YunNavMenuTitle.vue +86 -0
- package/components/menu/YunPostClassifyNavItem.vue +30 -0
- package/components/page/YunPageHeaderGradient.vue +38 -0
- package/components/project/YunProjectCard.vue +94 -0
- package/components/project/YunProjectCollection.vue +15 -0
- package/components/project/YunProjectLinkItem.vue +60 -0
- package/components/project/YunProjectToggleButton.vue +16 -0
- package/components/project/YunProjects.vue +48 -0
- package/components/prologue/PrologueSocialIcon.vue +58 -0
- package/components/prologue/PrologueSocialLinks.vue +16 -0
- package/components/prologue/PrologueSquare.vue +144 -0
- package/components/prologue/YunAEFrame.vue +155 -0
- package/components/prologue/YunAERect.vue +127 -0
- package/components/site/YunFullscreenMenuItem.vue +26 -0
- package/components/site/YunFullscreenMenuList.vue +19 -0
- package/components/site/YunSiteLinkItem.vue +26 -0
- package/components/site/YunSiteLinks.vue +19 -0
- package/components/site/YunSiteTitle.vue +59 -0
- package/components/third/YunWalineMeta.vue +17 -5
- package/docs/zh-CN/config.md +0 -3
- package/layouts/archives.vue +33 -21
- package/layouts/categories.vue +43 -31
- package/layouts/default.vue +10 -5
- package/layouts/empty.vue +3 -0
- package/layouts/home.vue +12 -5
- package/layouts/post.vue +23 -20
- package/layouts/posts.vue +10 -0
- package/layouts/projects.vue +25 -0
- package/layouts/tags.vue +45 -41
- package/node/config.ts +2 -5
- package/package.json +15 -7
- package/pages/page/[page].vue +3 -6
- package/pages/posts/index.vue +11 -0
- package/setup/main.ts +51 -9
- package/stores/app.ts +25 -3
- package/styles/animations/index.scss +36 -0
- package/styles/common/markdown.scss +4 -0
- package/styles/css-vars.scss +19 -1
- package/styles/global.scss +8 -0
- package/styles/index.scss +1 -0
- package/styles/layout/index.scss +3 -0
- package/styles/modules/prologue.scss +1 -0
- package/styles/primevue/index.ts +7 -0
- package/styles/primevue/tooltip.scss +55 -0
- package/styles/primevue/tooltip.ts +14 -0
- package/styles/vars.scss +23 -2
- package/styles/widgets/banner.scss +26 -6
- package/types/index.d.ts +53 -5
- package/types/projects.ts +48 -0
- package/unocss.config.ts +1 -1
- package/utils/animation.ts +33 -0
- package/utils/index.ts +2 -0
- package/LICENSE +0 -21
@@ -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-
|
21
|
+
<div class="mt-2px" i-ri-heart-fill />
|
22
22
|
</button>
|
23
23
|
|
24
|
-
<div
|
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:
|
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:
|
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
|
-
|
79
|
+
opacity: 0;
|
80
|
+
transition: all var(--va-transition-duration) map.get($cubic-bezier, 'ease-in-out');
|
73
81
|
|
74
82
|
&.show {
|
75
|
-
height:
|
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,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>
|