valaxy-theme-hairy 0.0.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 (102) hide show
  1. package/@types/markdown-it.d.ts +1 -0
  2. package/@types/markdown-toc.d.ts +1 -0
  3. package/@types/types.d.ts +1 -0
  4. package/@types/valaxy.d.ts +10 -0
  5. package/LICENSE +21 -0
  6. package/components/HairyAlgoliaSearchBox.vue +118 -0
  7. package/components/HairyArticleImage.vue +17 -0
  8. package/components/HairyArticleImageDefault.vue +127 -0
  9. package/components/HairyArticleImageSmall.vue +49 -0
  10. package/components/HairyArticleSeries.vue +73 -0
  11. package/components/HairyArticleText.vue +38 -0
  12. package/components/HairyArticleTop.vue +0 -0
  13. package/components/HairyBackToTop.vue +72 -0
  14. package/components/HairyBody.vue +55 -0
  15. package/components/HairyBreadcrumb.vue +51 -0
  16. package/components/HairyBreadcrumbItem.vue +14 -0
  17. package/components/HairyCarousel.vue +45 -0
  18. package/components/HairyDivider.vue +0 -0
  19. package/components/HairyHeader.vue +31 -0
  20. package/components/HairyImage.vue +14 -0
  21. package/components/HairyImageGroup.vue +69 -0
  22. package/components/HairyImageViewer.vue +22 -0
  23. package/components/HairyLayout.vue +29 -0
  24. package/components/HairyLink.vue +10 -0
  25. package/components/HairyMenu.vue +15 -0
  26. package/components/HairyMenuItem.vue +40 -0
  27. package/components/HairyMeting.vue +19 -0
  28. package/components/HairyNav.vue +39 -0
  29. package/components/HairyNavBackground.vue +7 -0
  30. package/components/HairyNavSearch.vue +265 -0
  31. package/components/HairyNavTitle.vue +13 -0
  32. package/components/HairyNavToggleDark.vue +16 -0
  33. package/components/HairyPostImageList.vue +28 -0
  34. package/components/HairyPostList.vue +24 -0
  35. package/components/HairyPostTitle.vue +33 -0
  36. package/components/HairySocialLinks.vue +27 -0
  37. package/components/HairyTimelinePostItem.vue +40 -0
  38. package/components/HairyToc.vue +135 -0
  39. package/components/HairyUserCard.vue +30 -0
  40. package/components/HairyUserNav.vue +68 -0
  41. package/components/HairyUserTab.vue +40 -0
  42. package/components/HairyWaline.vue +25 -0
  43. package/components/HairyWaves.vue +67 -0
  44. package/components/ValaxyMain.vue +45 -0
  45. package/hooks/useCategory.ts +18 -0
  46. package/hooks/useCategoryPost.ts +21 -0
  47. package/hooks/useContext.ts +16 -0
  48. package/hooks/useHeaderHeight.ts +9 -0
  49. package/hooks/usePostLayout.ts +9 -0
  50. package/hooks/useYearArchives.ts +43 -0
  51. package/images.json +101 -0
  52. package/index.d.ts +54 -0
  53. package/layouts/archives.vue +11 -0
  54. package/layouts/categories.vue +6 -0
  55. package/layouts/default.vue +9 -0
  56. package/layouts/home.vue +23 -0
  57. package/layouts/month.vue +6 -0
  58. package/layouts/post.vue +26 -0
  59. package/layouts/tag.vue +6 -0
  60. package/layouts/tags.vue +6 -0
  61. package/layouts/year.vue +6 -0
  62. package/locales/en.yml +1 -0
  63. package/locales/zh-CN.yml +3 -0
  64. package/modules/context.ts +5 -0
  65. package/modules/loading.scss +531 -0
  66. package/modules/loading.ts +42 -0
  67. package/modules/scroll.ts +21 -0
  68. package/node/addon-hairy.ts +18 -0
  69. package/node/addon-images.ts +61 -0
  70. package/node/addon-meting.ts +13 -0
  71. package/node/addon-statistics.ts +19 -0
  72. package/node/addon-toc.ts +20 -0
  73. package/node/utils.ts +20 -0
  74. package/package.json +47 -0
  75. package/pages/archives/[year]/[month]/index.vue +52 -0
  76. package/pages/archives/[year]/index.vue +73 -0
  77. package/pages/archives/index.vue +53 -0
  78. package/pages/categories/[...categories].vue +115 -0
  79. package/pages/index.vue +14 -0
  80. package/pages/tags/[tag].vue +40 -0
  81. package/pages/tags/index.vue +31 -0
  82. package/setup/main.ts +11 -0
  83. package/shims.d.ts +8 -0
  84. package/styles/css-vars.scss +161 -0
  85. package/styles/element-plus/index.scss +2 -0
  86. package/styles/element-plus/tabs.scss +26 -0
  87. package/styles/element-plus/timeline.scss +19 -0
  88. package/styles/font-face.scss +20 -0
  89. package/styles/fonts/FrederickatheGreat.ttf +0 -0
  90. package/styles/fonts/Modesty.ttf +0 -0
  91. package/styles/fonts/MountainsofChristmas-Bold.ttf +0 -0
  92. package/styles/fonts/MountainsofChristmas-Regular.ttf +0 -0
  93. package/styles/fonts/Seto.ttf +0 -0
  94. package/styles/index.scss +65 -0
  95. package/styles/markdown.scss +60 -0
  96. package/styles/scrollbar.scss +26 -0
  97. package/unocss.config.ts +29 -0
  98. package/utils/createContext.ts +40 -0
  99. package/utils/index.ts +28 -0
  100. package/utils/loading.scss +531 -0
  101. package/utils/loading.ts +30 -0
  102. package/valaxy.config.ts +26 -0
