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,59 @@
1
+ <script setup lang="ts">
2
+ import { useMotion } from '@vueuse/motion'
3
+ import Popover from 'primevue/popover'
4
+ import { onMounted, ref } from 'vue'
5
+
6
+ const op = ref<typeof Popover>()
7
+
8
+ const pContentRef = ref<HTMLElement>()
9
+ const motion = useMotion(pContentRef, {
10
+ initial: {
11
+ opacity: 0,
12
+ translateY: 30,
13
+ },
14
+ enter: {
15
+ opacity: 1,
16
+ translateY: 0,
17
+ transition: {
18
+ type: 'spring',
19
+ duration: 200,
20
+ damping: 9,
21
+ },
22
+ },
23
+ })
24
+
25
+ onMounted(() => {
26
+ motion.variant.value = 'initial'
27
+
28
+ // 滚动时隐藏
29
+ window.addEventListener('scroll', () => {
30
+ motion.variant.value = 'initial'
31
+ setTimeout(() => {
32
+ op.value?.hide()
33
+ }, 200)
34
+ })
35
+ })
36
+
37
+ function toggle(event: Event) {
38
+ op.value?.toggle(event)
39
+ motion.variant.value = op.value?.visible ? 'enter' : 'initial'
40
+ }
41
+ </script>
42
+
43
+ <template>
44
+ <YunNavMenuItem
45
+ icon="i-ri-mind-map" @click="toggle"
46
+ />
47
+ <Popover
48
+ ref="op"
49
+ >
50
+ <div
51
+ ref="pContentRef"
52
+ class="p-4 shadow-xl"
53
+ bg="$va-c-bg-light"
54
+ >
55
+ <YunSiteInfo class="text-center" />
56
+ <YunPostsInfo />
57
+ </div>
58
+ </Popover>
59
+ </template>
@@ -17,26 +17,21 @@
17
17
  <style lang="scss">
18
18
  @use 'valaxy/client/styles/mixins/index.scss' as *;
19
19
 
