valaxy-theme-hairy 0.2.2 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- package/client/index.ts +1 -0
- package/components/HairyBody.vue +15 -17
- package/components/HairyComment.vue +33 -0
- package/components/HairyContainer.vue +17 -0
- package/components/{HairyUserPopup.vue → HairyDrawer.vue} +16 -16
- package/components/HairyFooter.vue +12 -9
- package/components/HairyHeader.vue +10 -9
- package/components/HairyImage.vue +3 -2
- package/components/HairyImageGroup.vue +12 -16
- package/components/HairyNavbar.vue +56 -0
- package/components/HairyPageArchives.vue +59 -0
- package/components/HairyPageTags.vue +48 -0
- package/components/{HairyPostList.vue → HairyPosts.vue} +3 -3
- package/components/{HairyNavSearch.vue → HairySearch.vue} +23 -87
- package/components/{HairyUserCard.vue → HairySidebar.vue} +4 -4
- package/components/HairyTabbar.vue +56 -0
- package/components/PageTags.vue +48 -0
- package/components/ValaxyMain.vue +3 -3
- package/components/navbar/HairyNav.vue +16 -0
- package/components/navbar/HairyNavExpand.vue +12 -0
- package/components/navbar/HairyNavItem.vue +35 -0
- package/components/navbar/HairyNavbarBackground.vue +7 -0
- package/components/navbar/HairyNavbarSearch.vue +8 -0
- package/components/{HairyNavTitle.vue → navbar/HairyNavbarTitle.vue} +5 -3
- package/components/navbar/HairyNavbarToggleDark.vue +22 -0
- package/components/{HairyBreadcrumb.vue → parts/HairyBreadcrumb.vue} +2 -2
- package/components/{HairyBreadcrumbItem.vue → parts/HairyBreadcrumbItem.vue} +1 -4
- package/components/{lib/fish.js → parts/HairyFootFish.js} +5 -7
- package/components/parts/HairyFootFish.vue +38 -0
- package/components/{HairyPostTitle.vue → parts/HairyHeadHero.vue} +6 -5
- package/components/{HairyWaves.vue → parts/HairyHeadWaves.vue} +5 -5
- package/components/parts/HairyImageGlobal.vue +51 -0
- package/components/{HairyImageViewer.vue → parts/HairyImageViewer.vue} +5 -4
- package/components/parts/HairyLink.vue +21 -0
- package/components/{HairyMenu.vue → parts/HairyMenu.vue} +2 -1
- package/components/{HairyMenuItem.vue → parts/HairyMenuItem.vue} +11 -4
- package/components/parts/HairyOutline.vue +99 -0
- package/components/parts/HairyOutlineItem.vue +48 -0
- package/components/{HairySocialLinks.vue → parts/HairySocialLinks.vue} +2 -2
- package/components/{HairyTimelinePostItem.vue → parts/HairyTimelineContent.vue} +7 -8
- package/components/parts/HairyUserNav.vue +95 -0
- package/components/{article-layout → posts}/HairyArticleImage.vue +18 -19
- package/components/{article-layout → posts}/HairyArticleSeries.vue +8 -5
- package/components/{article-layout → posts}/HairyArticleText.vue +11 -4
- package/components/posts/HairyPostFooter.vue +15 -0
- package/components/{article-layout → posts}/HairyPostImageList.vue +4 -5
- package/components/{article-layout → posts}/HairyPostTextsList.vue +0 -1
- package/components/posts/HairyPostToggleLayout.vue +36 -0
- package/components/third/HairyAlgoliaSearch.vue +17 -0
- package/components/third/HairyFuseSearch.vue +10 -0
- package/components/third/HairyFuseSearchDialog.vue +32 -0
- package/components/third/HairyFuseSearchDropdown.vue +77 -0
- package/components/third/HairyFuseSearchFooter.vue +28 -0
- package/components/third/HairyFuseSearchHeader.vue +30 -0
- package/components/third/HairyFuseSearchHit.vue +52 -0
- package/components/third/HairySearchBtnDisplay.vue +29 -0
- package/components/third/HairySearchBtnInput.vue +20 -0
- package/components/third/HairySearchBtnKeys.vue +19 -0
- package/components/{HairyCarousel.vue → third/HairySwiperCarousel.vue} +6 -6
- package/{hooks/useYearArchives.ts → composables/archives.ts} +4 -3
- package/composables/category.ts +43 -0
- package/composables/config.ts +11 -0
- package/composables/dark.ts +13 -0
- package/composables/fuse.ts +60 -0
- package/composables/index.ts +8 -0
- package/composables/layout.ts +16 -0
- package/composables/outline.ts +49 -0
- package/composables/tags.ts +36 -0
- package/layouts/archive-month.vue +13 -0
- package/layouts/archive-year.vue +13 -0
- package/layouts/archives.vue +9 -9
- package/layouts/categories.vue +11 -4
- package/layouts/default.vue +9 -3
- package/layouts/home.vue +28 -18
- package/layouts/post.vue +42 -36
- package/layouts/tag.vue +8 -4
- package/layouts/tags.vue +12 -4
- package/{modules → library}/loading.ts +18 -6
- package/{modules → library}/scroll.ts +3 -2
- package/locales/zh-CN.yml +0 -2
- package/node/images/default.json +139 -0
- package/node/images/index.ts +46 -0
- package/node/index.ts +2 -0
- package/node/theme/index.ts +78 -0
- package/package.json +22 -28
- package/pages/archives/[year]/[month]/index.vue +15 -18
- package/pages/archives/[year]/index.vue +20 -20
- package/pages/archives/index.vue +1 -54
- package/pages/categories/{[...categories].vue → [...its].vue} +29 -36
- package/pages/index.vue +1 -1
- package/pages/page/[page].vue +2 -2
- package/pages/tags/[tag]/index.vue +38 -0
- package/pages/tags/index.vue +1 -36
- package/setup/main.ts +1 -1
- package/store/index.ts +1 -0
- package/store/modules/global.ts +12 -0
- package/styles/components/index.scss +4 -0
- package/styles/{markdown.scss → components/markdown.scss} +2 -1
- package/styles/components/nprogress.scss +16 -0
- package/styles/css-vars.scss +11 -0
- package/styles/element-plus/tabs.scss +1 -1
- package/styles/element-plus/timeline.scss +1 -1
- package/styles/global.scss +39 -0
- package/styles/index.scss +4 -73
- package/tsconfig.json +27 -0
- package/types/index.d.ts +163 -0
- package/unocss.config.ts +5 -1
- package/utils/index.ts +21 -39
- package/valaxy.config.ts +21 -24
- package/@types/markdown-it.d.ts +0 -1
- package/@types/markdown-toc.d.ts +0 -1
- package/@types/types.d.ts +0 -1
- package/@types/valaxy.d.ts +0 -10
- package/components/HairyAlgoliaSearchBox.vue +0 -118
- package/components/HairyBackToTop.vue +0 -72
- package/components/HairyDivider.vue +0 -0
- package/components/HairyFooterFish.vue +0 -29
- package/components/HairyLayout.vue +0 -28
- package/components/HairyLink.vue +0 -10
- package/components/HairyLinks.vue +0 -69
- package/components/HairyMeting.vue +0 -19
- package/components/HairyNav.vue +0 -42
- package/components/HairyNavBackground.vue +0 -7
- package/components/HairyNavMenu.vue +0 -11
- package/components/HairyNavToggleDark.vue +0 -16
- package/components/HairyPostToggleLayout.vue +0 -33
- package/components/HairyToc.vue +0 -135
- package/components/HairyUserNav.vue +0 -64
- package/components/HairyUserTab.vue +0 -40
- package/components/HairyWaline.vue +0 -44
- package/hooks/setupDefaultDark.ts +0 -11
- package/hooks/useCategory.ts +0 -18
- package/hooks/useCategoryPost.ts +0 -21
- package/hooks/useContext.ts +0 -13
- package/hooks/useHeaderHeight.ts +0 -9
- package/hooks/usePostLayout.ts +0 -16
- package/images.json +0 -140
- package/index.d.ts +0 -100
- package/layouts/hairy.vue +0 -36
- package/layouts/month.vue +0 -6
- package/layouts/year.vue +0 -6
- package/node/addon-hairy.ts +0 -36
- package/node/addon-images.ts +0 -61
- package/node/addon-meting.ts +0 -13
- package/node/addon-statistics.ts +0 -19
- package/node/addon-toc.ts +0 -20
- package/node/utils.ts +0 -20
- package/pages/tags/[tag].vue +0 -40
- package/utils/createContext.ts +0 -40
- package/utils/fonts.ts +0 -15
- /package/components/{HairyUserStats.vue → parts/HairyUserStats.vue} +0 -0
- /package/components/{article-layout → posts}/HairyArticleTop.vue +0 -0
- /package/{modules → library}/loading.scss +0 -0
- /package/{shims.d.ts → node/images/shims.d.ts} +0 -0
- /package/styles/{aplayer.scss → components/aplayer.scss} +0 -0
- /package/styles/{scrollbar.scss → components/scrollbar.scss} +0 -0
@@ -1,89 +1,18 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
import '
|
3
|
-
import { computed
|
4
|
-
import { useConfig } from 'valaxy'
|
2
|
+
import { useSiteConfig } from 'valaxy'
|
3
|
+
import { computed } from 'vue'
|
5
4
|
|
6
|
-
const
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
const config = useConfig()
|
11
|
-
const search = computed(() => config.value.search)
|
12
|
-
const enable = computed(() => search.value.algolia.enable)
|
13
|
-
|
14
|
-
// to avoid loading the docsearch js upfront (which is more than 1/3 of the
|
15
|
-
// payload), we delay initializing it until the user has actually clicked or
|
16
|
-
// hit the hotkey to invoke it.
|
17
|
-
const loaded = ref(false)
|
18
|
-
|
19
|
-
const metaKey = ref()
|
20
|
-
|
21
|
-
onMounted(() => {
|
22
|
-
if (!search.value.enable || !search.value.algolia.enable)
|
23
|
-
return
|
24
|
-
|
25
|
-
// meta key detect (same logic as in @docsearch/js)
|
26
|
-
metaKey.value.textContent = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)
|
27
|
-
? '⌘'
|
28
|
-
: 'Ctrl'
|
29
|
-
|
30
|
-
const handleSearchHotKey = (e: KeyboardEvent) => {
|
31
|
-
if (e.key === 'k' && (e.ctrlKey || e.metaKey)) {
|
32
|
-
e.preventDefault()
|
33
|
-
load()
|
34
|
-
remove()
|
35
|
-
}
|
36
|
-
}
|
37
|
-
function remove() {
|
38
|
-
window.removeEventListener('keydown', handleSearchHotKey)
|
39
|
-
}
|
40
|
-
|
41
|
-
window.addEventListener('keydown', handleSearchHotKey)
|
42
|
-
onUnmounted(remove)
|
43
|
-
})
|
44
|
-
function load() {
|
45
|
-
if (!loaded.value)
|
46
|
-
loaded.value = true
|
47
|
-
}
|
5
|
+
const siteConfig = useSiteConfig()
|
6
|
+
const isAlgolia = computed(() => siteConfig.value.search.type === 'algolia')
|
7
|
+
const isFuse = computed(() => siteConfig.value.search.type === 'fuse')
|
48
8
|
</script>
|
49
9
|
|
50
10
|
<template>
|
51
|
-
<
|
52
|
-
|
53
|
-
<div v-else id="docsearch" @click="load">
|
54
|
-
<button
|
55
|
-
type="button"
|
56
|
-
class="DocSearch DocSearch-Button"
|
57
|
-
aria-label="Search"
|
58
|
-
>
|
59
|
-
<span class="DocSearch-Button-Container lt-sm:text-size-xl">
|
60
|
-
<svg
|
61
|
-
class="DocSearch-Search-Icon"
|
62
|
-
width="20"
|
63
|
-
height="20"
|
64
|
-
viewBox="0 0 20 20"
|
65
|
-
>
|
66
|
-
<path
|
67
|
-
d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z"
|
68
|
-
stroke="currentColor"
|
69
|
-
fill="none"
|
70
|
-
fill-rule="evenodd"
|
71
|
-
stroke-linecap="round"
|
72
|
-
stroke-linejoin="round"
|
73
|
-
/>
|
74
|
-
</svg>
|
75
|
-
<span class="DocSearch-Button-Placeholder">Search</span>
|
76
|
-
</span>
|
77
|
-
<span class="DocSearch-Button-Keys">
|
78
|
-
<kbd ref="metaKey" class="DocSearch-Button-Key">Meta</kbd>
|
79
|
-
<kbd class="DocSearch-Button-Key">K</kbd>
|
80
|
-
</span>
|
81
|
-
</button>
|
82
|
-
</div>
|
83
|
-
</div>
|
11
|
+
<HairyAlgoliaSearch v-if="isAlgolia" />
|
12
|
+
<HairyFuseSearch v-if="isFuse" />
|
84
13
|
</template>
|
85
14
|
|
86
|
-
<style>
|
15
|
+
<style lang="scss">
|
87
16
|
.VPNavBarSearch {
|
88
17
|
display: flex;
|
89
18
|
align-items: center;
|
@@ -133,7 +62,7 @@ function load() {
|
|
133
62
|
width: 32px;
|
134
63
|
height: 55px;
|
135
64
|
background: transparent;
|
136
|
-
transition:
|
65
|
+
transition: all 0.25s;
|
137
66
|
}
|
138
67
|
|
139
68
|
.DocSearch-Button:hover {
|
@@ -146,7 +75,7 @@ function load() {
|
|
146
75
|
}
|
147
76
|
|
148
77
|
.DocSearch-Button:focus:not(:focus-visible) {
|
149
|
-
outline: none
|
78
|
+
outline: none;
|
150
79
|
}
|
151
80
|
|
152
81
|
@media (min-width: 768px) {
|
@@ -161,8 +90,15 @@ function load() {
|
|
161
90
|
}
|
162
91
|
|
163
92
|
.DocSearch-Button:hover {
|
93
|
+
background-color: rgba(255, 255, 255, 0.4);
|
164
94
|
border-color: var(--hy-c-brand);
|
165
|
-
|
95
|
+
}
|
96
|
+
|
97
|
+
.dark {
|
98
|
+
.DocSearch-Button:hover {
|
99
|
+
background-color: transparent;
|
100
|
+
border-color: var(--hy-c-brand);
|
101
|
+
}
|
166
102
|
}
|
167
103
|
}
|
168
104
|
|
@@ -242,7 +178,7 @@ function load() {
|
|
242
178
|
transition: color 0.5s, border-color 0.5s;
|
243
179
|
}
|
244
180
|
|
245
|
-
.DocSearch-Button .DocSearch-Button-Key
|
181
|
+
.DocSearch-Button .DocSearch-Button-Key+.DocSearch-Button-Key {
|
246
182
|
border-right: 1px solid var(--hy-c-divider);
|
247
183
|
border-left: none;
|
248
184
|
border-radius: 0 4px 4px 0;
|
@@ -250,10 +186,6 @@ function load() {
|
|
250
186
|
padding-right: 6px;
|
251
187
|
}
|
252
188
|
|
253
|
-
.dark .DocSearch-Footer {
|
254
|
-
border-top: 1px solid var(--hy-c-divider);
|
255
|
-
}
|
256
|
-
|
257
189
|
.DocSearch-Form {
|
258
190
|
border: 1px solid var(--hy-c-brand);
|
259
191
|
background-color: var(--hy-c-white);
|
@@ -262,4 +194,8 @@ function load() {
|
|
262
194
|
.dark .DocSearch-Form {
|
263
195
|
background-color: var(--hy-c-bg-mute);
|
264
196
|
}
|
197
|
+
|
198
|
+
.dark .DocSearch-Footer {
|
199
|
+
border-top: 1px solid var(--hy-c-divider);
|
200
|
+
}
|
265
201
|
</style>
|
@@ -1,10 +1,10 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
import {
|
2
|
+
import { useSiteConfig, useThemeConfig } from 'valaxy'
|
3
3
|
import { computed } from 'vue'
|
4
4
|
import type { HairyTheme } from 'valaxy-theme-hairy'
|
5
5
|
|
6
|
-
const config =
|
7
|
-
const theme = useThemeConfig<HairyTheme>()
|
6
|
+
const config = useSiteConfig()
|
7
|
+
const theme = useThemeConfig<HairyTheme.Config>()
|
8
8
|
|
9
9
|
const name = computed(() => theme.value.user?.name || config.value.author.name)
|
10
10
|
const description = computed(() => theme.value.user?.description || config.value.description)
|
@@ -13,7 +13,7 @@ const description = computed(() => theme.value.user?.description || config.value
|
|
13
13
|
<template>
|
14
14
|
<div class="pt-5 animate__animated animate__fadeIn relative z-1">
|
15
15
|
<div class="flex flex-col items-center">
|
16
|
-
<img class="mx-auto w-40 rounded-full -mx-1px" :src="config.author.avatar"
|
16
|
+
<img class="mx-auto w-40 rounded-full -mx-1px" :src="config.author.avatar">
|
17
17
|
<div class="leading-loose mt-2">
|
18
18
|
{{ name }}
|
19
19
|
</div>
|
@@ -0,0 +1,56 @@
|
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import { ElTabPane, ElTabs } from 'element-plus/es/components/tabs/index'
|
3
|
+
import 'element-plus/theme-chalk/el-tabs.css'
|
4
|
+
import 'element-plus/theme-chalk/el-tab-pane.css'
|
5
|
+
import { computed, provide, ref } from 'vue'
|
6
|
+
|
7
|
+
import type { DefaultTheme } from 'valaxy/types'
|
8
|
+
import { useFrontmatter, useThemeConfig } from 'valaxy'
|
9
|
+
|
10
|
+
const active = ref('aside')
|
11
|
+
|
12
|
+
const frontmatter = useFrontmatter()
|
13
|
+
const themeConfig = useThemeConfig()
|
14
|
+
|
15
|
+
const pageOutline = computed<DefaultTheme.Config['outline']>(
|
16
|
+
() => frontmatter.value.outline ?? themeConfig.value.outline,
|
17
|
+
)
|
18
|
+
|
19
|
+
const pageOutlineNotUndefined = computed(() =>
|
20
|
+
typeof pageOutline.value !== 'undefined',
|
21
|
+
)
|
22
|
+
|
23
|
+
provide('HairyUserTab:active', active)
|
24
|
+
</script>
|
25
|
+
|
26
|
+
<template>
|
27
|
+
<ElTabs v-model="active" class="pt-3">
|
28
|
+
<ElTabPane v-if="pageOutlineNotUndefined" name="aside">
|
29
|
+
<template #label>
|
30
|
+
<div class="flex items-center">
|
31
|
+
<div class="i-ri-list-check-2" />
|
32
|
+
<span class="ml-1">Aside</span>
|
33
|
+
</div>
|
34
|
+
</template>
|
35
|
+
<HairyOutline />
|
36
|
+
</ElTabPane>
|
37
|
+
<ElTabPane label="Series" name="series">
|
38
|
+
<template #label>
|
39
|
+
<div class="flex items-center gap-1">
|
40
|
+
<div class="i-ri-flow-chart" />
|
41
|
+
<span class="ml-1">Series</span>
|
42
|
+
</div>
|
43
|
+
</template>
|
44
|
+
<HairyArticleSeries />
|
45
|
+
</ElTabPane>
|
46
|
+
<ElTabPane label="User" name="user">
|
47
|
+
<template #label>
|
48
|
+
<div class="flex items-center gap-1">
|
49
|
+
<div class="i-ri-user-line" />
|
50
|
+
<span class="ml-1">User</span>
|
51
|
+
</div>
|
52
|
+
</template>
|
53
|
+
<HairySidebar />
|
54
|
+
</ElTabPane>
|
55
|
+
</ElTabs>
|
56
|
+
</template>
|
@@ -0,0 +1,48 @@
|
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import { useRouter } from 'vue-router'
|
3
|
+
import { useHairyTags } from '../../composables'
|
4
|
+
|
5
|
+
const router = useRouter()
|
6
|
+
|
7
|
+
const { getTagStyle, tags } = useHairyTags({
|
8
|
+
primary: '#1bc9a6',
|
9
|
+
})
|
10
|
+
|
11
|
+
function displayTag(tag: string) {
|
12
|
+
router.push(`/tags/${tag}`)
|
13
|
+
}
|
14
|
+
</script>
|
15
|
+
|
16
|
+
<template>
|
17
|
+
<div class="min-h-59vh flex-center flex-col">
|
18
|
+
<div text="center" class="text-size-2.5em pt-10 mb-5">
|
19
|
+
目前共计 {{ Array.from(tags).length }} 个标签
|
20
|
+
</div>
|
21
|
+
<div text="center" class="max-w-7xl flex flex-wrap justify-center items-center gap-2">
|
22
|
+
<a
|
23
|
+
v-for="[key, tag] in Array.from(tags).sort()"
|
24
|
+
:key="key" class="post-tag cursor-pointer"
|
25
|
+
:style="getTagStyle(tag.count)"
|
26
|
+
p="1"
|
27
|
+
@click="displayTag(key.toString())"
|
28
|
+
>
|
29
|
+
{{ key }}
|
30
|
+
</a>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
</template>
|
34
|
+
|
35
|
+
<route lang="yaml">
|
36
|
+
meta:
|
37
|
+
layout: tags
|
38
|
+
</route>
|
39
|
+
|
40
|
+
<style lang="scss" scoped>
|
41
|
+
a {
|
42
|
+
color: var(--yun-tag-color);
|
43
|
+
&:hover {
|
44
|
+
--un-text-opacity: 1;
|
45
|
+
color: var(--hy-c-primary-dark);
|
46
|
+
}
|
47
|
+
}
|
48
|
+
</style>
|
@@ -1,12 +1,12 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import type { PageData, Post } from 'valaxy'
|
3
|
-
import {
|
3
|
+
import { useSiteConfig } from 'valaxy'
|
4
4
|
|
5
5
|
defineProps<{
|
6
6
|
frontmatter: Post
|
7
7
|
data?: PageData
|
8
8
|
}>()
|
9
|
-
const
|
9
|
+
const siteConfig = useSiteConfig()
|
10
10
|
</script>
|
11
11
|
|
12
12
|
<template>
|
@@ -34,7 +34,7 @@ const config = useConfig()
|
|
34
34
|
|
35
35
|
<slot name="main-nav-after" />
|
36
36
|
|
37
|
-
<slot v-if="
|
37
|
+
<slot v-if="siteConfig.comment.enable && frontmatter.comment !== false" name="comment" />
|
38
38
|
|
39
39
|
<slot name="footer" />
|
40
40
|
</slot>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import type { HairyTheme } from 'valaxy-theme-hairy'
|
3
|
+
import { useThemeConfig } from 'valaxy'
|
4
|
+
import { computed } from 'vue'
|
5
|
+
|
6
|
+
const themeConfig = useThemeConfig<HairyTheme.Config>()
|
7
|
+
const nav = computed(() => themeConfig.value.nav || [])
|
8
|
+
</script>
|
9
|
+
|
10
|
+
<template>
|
11
|
+
<div class="flex items-center h-12.5">
|
12
|
+
<HairyNavItem v-for="(item, index) in nav" :key="index" :item="item" />
|
13
|
+
</div>
|
14
|
+
</template>
|
15
|
+
|
16
|
+
<style lang="scss" scoped></style>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import { storeToRefs } from 'pinia'
|
3
|
+
import { useGlobalStore } from '../../store'
|
4
|
+
|
5
|
+
const { showDrawer } = storeToRefs(useGlobalStore())
|
6
|
+
</script>
|
7
|
+
|
8
|
+
<template>
|
9
|
+
<button class="yun-icon-btn lt-sm:text-size-xl" @click="showDrawer = !showDrawer">
|
10
|
+
<div i="ri-menu-fill" />
|
11
|
+
</button>
|
12
|
+
</template>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import type { NavItem } from 'valaxy-theme-hairy'
|
3
|
+
import { computed } from 'vue'
|
4
|
+
import { useRoute } from 'vue-router'
|
5
|
+
|
6
|
+
const props = defineProps<{
|
7
|
+
item: NavItem
|
8
|
+
}>()
|
9
|
+
const urlReg = /^(((ht|f)tps?):\/\/)?([^!@#$%^&*?.\s-]([^!@#$%^&*?.\s]{0,63}[^!@#$%^&*?.\s])?\.)+[a-z]{2,6}\/?/
|
10
|
+
const isLink = computed(() => urlReg.test(props.item?.link || ''))
|
11
|
+
const isPointer = computed(() => Boolean(props.item.link) || isLink.value)
|
12
|
+
|
13
|
+
const route = useRoute()
|
14
|
+
|
15
|
+
const active = computed(() => {
|
16
|
+
return !isLink.value && (
|
17
|
+
props.item.link === '/'
|
18
|
+
? route.path === props.item.link
|
19
|
+
: route.path.includes(props.item.link!)
|
20
|
+
)
|
21
|
+
})
|
22
|
+
</script>
|
23
|
+
|
24
|
+
<template>
|
25
|
+
<a href="" class="px-2.5 HairyMenuItem" :class="[isPointer ? 'cursor-pointer' : 'select-none', active && 'text-primary active']">
|
26
|
+
<HairyLink class="flex items-center" type="white" :href="item.link" :target="isLink ? '__blank' : ''">
|
27
|
+
<div v-if="item.icon" class="mr-1 icon" :class="item.icon" />
|
28
|
+
<div class="question">
|
29
|
+
{{ item.text }}
|
30
|
+
</div>
|
31
|
+
</HairyLink>
|
32
|
+
</a>
|
33
|
+
</template>
|
34
|
+
|
35
|
+
<style lang="scss" scoped></style>
|
@@ -1,7 +1,9 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
import {
|
3
|
-
|
4
|
-
const
|
2
|
+
import { useSiteConfig } from 'valaxy'
|
3
|
+
|
4
|
+
const config = useSiteConfig()
|
5
|
+
|
6
|
+
function hrefToUrl() {
|
5
7
|
location.href = config.value.url
|
6
8
|
}
|
7
9
|
</script>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import { useI18n } from 'vue-i18n'
|
3
|
+
import { computed } from 'vue'
|
4
|
+
import { useAppStore } from 'valaxy'
|
5
|
+
|
6
|
+
const { t } = useI18n()
|
7
|
+
const appStore = useAppStore()
|
8
|
+
const dark = computed(() => appStore.isDark)
|
9
|
+
|
10
|
+
const themeTitle = computed(() => {
|
11
|
+
return dark.value ? t('button.toggle_light') : t('button.toggle_dark')
|
12
|
+
})
|
13
|
+
</script>
|
14
|
+
|
15
|
+
<template>
|
16
|
+
<button
|
17
|
+
class="yun-icon-btn bg-light-1 p-1 dark:bg-transparent rounded-5 lt-sm:text-size-xl"
|
18
|
+
:title="themeTitle" :style="{ color: dark ? '' : '#f1cb64' }" @click="($event) => appStore.toggleDarkWithTransition($event, { duration: 600, easing: 'ease-in-out' })"
|
19
|
+
>
|
20
|
+
<div i="ri-sun-line dark:ri-moon-line" />
|
21
|
+
</button>
|
22
|
+
</template>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import { ElBreadcrumb, breadcrumbProps } from 'element-plus/es/components/breadcrumb/index'
|
3
|
-
import 'element-plus/
|
3
|
+
import 'element-plus/theme-chalk/el-breadcrumb.css'
|
4
4
|
import type { PropType } from 'vue'
|
5
5
|
import { computed } from 'vue'
|
6
6
|
|
@@ -26,7 +26,7 @@ const text = computed(() => `"${props.after || ''}"`)
|
|
26
26
|
}
|
27
27
|
.el-breadcrumb .el-breadcrumb__inner,
|
28
28
|
.el-breadcrumb .el-breadcrumb__item:last-child .el-breadcrumb__inner {
|
29
|
-
color:
|
29
|
+
color: var(--va-c-text);
|
30
30
|
}
|
31
31
|
.el-breadcrumb .el-breadcrumb__inner.is-link {
|
32
32
|
@apply border-b border-dashed hover:border-primary hover:text-primary transition-all cursor-pointer;
|
@@ -1,5 +1,6 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import { ElBreadcrumbItem, breadcrumbItemProps } from 'element-plus/es/components/breadcrumb/index'
|
3
|
+
|
3
4
|
defineProps(breadcrumbItemProps)
|
4
5
|
</script>
|
5
6
|
|
@@ -8,7 +9,3 @@ defineProps(breadcrumbItemProps)
|
|
8
9
|
<slot />
|
9
10
|
</ElBreadcrumbItem>
|
10
11
|
</template>
|
11
|
-
|
12
|
-
<style lang="scss" scoped>
|
13
|
-
|
14
|
-
</style>
|
@@ -1,8 +1,5 @@
|
|
1
|
+
/* eslint-disable ts/no-use-before-define */
|
1
2
|
/* eslint-disable eqeqeq */
|
2
|
-
/* eslint-disable no-var */
|
3
|
-
/* eslint-disable vars-on-top */
|
4
|
-
/* eslint-disable @typescript-eslint/no-use-before-define */
|
5
|
-
/* eslint-disable no-undef */
|
6
3
|
|
7
4
|
class RENDERER {
|
8
5
|
ENABLE = false
|
@@ -185,7 +182,7 @@ class RENDERER {
|
|
185
182
|
}
|
186
183
|
}
|
187
184
|
|
188
|
-
|
185
|
+
const SURFACE_POINT = function (renderer, x) {
|
189
186
|
this.renderer = renderer
|
190
187
|
this.x = x
|
191
188
|
this.init()
|
@@ -236,7 +233,7 @@ SURFACE_POINT.prototype = {
|
|
236
233
|
},
|
237
234
|
}
|
238
235
|
|
239
|
-
|
236
|
+
const FISH = function (renderer) {
|
240
237
|
this.renderer = renderer
|
241
238
|
this.init()
|
242
239
|
}
|
@@ -247,7 +244,8 @@ FISH.prototype = {
|
|
247
244
|
this.direction = Math.random() < 0.5
|
248
245
|
this.x = this.direction ? this.renderer.width + this.renderer.THRESHOLD : -this.renderer.THRESHOLD
|
249
246
|
this.previousY = this.y
|
250
|
-
this.vx = this.getRandomValue(4,
|
247
|
+
this.vx = this.getRandomValue(4, 7) * (this.direction ? -1 : 1)
|
248
|
+
|
251
249
|
if (this.renderer.reverse) {
|
252
250
|
this.y = this.getRandomValue((this.renderer.height * 1) / 10, (this.renderer.height * 4) / 10)
|
253
251
|
this.vy = this.getRandomValue(2, 5)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<!-- eslint-disable no-new -->
|
2
|
+
<script lang="ts" setup>
|
3
|
+
import { computed, onMounted, ref, watch } from 'vue'
|
4
|
+
import { useScriptTag } from '@vueuse/core'
|
5
|
+
import { useAppStore } from 'valaxy'
|
6
|
+
import { RENDERER } from './HairyFootFish'
|
7
|
+
|
8
|
+
const fishContainer = ref()
|
9
|
+
|
10
|
+
const tag = useScriptTag('https://cdn.bootcdn.net/ajax/libs/zepto/1.2.0/zepto.min.js')
|
11
|
+
const appStore = useAppStore()
|
12
|
+
const dark = computed(() => appStore.isDark)
|
13
|
+
|
14
|
+
let renderer: RENDERER
|
15
|
+
|
16
|
+
function reset() {
|
17
|
+
const color = dark.value ? 'hsl(0, 0%, 95%)' : 'hsl(0, 0%, 80%)'
|
18
|
+
if (!renderer)
|
19
|
+
renderer = new RENDERER(color)
|
20
|
+
else
|
21
|
+
renderer.setColor(color)
|
22
|
+
}
|
23
|
+
|
24
|
+
onMounted(() => {
|
25
|
+
tag.load()
|
26
|
+
.then(reset)
|
27
|
+
})
|
28
|
+
watch(dark, reset)
|
29
|
+
</script>
|
30
|
+
|
31
|
+
<template>
|
32
|
+
<div
|
33
|
+
id="jsi-flying-fish-container"
|
34
|
+
ref="fishContainer"
|
35
|
+
class="z-1 relative"
|
36
|
+
style="margin-top: -60px;"
|
37
|
+
/>
|
38
|
+
</template>
|
@@ -1,20 +1,21 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
2
|
import { useFrontmatter } from 'valaxy'
|
3
3
|
import { computed } from 'vue'
|
4
|
+
|
4
5
|
const props = defineProps<{
|
5
|
-
headline?:
|
6
|
-
title:
|
6
|
+
headline?: string
|
7
|
+
title: string
|
7
8
|
description?: string
|
8
9
|
}>()
|
9
10
|
|
10
11
|
const post = useFrontmatter()
|
11
12
|
|
12
|
-
const headline = computed(() =>
|
13
|
-
const title = computed(() =>
|
13
|
+
const headline = computed(() => props.headline || post.value.headline)
|
14
|
+
const title = computed(() => props.title || post.value.title)
|
14
15
|
</script>
|
15
16
|
|
16
17
|
<template>
|
17
|
-
<div class="flex-center flex-col text-shadow-lg text-white">
|
18
|
+
<div class="flex-center flex-col text-shadow-lg text-white mx-12px text-center">
|
18
19
|
<div v-if="headline" class="font-frederick text-size-3.35em lt-sm:text-size-3rem leading-snug">
|
19
20
|
{{ headline }}
|
20
21
|
</div>
|
@@ -3,14 +3,14 @@
|
|
3
3
|
class="waves w-full min-h-100px max-h-150px"
|
4
4
|
viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto"
|
5
5
|
>
|
6
|
-
<defs>
|
6
|
+
<defs>rgba(250,250,250,var(--un-bg-opacity))
|
7
7
|
<path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" />
|
8
8
|
</defs>
|
9
9
|
<g class="parallax">
|
10
|
-
<use class="
|
11
|
-
<use class="
|
12
|
-
<use class="
|
13
|
-
<use class="
|
10
|
+
<use class="" xlink:href="#gentle-wave" x="48" y="0" fill="var(--hy-c-waves-dimm-1)" />
|
11
|
+
<use class="" xlink:href="#gentle-wave" x="48" y="3" fill="var(--hy-c-waves-dimm-2)" />
|
12
|
+
<use class="" xlink:href="#gentle-wave" x="48" y="5" fill="var(--hy-c-waves-dimm-3)" />
|
13
|
+
<use class="" xlink:href="#gentle-wave" x="48" y="7" fill="var(--hy-c-waves-dimm)" />
|
14
14
|
</g>
|
15
15
|
</svg>
|
16
16
|
</template>
|
@@ -0,0 +1,51 @@
|
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import { provide, useCssVars } from 'vue'
|
3
|
+
import { renderOverlay } from '@overlastic/vue'
|
4
|
+
import type { ImageViewerProps } from 'element-plus/es/components/image-viewer/index'
|
5
|
+
import { atWillToUnit } from '@hairy/utils'
|
6
|
+
import HairyImageViewer from './HairyImageViewer.vue'
|
7
|
+
|
8
|
+
const props = withDefaults(defineProps<{
|
9
|
+
row?: string | number
|
10
|
+
col?: string | number
|
11
|
+
gap?: string | number
|
12
|
+
justify?: string
|
13
|
+
align?: string
|
14
|
+
}>(), {
|
15
|
+
row: 'auto',
|
16
|
+
col: 'auto',
|
17
|
+
gap: 10,
|
18
|
+
justify: 'space-evenly',
|
19
|
+
align: 'initial',
|
20
|
+
})
|
21
|
+
|
22
|
+
useCssVars(() => ({
|
23
|
+
width: atWillToUnit(props.row),
|
24
|
+
height: atWillToUnit(props.col),
|
25
|
+
gap: atWillToUnit(props.gap),
|
26
|
+
justify: props.justify,
|
27
|
+
align: props.align,
|
28
|
+
}))
|
29
|
+
|
30
|
+
// TODO global find images
|
31
|
+
// const slots = useSlots()
|
32
|
+
// const paths = computed(() => slots
|
33
|
+
// .default?.()
|
34
|
+
// .map(v => v.props?.src)
|
35
|
+
// .filter(Boolean) as string[],
|
36
|
+
// )
|
37
|
+
|
38
|
+
function preview(url: string) {
|
39
|
+
// const initialIndex = paths.value.findIndex(v => v === url) || 0
|
40
|
+
renderOverlay<Partial<ImageViewerProps>>(HairyImageViewer, {
|
41
|
+
urlList: [url],
|
42
|
+
initialIndex: 0,
|
43
|
+
})
|
44
|
+
}
|
45
|
+
|
46
|
+
provide('HairyImageGroup:preview', preview)
|
47
|
+
</script>
|
48
|
+
|
49
|
+
<template>
|
50
|
+
<slot />
|
51
|
+
</template>
|
@@ -1,11 +1,12 @@
|
|
1
1
|
<script lang="ts" setup>
|
2
|
-
import {
|
2
|
+
import { usePrograms } from '@overlastic/vue'
|
3
3
|
import { ElImageViewer, imageViewerProps } from 'element-plus/es/components/image-viewer/index'
|
4
|
-
import 'element-plus/
|
4
|
+
import 'element-plus/theme-chalk/el-image-viewer.css'
|
5
5
|
import { onMounted, onUnmounted } from 'vue'
|
6
|
+
|
6
7
|
const props = defineProps(imageViewerProps)
|
7
8
|
|
8
|
-
const { visible,
|
9
|
+
const { visible, resolve } = usePrograms()
|
9
10
|
|
10
11
|
onMounted(() => {
|
11
12
|
document.body.style.overflow = 'hidden'
|
@@ -17,6 +18,6 @@ onUnmounted(() => {
|
|
17
18
|
|
18
19
|
<template>
|
19
20
|
<div class="HairyImageViewer fixed inset-0 z-2000">
|
20
|
-
<ElImageViewer v-if="visible" v-bind="props" @close="
|
21
|
+
<ElImageViewer v-if="visible" v-bind="props" @close="resolve()" />
|
21
22
|
</div>
|
22
23
|
</template>
|