@@ -0,0 +1 @@
1
+ declare module "*-markdown-it"
@@ -0,0 +1 @@
1
+ declare module "markdown-toc"
@@ -0,0 +1 @@
1
+ declare const __ALGOLIA__: boolean
@@ -0,0 +1,10 @@
1
+ declare module 'valaxy' {
2
+ export interface Post {
3
+ image: string
4
+ durations: Omit<import('reading-time').ReadTimeResults, 'text'>
5
+ length: number
6
+ }
7
+ }
8
+
9
+
10
+ export {}
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 云游君
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,118 @@
1
+ <script setup lang="ts">
2
+ import docsearch from '@docsearch/js'
3
+ import type { DocSearchHit } from '@docsearch/react/dist/esm/types'
4
+ import { onMounted } from 'vue'
5
+ import type { AlgoliaSearchOptions } from 'valaxy'
6
+ import { useConfig } from 'valaxy'
7
+ import { useRoute, useRouter } from 'vue-router'
8
+
9
+ const router = useRouter()
10
+ const route = useRoute()
11
+ const config = useConfig()
12
+
13
+ onMounted(() => {
14
+ initialize(config.value.search.algolia)
15
+ setTimeout(poll, 16)
16
+ })
17
+
18
+ /**
19
+ * poll until open
20
+ */
21
+ function poll() {
22
+ // programmatically open the search box after initialize
23
+ const e = new Event('keydown') as any
24
+
25
+ e.key = 'k'
26
+ e.metaKey = true
27
+
28
+ window.dispatchEvent(e)
29
+
30
+ setTimeout(() => {
31
+ if (!document.querySelector('.DocSearch-Modal'))
32
+ poll()
33
+ }, 16)
34
+ }
35
+
36
+ function initialize(userOptions: AlgoliaSearchOptions) {
37
+ // note: multi-lang search support is removed since the theme
38
+ // doesn't support multiple locales as of now.
39
+ const options = Object.assign({}, userOptions, {
40
+ container: '#docsearch',
41
+ navigator: {
42
+ navigate({ itemUrl }: { itemUrl: string }) {
43
+ const { pathname: hitPathname } = new URL(
44
+ window.location.origin + itemUrl,
45
+ )
46
+ // router doesn't handle same-page navigation so we use the native
47
+ // browser location API for anchor navigation
48
+ if (route.path === hitPathname)
49
+ window.location.assign(window.location.origin + itemUrl)
50
+
51
+ else
52
+ router.push(itemUrl)
53
+ },
54
+ },
55
+ transformItems(items: DocSearchHit[]) {
56
+ return items.map((item) => {
57
+ return Object.assign({}, item, {
58
+ url: getRelativePath(item.url),
59
+ })
60
+ })
61
+ },
62
+ hitComponent({ hit, children }: { hit: DocSearchHit; children: any }) {
63
+ const relativeHit = hit.url.startsWith('http')
64
+ ? getRelativePath(hit.url as string)
65
+ : hit.url
66
+ return {
67
+ __v: null,
68
+ type: 'a',
69
+ ref: undefined,
70
+ constructor: undefined,
71
+ key: undefined,
72
+ props: {
73
+ href: hit.url,
74
+ onClick(event: MouseEvent) {
75
+ if (isSpecialClick(event))
76
+ return
77
+
78
+ // we rely on the native link scrolling when user is already on
79
+ // the right anchor because Router doesn't support duplicated
80
+ // history entries.
81
+ if (route.path === relativeHit)
82
+ return
83
+
84
+ // if the hits goes to another page, we prevent the native link
85
+ // behavior to leverage the Router loading feature.
86
+ if (route.path !== relativeHit)
87
+ event.preventDefault()
88
+
89
+ router.push(relativeHit)
90
+ },
91
+ children,
92
+ },
93
+ }
94
+ },
95
+ })
96
+ docsearch(options as any)
97
+ }
98
+
99
+ function isSpecialClick(event: MouseEvent) {
100
+ return (
101
+ event.button === 1
102
+ || event.altKey
103
+ || event.ctrlKey
104
+ || event.metaKey
105
+ || event.shiftKey
106
+ )
107
+ }
108
+
109
+ function getRelativePath(absoluteUrl: string) {
110
+ const { pathname, hash } = new URL(absoluteUrl)
111
+ return pathname + hash
112
+ }
113
+ </script>
114
+
115
+ <template>
116
+ <div id="docsearch" />
117
+ </template>
118
+
@@ -0,0 +1,17 @@
1
+ <script lang="ts" setup>
2
+ import type { Post } from 'valaxy'
3
+
4
+ defineProps<{
5
+ post: Post
6
+ reverse?: boolean
7
+ }>()
8
+ </script>
9
+
10
+ <template>
11
+ <HairyArticleImageDefault :post="post" :reverse="reverse" class="hidden sm:block" />
12
+ <HairyArticleImageSmall :post="post" :reverse="reverse" class="hidden lt-sm:block" />
13
+ </template>
14
+
15
+ <style lang="scss">
16
+
17
+ </style>
@@ -0,0 +1,127 @@
1
+ <script lang="ts" setup>
2
+ import type { Post } from 'valaxy'
3
+ import { computed, defineProps } from 'vue'
4
+ import dayjs from 'dayjs'
5
+ import { useRouter } from 'vue-router'
6
+ import { useI18n } from 'vue-i18n'
7
+ import last from 'lodash/last'
8
+ import { usePostLayout } from '../hooks/usePostLayout'
9
+ import { toArr } from '../utils'
10
+
11
+ const props = defineProps<{
12
+ post: Post
13
+ reverse?: boolean
14
+ }>()
15
+ const router = useRouter()
16
+ const layout = usePostLayout()
17
+ const slice = computed(() => layout.value.includes('slice'))
18
+ const image = computed(() => props.post.image)
19
+
20
+ const i18n = useI18n()
21
+
22
+ const onReadMore = () => {
23
+ if (props.post.path)
24
+ router.push(props.post.path)
25
+ }
26
+
27
+ const displayCategory = (keys: string | string[] = []) => {
28
+ router.push({ path: `/categories/${toArr(keys).join('/')}` })
29
+ }
30
+ </script>
31
+
32
+ <template>
33
+ <li class="HairyArticleImage my-10 py-2" :class="[slice && 'slice', reverse && 'reverse']">
34
+ <article>
35
+ <div class="flex justify-between items-center">
36
+ <a class="text-size-2xl font-bold truncate cursor-pointer" :class="[reverse ? 'order-first' : 'order-last']" @click="onReadMore">{{ post.title }}</a>
37
+ <div class="flex justify-end gap-2 text-size-sm">
38
+ <span>{{ dayjs(post.date).format('YYYY-MM-DD') }}</span>
39
+ <span>{{ (post.length / 1000).toFixed(1) }}k字</span>
40
+ <span>{{ post.durations.minutes.toFixed(2) }}分钟</span>
41
+ </div>
42
+ </div>
43
+ <div class="h-200px flex">
44
+ <div class="flex-1 post-image-content" :class="[reverse ? 'order-last' : 'order-first']">
45
+ <img
46
+ class="post-image rounded-1 w-full h-full object-cover cursor-pointer" :src="image"
47
+ @click="onReadMore"
48
+ />
49
+ </div>
50
+ <div class="flex flex-col justify-between flex-1">
51
+ <div class="flex-1 text-size-sm">
52
+ <div class="line-clamp-text">
53
+ {{ post.text }}
54
+ </div>
55
+ </div>
56
+ <div class="flex justify-between items-center">
57
+ <a class="cursor-pointer" :class="[reverse && 'order-1']">
58
+ <span v-if="post.categories?.length" @click="displayCategory(post.categories)">
59
+ {{ i18n.t(last(toArr(post.categories)) || '') }}
60
+ </span>
61
+ </a>
62
+ <div class="text-base leading-6 font-medium">
63
+ <a class="link cursor-pointer" aria-label="read more" @click="onReadMore">
64
+ <span v-if="reverse">←</span>
65
+ Read more
66
+ <span v-if="!reverse">→</span>
67
+ </a>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </article>
73
+ </li>
74
+ </template>
75
+
76
+ <style lang="scss" scoped>
77
+ .line-clamp-text {
78
+ word-break: break-all;
79
+ overflow: hidden;
80
+ text-overflow: ellipsis;
81
+ display: -webkit-box;
82
+ text-overflow: ellipsis;
83
+ -webkit-box-orient: vertical;
84
+ -webkit-line-clamp: 5;
85
+ }
86
+
87
+ .dark {
88
+ .post-image {
89
+ @apply opacity-75 hover: opacity-90 duration-200;
90
+ }
91
+ }
92
+
93
+ .post-image-content {
94
+ margin-right: 1rem;
95
+ }
96
+
97
+ .slice {
98
+ .post-image-content {
99
+ webkit-clip-path: polygon(0 0, 92% 0, 100% 100%, 0 100%);
100
+ clip-path: polygon(0 0, 92% 0, 100% 100%, 0 100%);
101
+ border-radius: 0.625rem 0 0 0.625rem;
102
+ overflow: hidden;
103
+ }
104
+ }
105
+
106
+ .reverse {
107
+ .post-image-content {
108
+ margin-right: 0;
109
+ margin-left: 1rem;
110
+ clip-path: polygon(0 0,100% 0,100% 100%,8% 100%);
111
+ border-radius: 0 0.625rem 0.625rem 0;
112
+ overflow: hidden;
113
+ }
114
+ }
115
+
116
+ .HairyArticleImage.slice {
117
+ .post-image {
118
+ @apply transition-all;
119
+ }
120
+
121
+ &:hover {
122
+ .post-image {
123
+ transform: scale(1.05) rotate(1deg);
124
+ }
125
+ }
126
+ }
127
+ </style>
@@ -0,0 +1,49 @@
1
+ <script lang="ts" setup>
2
+ import type { Post } from 'valaxy'
3
+ import { computed, defineProps } from 'vue'
4
+ import dayjs from 'dayjs'
5
+ import { useRouter } from 'vue-router'
6
+ import { useI18n } from 'vue-i18n'
7
+ import last from 'lodash/last'
8
+ import { usePostLayout } from '../hooks/usePostLayout'
9
+ import { toArr } from '../utils'
10
+
11
+ const props = defineProps<{
12
+ post: Post
13
+ reverse?: boolean
14
+ }>()
15
+ const router = useRouter()
16
+ const layout = usePostLayout()
17
+ const slice = computed(() => layout.value.includes('slice'))
18
+ const image = computed(() => props.post.image)
19
+
20
+ const i18n = useI18n()
21
+
22
+ const onReadMore = () => {
23
+ if (props.post.path)
24
+ router.push(props.post.path)
25
+ }
26
+
27
+ const displayCategory = (keys: string | string[] = []) => {
28
+ router.push({ path: `/categories/${toArr(keys).join('/')}` })
29
+ }
30
+ </script>
31
+
32
+ <template>
33
+ <li class="HairyArticleImage my-10 py-2" :class="[slice && 'slice', reverse && 'reverse']">
34
+ <article>
35
+ <div class="flex justify-between items-center">
36
+ <a class="text-size-2xl font-bold truncate cursor-pointer" :class="[reverse ? 'order-first' : 'order-last']" @click="onReadMore">{{ post.title }}</a>
37
+ <div class="flex justify-end gap-2 text-size-sm">
38
+ <span>{{ dayjs(post.date).format('YYYY-MM-DD') }}</span>
39
+ <span>{{ (post.length / 1000).toFixed(1) }}k字</span>
40
+ <span>{{ post.durations.minutes.toFixed(2) }}分钟</span>
41
+ </div>
42
+ </div>
43
+ </article>
44
+ </li>
45
+ </template>
46
+
47
+ <style lang="scss">
48
+
49
+ </style>
@@ -0,0 +1,73 @@
1
+ <script lang="ts" setup>
2
+ import { useFrontmatter } from 'valaxy'
3
+ import { computed } from 'vue'
4
+ import { toArr } from '../utils'
5
+ import { useCurrentCategory } from '../hooks/useCategory'
6
+
7
+ const frontmatter = useFrontmatter()
8
+ const paths = computed(() => toArr(frontmatter.value.categories).filter(Boolean) as string[])
9
+ const category = useCurrentCategory(paths)
10
+ const posts = computed(() => category.value.posts || [])
11
+
12
+ function isCurrent(title = '') {
13
+ return frontmatter.value.title === title
14
+ }
15
+ </script>
16
+
17
+ <template>
18
+ <div class="pl-16px text-14px relative overflow-hidden">
19
+ <div class="outline-title">
20
+ On this Series
21
+ </div>
22
+ <ul class="va-toc relative z-1">
23
+ <a v-for="(item, index) of posts" :key="index" class="va-toc-item" @click="$router.push(item.path || '')">
24
+ <a class="outline-link" :class="[isCurrent(item.title) && 'active']">{{ index + 1 }}.{{ item.title }}</a>
25
+ </a>
26
+ </ul>
27
+ </div>
28
+ </template>
29
+
30
+ <style lang="scss" scoped>
31
+ .outline-title {
32
+ letter-spacing: 0.4px;
33
+ line-height: 28px;
34
+ font-size: 14px;
35
+ font-weight: 600;
36
+ }
37
+
38
+ .outline-link {
39
+ display: block;
40
+ position: relative;
41
+ line-height: 28px;
42
+ color: var(--va-c-text-light);
43
+ white-space: nowrap;
44
+ text-overflow: ellipsis;
45
+ transition: color 0.5s;
46
+ cursor: pointer;
47
+
48
+ &:hover {
49
+ color: var(--va-c-brand);
50
+ transition: color 0.25s;
51
+ }
52
+
53
+ &.active {
54
+ color: var(--va-c-brand);
55
+ transition: color .25s;
56
+
57
+ &::after {
58
+ position: absolute;
59
+ content: '';
60
+ left: -1.12rem;
61
+ top: 0;
62
+ bottom: 0;
63
+ margin: auto;
64
+ width: 4px;
65
+ height: 18px;
66
+ background-color: var(--va-c-brand);
67
+ transition: top 0.25s cubic-bezier(0, 1, 0.5, 1), background-color 0.5s, opacity 0.25s;
68
+ border-top-right-radius: 2px;
69
+ border-bottom-right-radius: 2px;
70
+ }
71
+ }
72
+ }
73
+ </style>
@@ -0,0 +1,38 @@
1
+ <script lang="ts" setup>
2
+ import { useThemeConfig } from 'valaxy'
3
+ import type { Post } from 'valaxy'
4
+ import { computed, defineProps } from 'vue'
5
+ import type { HairyTheme } from '..'
6
+ const props = defineProps<{
7
+ post: Post
8
+ }>()
9
+ const themeConfig = useThemeConfig<HairyTheme>()
10
+ const layout = computed(() => themeConfig.value.post?.layout || 'text')
11
+ const text = computed(() => {
12
+ if (layout.value === 'text')
13
+ return props.post.text
14
+ return props.post.excerpt
15
+ })
16
+ </script>
17
+
18
+ <template>
19
+ <li class="py-12">
20
+ <article class="space-y-2 xl:grid xl:grid-cols-4 xl:space-y-0 xl:items-baseline">
21
+ <div class="space-y-5 xl:col-span-4">
22
+ <div class="space-y-6">
23
+ <h2 class="text-2xl leading-8 font-bold tracking-tight">
24
+ <a class="st-text" :href="post.path">{{ post.title }}</a>
25
+ </h2>
26
+ <div
27
+ v-if="text"
28
+ class="prose max-w-none text-gray-500"
29
+ v-html="text"
30
+ />
31
+ </div>
32
+ <div class="text-base leading-6 font-medium">
33
+ <a class="link" aria-label="read more" :href="post.path">Read more →</a>
34
+ </div>
35
+ </div>
36
+ </article>
37
+ </li>
38
+ </template>
File without changes
@@ -0,0 +1,72 @@
1
+ <script lang="ts" setup>
2
+ import { computed } from 'vue'
3
+ import { useBackToTop } from 'valaxy'
4
+
5
+ const { show, percentage } = useBackToTop({ offset: 100 })
6
+
7
+ const radius = 48
8
+ const circumference = 2 * radius * Math.PI
9
+
10
+ const strokeOffset = computed(() => {
11
+ // 周长
12
+ const val = (1 - percentage.value) * circumference
13
+ return val < 0 ? 0 : val
14
+ })
15
+
16
+ function toTop() {
17
+ window.scrollTo({ top: 0, behavior: 'smooth' })
18
+ }
19
+ </script>
20
+
21
+ <template>
22
+ <a class="back-to-top cursor-pointer" :class="show && 'show'" @click="toTop">
23
+ <div w="8" h="8" i-ri-arrow-up-s-line />
24
+ <svg class="progress-circle-container" viewBox="0 0 100 100">
25
+ <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
+ </svg>
27
+ </a>
28
+ </template>
29
+
30
+ <style lang="scss">
31
+ @use "sass:map";
32
+
33
+ .back-to-top {
34
+ position: fixed;
35
+ width: 32px;
36
+ height: 32px;
37
+ right: 0rem;
38
+ bottom: 1.5rem;
39
+ z-index: 10;
40
+ opacity: 0;
41
+ pointer-events: none;
42
+
43
+ color: var(--hy-c-primary);
44
+ transform: translateX(0) rotate(270deg);
45
+ // override yun-icon-btn transition
46
+ transition: transform var(--va-transition-duration), opacity var(--va-transition-duration-fast) !important;
47
+
48
+ &.show {
49
+ transform: translateX(-32px) rotate(360deg);
50
+ opacity: 1;
51
+ pointer-events: fill;
52
+ }
53
+
54
+ .icon {
55
+ width: 2.5rem;
56
+ height: 2.5rem;
57
+ }
58
+ > div {
59
+ margin-bottom: -2em;
60
+ }
61
+ }
62
+
63
+ .progress-circle {
64
+ transition: 0.3s stroke-dashoffset;
65
+ transform: rotate(-90deg);
66
+ transform-origin: 50% 50%;
67
+
68
+ &-container {
69
+ position: absolute;
70
+ }
71
+ }
72
+ </style>
@@ -0,0 +1,55 @@
1
+ <script lang="ts" setup>
2
+ import { useFrontmatter } from 'valaxy'
3
+ import { computed } from 'vue'
4
+ import { useRoute } from 'vue-router'
5
+ const fr = useFrontmatter()
6
+ const route = useRoute()
7
+
8
+ const showWaline = computed(() => route.path.includes('/posts/') || fr.value.waline)
9
+ </script>
10
+
11
+ <template>
12
+ <div class="HairyBody min-h-50vh relative">
13
+ <div class="mx-auto breakpoint flex z-1 relative">
14
+ <div class="relative flex-1 pt-2">
15
+ <slot />
16
+ <HairyWaline v-if="showWaline" />
17
+ </div>
18
+ <div class="ml-4 w-60 lg:block hidden">
19
+ <div class="sticky top-3.125rem z-1">
20
+ <slot v-if="$slots.slide" name="slide" />
21
+ <HairyUserCard v-else />
22
+ </div>
23
+ </div>
24
+ </div>
25
+ <div class="HairyBodyBackground" />
26
+ </div>
27
+ </template>
28
+
29
+ <style lang="scss">
30
+ .a {
31
+ overflow: hidden;
32
+ }
33
+
34
+ .HairyBodyBackground {
35
+ @apply transition-all duration-200;
36
+ @apply absolute top-0 max-h-150vh top-5 bottom-0 w-full transition-opacity;
37
+ opacity: 0;
38
+ }
39
+
40
+ .dark {
41
+ .HairyBodyBackground {
42
+ transition-delay: 200ms;
43
+ transition-delay: 0;
44
+ opacity: 1;
45
+ background-image:
46
+ linear-gradient(to bottom, var(--hy-c-waves-dimm) 0%, transparent 60%, var(--hy-c-waves-dimm) 100%),
47
+ url(https://tva2.sinaimg.cn/large/008ugSUaly8h4mt4lbuc0j31hc0u040c.jpg);
48
+ background-position: center;
49
+ opacity: 0.4;
50
+ background-repeat: no-repeat;
51
+ filter: blur(0px);
52
+ background-size: cover;
53
+ }
54
+ }
55
+ </style>
@@ -0,0 +1,51 @@
1
+ <script lang="ts" setup>
2
+ import { ElBreadcrumb, breadcrumbProps } from 'element-plus/es/components/breadcrumb/index'
3
+ import 'element-plus/es/components/breadcrumb/style/index'
4
+ import type { PropType } from 'vue'
5
+ import { computed } from 'vue'
6
+
7
+ const props = defineProps({
8
+ ...breadcrumbProps,
9
+ size: String as PropType<'default' | 'large'>,
10
+ after: String,
11
+ })
12
+ const text = computed(() => `"${props.after || ''}"`)
13
+ </script>
14
+
15
+ <template>
16
+ <ElBreadcrumb :class="[size]" :style="{ '--after-text': text }" v-bind="$props">
17
+ <slot />
18
+ </ElBreadcrumb>
19
+ </template>
20
+
21
+ <style lang="scss">
22
+ .el-breadcrumb {
23
+ overflow: hidden;
24
+ display: inline-block;
25
+ position: relative;
26
+ }
27
+ .el-breadcrumb .el-breadcrumb__inner,
28
+ .el-breadcrumb .el-breadcrumb__item:last-child .el-breadcrumb__inner {
29
+ color: initial;
30
+ }
31
+ .el-breadcrumb .el-breadcrumb__inner.is-link {
32
+ @apply border-b border-dashed hover:border-primary hover:text-primary transition-all cursor-pointer;
33
+ font-weight: normal;
34
+ }
35
+
36
+ .el-breadcrumb::after {
37
+ @apply text-gray-5 text-size-5;
38
+ content: var(--after-text);
39
+ clear: none;
40
+ transform: translateX(2px);
41
+ }
42
+ .el-breadcrumb.large {
43
+ margin-top: 3rem;
44
+ font-size: 2.5em;
45
+ @apply lt-md:text-3xl lt-sm:text-xl;
46
+ .el-breadcrumb__separator {
47
+ @apply text-size-5 text-gray-5;
48
+ font-weight: normal;
49
+ }
50
+ }
51
+ </style>
@@ -0,0 +1,14 @@
1
+ <script lang="ts" setup>
2
+ import { ElBreadcrumbItem, breadcrumbItemProps } from 'element-plus/es/components/breadcrumb/index'
3
+ defineProps(breadcrumbItemProps)
4
+ </script>
5
+
6
+ <template>
7
+ <ElBreadcrumbItem v-bind="$props">
8
+ <slot />
9
+ </ElBreadcrumbItem>
10
+ </template>
11
+
12
+ <style lang="scss" scoped>
13
+
14
+ </style>