20
+ .dark .yun-cloud {
21
+ --yun-c-cloud: var(--va-c-bg-soft);
22
+ }
23
+
20
24
  .yun-cloud {
21
25
  display: flex;
22
26
  width: 100%;
23
- position: absolute;
24
- bottom: 0;
25
- left: 0;
26
- right: 0;
27
27
  z-index: var(--yun-z-cloud);
28
28
  box-sizing: border-box;
29
- mix-blend-mode: overlay;
30
29
 
31
30
  .waves {
32
31
  display: flex;
33
32
  position: relative;
34
33
  width: 100%;
35
- height: 100px;
36
-
37
- @include screen('md') {
38
- height: 40px;
39
- }
34
+ height: 40px;
40
35
  }
41
36
 
42
37
  .parallax {
@@ -1,27 +1,10 @@
1
1
  <script lang="ts" setup>
2
- import { useI18n } from 'vue-i18n'
3
- import { computed } from 'vue'
4
- import { useAppStore } from 'valaxy'
5
-
6
- const appStore = useAppStore()
7
- const { t } = useI18n()
8
-
9
- const themeTitle = computed(() => {
10
- return appStore.isDark ? t('button.toggle_light') : t('button.toggle_dark')
11
- })
12
-
13
- const styles = computed(() => {
14
- return {
15
- color: appStore.isDark ? '' : '#f1cb64',
16
- }
17
- })
2
+ // sidebar config
18
3
  </script>
19
4
 
20
5
  <template>
21
6
  <div>
22
- <button class="yun-icon-btn" :title="themeTitle" :style="styles" @click="appStore.toggleDarkWithTransition">
23
- <div i="ri-sun-line dark:ri-moon-line" />
24
- </button>
7
+ <YunToggleDark />
25
8
 
26
9
  <YunToggleLocale />
27
10
  </div>
@@ -0,0 +1,47 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { useYunAppStore } from '../stores'
4
+
5
+ const yun = useYunAppStore()
6
+
7
+ const infoList = computed(() => {
8
+ return [
9
+ {
10
+ label: 'xs',
11
+ value: yun.size.isXs,
12
+ },
13
+ {
14
+ label: 'sm',
15
+ value: yun.size.isSm,
16
+ },
17
+ {
18
+ label: 'md',
19
+ value: yun.size.isMd,
20
+ },
21
+ {
22
+ label: 'lg',
23
+ value: yun.size.isLg,
24
+ },
25
+ {
26
+ label: 'xl',
27
+ value: yun.size.isXl,
28
+ },
29
+ {
30
+ label: '2xl',
31
+ value: yun.size.is2xl,
32
+ },
33
+ ]
34
+ })
35
+ </script>
36
+
37
+ <template>
38
+ <div
39
+ class="bg-black/50 fixed bottom-2 left-2 p-2 gap-1 rounded z-9999"
40
+ text="xs white" flex="~ col"
41
+ >
42
+ <div v-for="item in infoList" :key="item.label" class="gap-2 inline-flex">
43
+ <span class="w-6" font="bold">{{ item.label }}: </span>
44
+ <span>{{ item.value ? '✅' : '❌' }}</span>
45
+ </div>
46
+ </div>
47
+ </template>
@@ -0,0 +1,223 @@
1
+ <script setup>
2
+ import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
3
+ import { useToast } from 'primevue/usetoast'
4
+ import TerminalService from 'primevue/terminalservice'
5
+ import Dock from 'primevue/dock'
6
+ import Toast from 'primevue/toast'
7
+ import { useYunAppStore } from '../stores'
8
+
9
+ onMounted(() => {
10
+ TerminalService.on('command', commandHandler)
11
+ })
12
+
13
+ onBeforeUnmount(() => {
14
+ TerminalService.off('command', commandHandler)
15
+ })
16
+
17
+ const yunApp = useYunAppStore()
18
+ const showDock = ref(false)
19
+ watch(() => yunApp.scrollY, () => {
20
+ if (yunApp.scrollY > 10)
21
+ showDock.value = true
22
+ else
23
+ showDock.value = false
24
+ })
25
+
26
+ const displayFinder = ref(false)
27
+ const displayTerminal = ref(false)
28
+ const displayPhotos = ref(false)
29
+ const toast = useToast()
30
+ const items = ref([
31
+ {
32
+ label: 'Finder',
33
+ icon: 'https://primefaces.org/cdn/primevue//images/dock/finder.svg',
34
+ command: () => {
35
+ displayFinder.value = true
36
+ },
37
+ },
38
+ {
39
+ label: 'Terminal',
40
+ icon: 'https://primefaces.org/cdn/primevue//images/dock/terminal.svg',
41
+ command: () => {
42
+ displayTerminal.value = true
43
+ },
44
+ },
45
+ {
46
+ label: 'App Store',
47
+ icon: 'https://primefaces.org/cdn/primevue//images/dock/appstore.svg',
48
+ command: () => {
49
+ toast.add({ severity: 'error', summary: 'An unexpected error occurred while signing in.', detail: 'UNTRUSTED_CERT_TITLE', group: 'tc', life: 3000 })
50
+ },
51
+ },
52
+ {
53
+ label: 'Safari',
54
+ icon: 'https://primefaces.org/cdn/primevue//images/dock/safari.svg',
55
+ command: () => {
56
+ toast.add({ severity: 'warn', summary: 'Safari has stopped working', group: 'tc', life: 3000 })
57
+ },
58
+ },
59
+ {
60
+ label: 'Photos',
61
+ icon: 'https://primefaces.org/cdn/primevue//images/dock/photos.svg',
62
+ command: () => {
63
+ displayPhotos.value = true
64
+ },
65
+ },
66
+ {
67
+ label: 'GitHub',
68
+ icon: 'https://primefaces.org/cdn/primevue//images/dock/github.svg',
69
+ },
70
+ {
71
+ label: 'Trash',
72
+ icon: 'https://primefaces.org/cdn/primevue//images/dock/trash.png',
73
+ command: () => {
74
+ toast.add({ severity: 'info', summary: 'Empty Trash', life: 3000 })
75
+ },
76
+ },
77
+ ])
78
+
79
+ function onDockItemClick(event, item) {
80
+ if (item.command)
81
+ item.command()
82
+
83
+ event.preventDefault()
84
+ }
85
+
86
+ function commandHandler(text) {
87
+ let response
88
+ const argsIndex = text.indexOf(' ')
89
+ const command = argsIndex !== -1 ? text.substring(0, argsIndex) : text
90
+
91
+ switch (command) {
92
+ case 'date':
93
+ response = `Today is ${new Date().toDateString()}`
94
+ break
95
+
96
+ case 'greet':
97
+ response = `Hola ${text.substring(argsIndex + 1)}`
98
+ break
99
+
100
+ case 'random':
101
+ response = Math.floor(Math.random() * 100)
102
+ break
103
+
104
+ default:
105
+ response = `Unknown command: ${command}`
106
+ }
107
+
108
+ TerminalService.emit('response', response)
109
+ }
110
+ </script>
111
+
112
+ <template>
113
+ <Toast position="top-center" group="tc" />
114
+
115
+ <!-- yun-dock slide in -->
116
+ <Transition
117
+ name="custom-classes"
118
+ enter-from-class="opacity-0 translate-y-10"
119
+ enter-to-class="opacity-100 translate-y-0"
120
+ leave-from-class="opacity-100 translate-y-0"
121
+ leave-to-class="opacity-0 translate-y-10"
122
+ >
123
+ <Dock
124
+ v-show="showDock"
125
+ class="fixed bottom-0 left-0 right-0 z-99 transition" flex="~"
126
+ :model="items"
127
+ >
128
+ <template #item="{ item }">
129
+ <a
130
+ v-tooltip.top="item.label" href="#"
131
+ class="yun-dock-item-link" @click="onDockItemClick($event, item)"
132
+ >
133
+ <img :alt="item.label" :src="item.icon" style="width: 100%">
134
+ </a>
135
+ </template>
136
+ </Dock>
137
+ </Transition>
138
+
139
+ <!-- <div class="dock-window dock-advanced">
140
+ <Dialog v-model:visible="displayTerminal" header="Terminal" :breakpoints="{ '960px': '50vw' }" :style="{ width: '40vw' }" :maximizable="true">
141
+ <Terminal welcome-message="Welcome to PrimeVue(cmd: 'date', 'greet {0}' and 'random')" prompt="primevue $" />
142
+ </Dialog>
143
+
144
+ <Dialog v-model:visible="displayFinder" header="Finder" :breakpoints="{ '960px': '50vw' }" :style="{ width: '40vw' }" :maximizable="true">
145
+ <Tree :value="nodes" />
146
+ </Dialog>
147
+
148
+ <Galleria v-model:visible="displayPhotos" :value="images" :responsive-options="responsiveOptions" :num-visible="2" container-style="width: 400px" :circular="true" :full-screen="true" :show-thumbnails="false" :show-item-navigators="true">
149
+ <template #item="slotProps">
150
+ <img :src="slotProps.item.itemImageSrc" :alt="slotProps.item.alt" style="width: 100%">
151
+ </template>
152
+ </Galleria>
153
+ </div> -->
154
+ </template>
155
+
156
+ <style lang="scss">
157
+ :root {
158
+ --p-dock-background: rgb(255 255 255 / 0.1);
159
+ --p-dock-border-color: rgb(255 255 255 / 0.2);
160
+ --p-dock-padding: 0.5rem;
161
+ --p-dock-border-radius: var(--p-border-radius-xl);
162
+ --p-dock-item-border-radius: var(--p-content-border-radius);
163
+ --p-dock-item-padding: 0.5rem;
164
+ --p-dock-item-size: 3rem;
165
+ --p-dock-item-focus-ring-width: var(--p-focus-ring-width);
166
+ --p-dock-item-focus-ring-style: var(--p-focus-ring-style);
167
+ --p-dock-item-focus-ring-color: var(--p-focus-ring-color);
168
+ --p-dock-item-focus-ring-offset: var(--p-focus-ring-offset);
169
+ --p-dock-item-focus-ring-shadow: var(--p-focus-ring-shadow);
170
+ }
171
+
172
+ .yun-dock {
173
+ position: fixed;
174
+ display: flex;
175
+ justify-content: center;
176
+ align-items: center;
177
+ z-index: var(--yun-z-dock);
178
+
179
+ &-bottom {
180
+ left: 0;
181
+ bottom: 0;
182
+ width: 100%;
183
+ }
184
+
185
+ .yun-dock-list-container {
186
+ display: flex;
187
+ pointer-events: auto;
188
+ background: var(--p-dock-background);
189
+ border: 1px solid var(--p-dock-border-color);
190
+ padding: var(--p-dock-padding);
191
+ border-radius: var(--p-dock-border-radius);
192
+ }
193
+ }
194
+
195
+ .yun-dock-list {
196
+ margin: 0;
197
+ padding: 0;
198
+ list-style: none;
199
+ display: flex;
200
+ align-items: center;
201
+ justify-content: center;
202
+ outline: 0 none;
203
+ }
204
+
205
+ .yun-dock-item {
206
+ transition: all 0.2s cubic-bezier(0.4,0,0.2,1);
207
+ will-change: transform;
208
+ padding: var(--p-dock-item-padding);
209
+ border-radius: var(--p-dock-item-border-radius);
210
+ }
211
+
212
+ .yun-dock-item-link {
213
+ display: flex;
214
+ flex-direction: column;
215
+ align-items: center;
216
+ justify-content: center;
217
+ position: relative;
218
+ overflow: hidden;
219
+ cursor: pointer;
220
+ width: var(--p-dock-item-size);
221
+ height: var(--p-dock-item-size);
222
+ }
223
+ </style>
@@ -1,10 +1,25 @@
1
1
  <script lang="ts" setup>
2
2
  import { capitalize, computed } from 'vue'
3
- import { useSiteConfig, useValaxyConfig } from 'valaxy'
3
+ import { useSiteConfig, useValaxyConfig, useValaxyDark } from 'valaxy'
4
4
  import { useI18n } from 'vue-i18n'
5
5
  import pkg from 'valaxy/package.json'
6
6
  import { useThemeConfig } from '../composables'
7
7
 
8
+ // background-image: linear-gradient(120deg, #a1c4fd 0%, #c2e9fb 100%);
9
+ const { isDark } = useValaxyDark()
10
+ const gradientStyles = computed(() => {
11
+ if (isDark.value) {
12
+ return {
13
+ '--gradient-from': '0 0 0',
14
+ '--gradient-to': '0 0 0',
15
+ }
16
+ }
17
+ return {
18
+ '--gradient-from': '161 196 253',
19
+ '--gradient-to': '194 233 251',
20
+ }
21
+ })
22
+
8
23
  const { t } = useI18n()
9
24
  const config = useValaxyConfig()
10
25
  const siteConfig = useSiteConfig()
@@ -15,7 +30,7 @@ const isThisYear = computed(() => {
15
30
  return year === themeConfig.value.footer.since
16
31
  })
17
32
 
18
- const poweredHtml = computed(() => t('footer.powered', [`<a href="${pkg.repository.url}" target="_blank" rel="noopener">Valaxy</a> v${pkg.version}`]))
33
+ const poweredHtml = computed(() => t('footer.powered', [`<a href="${pkg.repository.url}" target="_blank" rel="noopener">Valaxy</a> <span class="op-60">v${pkg.version}</span>`]))
19
34
  const footerIcon = computed(() => themeConfig.value.footer.icon || {
20
35
  url: pkg.repository.url,
21
36
  name: 'i-ri-cloud-line',
@@ -24,7 +39,14 @@ const footerIcon = computed(() => themeConfig.value.footer.icon || {
24
39
  </script>
25
40
 
26
41
  <template>
27
- <footer class="va-footer p-4 text-$va-c-text-light" text="center sm">
42
+ <footer
43
+ flex="~ col"
44
+ class="relative yun-footer va-footer px-4 py-4 pt-0 text-$va-c-text-light w-full mt-14"
45
+ bg="white dark:$va-c-bg-soft"
46
+ text="center sm"
47
+ >
48
+ <YunCloud class="absolute top--10 left-0 right-0" />
49
+
28
50
  <div v-if="themeConfig.footer.beian?.enable && themeConfig.footer.beian.icp" class="beian" m="y-2">
29
51
  <a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">
30
52
  {{ themeConfig.footer.beian.icp }}
@@ -54,9 +76,39 @@ const footerIcon = computed(() => themeConfig.value.footer.icon || {
54
76
  </div>
55
77
 
56
78
  <div v-if="themeConfig.footer.powered" class="powered" m="2">
57
- <span v-html="poweredHtml" /> | <span>{{ t('footer.theme') }} - <a :href="themeConfig.pkg.repository.url" :title="themeConfig.pkg.name" target="_blank">{{ capitalize(config.theme) }}</a> v{{ themeConfig.pkg.version }}</span>
79
+ <span v-html="poweredHtml" />
80
+ <span mx-1>|</span>
81
+ <span>
82
+ <span>{{ t('footer.theme') }}</span>
83
+ <span mx-1>-</span>
84
+ <a :href="themeConfig.pkg.repository.url" :title="themeConfig.pkg.name" target="_blank">{{ capitalize(config.theme) }}</a>
85
+ <span class="ml-1 op-60">v{{ themeConfig.pkg.version }}</span>
86
+ </span>
58
87
  </div>
59
88
 
60
89
  <slot />
90
+
91
+ <div class="yun-footer-gradient" :style="gradientStyles" />
61
92
  </footer>
62
93
  </template>
94
+
95
+ <style lang="scss">
96
+ .yun-footer {
97
+ letter-spacing: 0.05rem;
98
+ line-height: 1.8;
99
+ }
100
+
101
+ .yun-footer-gradient {
102
+ position: absolute;
103
+ bottom: 0;
104
+ left: 0;
105
+ right: 0;
106
+ width: 100%;
107
+ height: 300px;
108
+ z-index: 999;
109
+ pointer-events: none;
110
+ background: linear-gradient(to right,rgb(var(--gradient-from) / 0.2) 0,rgb(var(--gradient-to) / .2) 100%);
111
+ mask-image: linear-gradient(#fff0,#000 70%);
112
+ animation: fade-in 2s;
113
+ }
114
+ </style>
@@ -0,0 +1,57 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import { useYunAppStore } from '../stores'
4
+
5
+ const yunApp = useYunAppStore()
6
+ const fullscreenMenuRef = ref<HTMLElement>()
7
+ </script>
8
+
9
+ <template>
10
+ <Transition name="slide-down">
11
+ <div
12
+ v-if="yunApp.fullscreenMenu.isOpen"
13
+ ref="fullscreenMenuRef"
14
+ p="t-20 md:t-26"
15
+ class="yun-fullscreen-menu fixed left-0 right-0 bottom-0 top-0 bg-$va-c-bg-soft z-$yun-z-fullscreen-menu overflow-auto"
16
+ >
17
+ <div class="flex-center gap-2">
18
+ <YunToggleDark transition />
19
+ <YunToggleLocale />
20
+ </div>
21
+
22
+ <YunFullscreenMenuList>
23
+ <YunFullscreenMenuItem
24
+ :page="{
25
+ name: '站点主页',
26
+ icon: 'i-ri-home-2-line',
27
+ url: '/',
28
+ }"
29
+ />
30
+ </YunFullscreenMenuList>
31
+
32
+ <div v-if="!yunApp.size.isLg" class="mt-4">
33
+ <YunSiteInfo class="text-center" />
34
+ <YunPostsInfo />
35
+ </div>
36
+ </div>
37
+ </Transition>
38
+ </template>
39
+
40
+ <style lang="scss">
41
+ @use 'sass:map';
42
+ @use 'valaxy-theme-yun/styles/vars.scss' as *;
43
+
44
+ .slide-down-enter-active,
45
+ .slide-down-leave-active {
46
+ opacity: 1;
47
+ transition: transform 0.4s map.get($cubic-bezier, 'ease-in-out'),
48
+ opacity 0.2s map.get($cubic-bezier, 'ease-in-out');
49
+ transform: translateY(0);
50
+ }
51
+
52
+ .slide-down-enter-from,
53
+ .slide-down-leave-to {
54
+ opacity: 0;
55
+ transform: translateY(-100%);
56
+ }
57
+ </style>
@@ -0,0 +1,98 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import { useMotion } from '@vueuse/motion'
4
+ import { onImgError } from '../utils'
5
+ import type { GirlType } from '../types'
6
+
7
+ const props = defineProps<{
8
+ i: number
9
+ girl: GirlType
10
+ }>()
11
+
12
+ const itemRef = ref()
13
+ useMotion(itemRef, {
14
+ initial: {
15
+ opacity: 0,
16
+ translateY: 40,
17
+ },
18
+ enter: {
19
+ opacity: 1,
20
+ translateY: 0,
21
+ transition: {
22
+ type: 'spring',
23
+ duration: 400,
24
+ damping: 8,
25
+ delay: props.i * 50,
26
+ },
27
+ },
28
+ })
29
+ </script>
30
+
31
+ <template>
32
+ <li ref="itemRef" class="girl-item">
33
+ <a
34
+ class="girl-item-link"
35
+ :href="girl.url || `https://zh.moegirl.org/${girl.name}`"
36
+ :title="girl.reason" alt="portrait" target="_blank" rel="noopener"
37
+ >
38
+ <figure class="girl-info">
39
+ <img class="girl-avatar" loading="lazy" :src="girl.avatar" :alt="girl.name" :onError="onImgError">
40
+ <figcaption class="girl-name" :title="(i + 1).toString()">{{ girl.name }}</figcaption>
41
+ <figcaption class="girl-from">{{ girl.from }}</figcaption>
42
+ </figure>
43
+ </a>
44
+ </li>
45
+ </template>
46
+
47
+ <style lang="scss">
48
+ .girl-item {
49
+ display: inline-flex;
50
+ text-align: center;
51
+ justify-content: center;
52
+ width: 8rem;
53
+ margin: 1rem;
54
+
55
+ .girl {
56
+ &-info {
57
+ width: 100%;
58
+ padding: 0;
59
+ margin: 0;
60
+ }
61
+
62
+ &-avatar {
63
+ object-fit: cover;
64
+ object-position: center top;
65
+ width: 4rem;
66
+ height: 4rem;
67
+ border-radius: 50%;
68
+ padding: 0.2rem;
69
+ background-color: #fff;
70
+ box-shadow: 0 0 1rem rgb(0 0 0 / 0.12);
71
+ transition: 0.5s;
72
+
73
+ &:hover {
74
+ box-shadow: 0 0 2rem rgb(0 0 0 / 0.12);
75
+ }
76
+ }
77
+
78
+ &-name {
79
+ font-size: 0.9rem;
80
+ }
81
+
82
+ &-from {
83
+ font-size: 12px;
84
+ font-family: var(--va-font-serif);
85
+ font-weight: bold;
86
+ color: var(--va-c-text-light);
87
+
88
+ &::before {
89
+ content: '「';
90
+ }
91
+
92
+ &::after {
93
+ content: '」';
94
+ }
95
+ }
96
+ }
97
+ }
98
+ </style>