valaxy-theme-hairy 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -15,6 +15,7 @@ const showWaline = computed(() => route.path.includes('/posts/') || fr.value.wal
15
15
  <slot />
16
16
  <HairyWaline v-if="showWaline" />
17
17
  </div>
18
+
18
19
  <div class="ml-4 w-60 lg:block hidden">
19
20
  <div class="sticky top-3.125rem z-1">
20
21
  <slot v-if="$slots.slide" name="slide" />
@@ -27,10 +28,6 @@ const showWaline = computed(() => route.path.includes('/posts/') || fr.value.wal
27
28
  </template>
28
29
 
29
30
  <style lang="scss">
30
- .a {
31
- overflow: hidden;
32
- }
33
-
34
31
  .HairyBodyBackground {
35
32
  @apply transition-all duration-200;
36
33
  @apply absolute top-0 max-h-150vh top-5 bottom-0 w-full transition-opacity;
@@ -43,8 +40,7 @@ const showWaline = computed(() => route.path.includes('/posts/') || fr.value.wal
43
40
  transition-delay: 0;
44
41
  opacity: 1;
45
42
  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);
43
+ linear-gradient(to bottom, var(--hy-c-waves-dimm) 0%, transparent 60%, var(--hy-c-waves-dimm) 100%), url(./images/bg.jpg);
48
44
  background-position: center;
49
45
  opacity: 0.4;
50
46
  background-repeat: no-repeat;
@@ -1,16 +1,26 @@
1
+ <!-- eslint-disable no-new -->
1
2
  <script lang="ts" setup>
2
3
  import { onMounted, ref } from 'vue'
3
4
  import { useScriptTag } from '@vueuse/core'
4
- import { RENDERER } from './fish'
5
+ import { RENDERER } from './lib/fish'
5
6
 
6
7
  const fishContainer = ref()
7
8
 
8
9
  const tag = useScriptTag('https://cdn.bootcdn.net/ajax/libs/zepto/1.2.0/zepto.min.js')
10
+
11
+ let renderer: RENDERER
12
+
13
+ function reset() {
14
+ const color = 'hsl(0, 0%, 95%)'
15
+ if (!renderer)
16
+ renderer = new RENDERER(color)
17
+ else
18
+ renderer.setColor(color)
19
+ }
20
+
9
21
  onMounted(() => {
10
22
  tag.load()
11
- .then(() => {
12
- new RENDERER().init()
13
- })
23
+ .then(reset)
14
24
  })
15
25
  </script>
16
26
 
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="HairyNavBackground transition-colors absolute inset-0 bg-white bg-opacity-80 dark:bg-black dark:bg-opacity-80 blur-5" />
2
+ <div class="HairyNavBackground transition-colors absolute inset-0 bg-white bg-opacity-85 dark:bg-black dark:bg-opacity-80 blur-5" />
3
3
  </template>
4
4
 
5
5
  <style>
@@ -10,7 +10,7 @@ const themeTitle = computed(() => {
10
10
  </script>
11
11
 
12
12
  <template>
13
- <button class="yun-icon-btn" :title="themeTitle" :style="{ color: isDark ? '' : '#f1cb64' }" @click="toggleDark()">
13
+ <button class="yun-icon-btn bg-light-1 p-1 dark:bg-transparent rounded-5" :title="themeTitle" :style="{ color: isDark ? '' : '#f1cb64' }" @click="toggleDark()">
14
14
  <div i="ri-sun-line dark:ri-moon-line" />
15
15
  </button>
16
16
  </template>
@@ -23,9 +23,12 @@ const displayedPosts = computed(() => props.pagination ? pagePosts.value : posts
23
23
  </script>
24
24
 
25
25
  <template>
26
- <HairyPostImageList v-if="layout.includes('image')" :posts="displayedPosts" />
27
- <HairyPostTextsList v-else :posts="displayedPosts" />
28
- <ValaxyPagination v-if="pagination" :cur-page="curPage" :page-size="pageSize" :total="posts.length" />
26
+ <div class="mt-8">
27
+ <HairyPostToggleLayout />
28
+ <HairyPostImageList v-if="layout.includes('image')" :posts="displayedPosts" />
29
+ <HairyPostTextsList v-else :posts="displayedPosts" />
30
+ <ValaxyPagination v-if="pagination" :cur-page="curPage" :page-size="pageSize" :total="posts.length" />
31
+ </div>
29
32
  </template>
30
33
 
31
34
  <style lang="scss">
@@ -0,0 +1,33 @@
1
+ <script lang="ts" setup>
2
+ import { useScroll } from '@vueuse/core'
3
+ import { computed } from 'vue'
4
+ import { usePostLayout } from '../hooks/usePostLayout'
5
+ import { useHeaderHeight } from '../hooks/useHeaderHeight'
6
+ import type { HairyPostLayout } from '..'
7
+
8
+ const layout = usePostLayout()
9
+ const layouts = [
10
+ { layout: 'image:slice:reverse' as HairyPostLayout, icon: 'i-fluent-text-align-distributed-24-filled' },
11
+ { layout: 'image:slice' as HairyPostLayout, icon: 'i-fluent-text-align-left-16-filled' },
12
+ { layout: 'image' as HairyPostLayout, icon: 'i-fluent-text-align-justify-20-filled' },
13
+ { layout: 'markdown' as HairyPostLayout, icon: 'i-fluent-markdown-20-filled' },
14
+ { layout: 'text' as HairyPostLayout, icon: 'i-fluent-code-text-16-filled' },
15
+ ]
16
+
17
+ const headerHeight = useHeaderHeight()
18
+ const scroll = useScroll(document)
19
+
20
+ const show = computed(() => {
21
+ return scroll.y.value > headerHeight.value
22
+ })
23
+ </script>
24
+
25
+ <template>
26
+ <div class="inline-flex gap-2 sticky top-15 inset-0 rounded-2" :class="[show && 'bg-white bg-opacity-85 dark:bg-black dark:bg-opacity-80 z-100']">
27
+ <div v-for="(item) in layouts" :key="item.layout" class="p-2 rounded-full cursor-pointer" :class="[layout === item.layout && 'text-primary']" @click="layout = item.layout">
28
+ <div class="text-size-xl" :class="item.icon" />
29
+ </div>
30
+ </div>
31
+ </template>
32
+
33
+ <style lang="scss" scoped></style>
@@ -5,8 +5,8 @@ import dayjs from 'dayjs'
5
5
  import { useRouter } from 'vue-router'
6
6
  import { useI18n } from 'vue-i18n'
7
7
  import { last } from 'lodash-es'
8
- import { usePostLayout } from '../hooks/usePostLayout'
9
- import { toArr } from '../utils'
8
+ import { usePostLayout } from '../../hooks/usePostLayout'
9
+ import { toArr } from '../../utils'
10
10
 
11
11
  const props = defineProps<{
12
12
  post: Post
@@ -30,24 +30,24 @@ const displayCategory = (keys: string | string[] = []) => {
30
30
  </script>
31
31
 
32
32
  <template>
33
- <li class="HairyArticleImage my-10 py-2" :class="[slice && 'slice', reverse && 'reverse']">
33
+ <li class="HairyArticleImage mb-10 py-2" :class="[slice && 'slice', reverse && 'reverse']">
34
34
  <article>
35
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">
36
+ <a class="text-size-2xl font-bold truncate cursor-pointer lt-sm:text-size-lg" :class="[reverse ? 'order-last' : 'order-first']" @click="onReadMore">{{ post.title }}</a>
37
+ <div class="flex justify-end gap-2 text-size-sm lt-sm:text-size-xs">
38
38
  <span>{{ dayjs(post.date).format('YYYY-MM-DD') }}</span>
39
39
  <span>{{ (post.length / 1000).toFixed(1) }}k字</span>
40
- <span>{{ post.durations.minutes.toFixed(2) }}分钟</span>
40
+ <span class="lt-sm:hidden">{{ post.durations.minutes.toFixed(2) }}分钟</span>
41
41
  </div>
42
42
  </div>
43
- <div class="h-200px flex">
43
+ <div class="h-200px lt-sm:h-150px flex bg-light-2 dark:bg-transparent rounded-5 duration-200" :class="[reverse ? 'pl-4' : 'pr-4']">
44
44
  <div class="flex-1 post-image-content" :class="[reverse ? 'order-last' : 'order-first']">
45
45
  <img
46
46
  class="post-image rounded-1 w-full h-full object-cover cursor-pointer" :src="image"
47
47
  @click="onReadMore"
48
48
  />
49
49
  </div>
50
- <div class="flex flex-col justify-between flex-1">
50
+ <div class="flex-1 flex flex-col justify-between py-2 dark:py-0">
51
51
  <div class="flex-1 text-size-sm">
52
52
  <div class="line-clamp-text">
53
53
  {{ post.text }}
@@ -2,8 +2,8 @@
2
2
  import { useFrontmatter } from 'valaxy'
3
3
  import { computed, inject, nextTick, ref } from 'vue'
4
4
  import { useRouter } from 'vue-router'
5
- import { toArr } from '../utils'
6
- import { useCurrentCategory } from '../hooks/useCategory'
5
+ import { toArr } from '../../utils'
6
+ import { useCurrentCategory } from '../../hooks/useCategory'
7
7
 
8
8
  const frontmatter = useFrontmatter()
9
9
  const paths = computed(() => toArr(frontmatter.value.categories).filter(Boolean) as string[])
@@ -1,25 +1,16 @@
1
1
  <script lang="ts" setup>
2
- import { useThemeConfig } from 'valaxy'
3
2
  import type { Post } from 'valaxy'
4
3
  import { computed, defineProps } from 'vue'
5
- import type { HairyTheme } from '..'
4
+ import { usePostLayout } from '../../hooks/usePostLayout'
6
5
  const props = defineProps<{
7
6
  post: Post
8
7
  }>()
9
- const themeConfig = useThemeConfig<HairyTheme>()
10
- const layout = computed(() => themeConfig.value.post?.layout || 'text')
8
+ const layout = usePostLayout()
11
9
  const text = computed(() => {
12
10
  if (layout.value === 'text')
13
11
  return props.post.text
14
12
  return props.post.excerpt
15
13
  })
16
-
17
- const Blogs = {
18
- name: 'Mao’s blog',
19
- desc: '记录生活、持续学习。',
20
- link: 'https://hairy.blog/',
21
- thumbnail: 'https://user-images.githubusercontent.com/49724027/182444624-6228d153-94cb-461d-a5d8-be8535441fb6.png',
22
- }
23
14
  </script>
24
15
 
25
16
  <template>
@@ -4,7 +4,7 @@ import { computed } from 'vue'
4
4
  import type { Post } from 'valaxy'
5
5
  import { usePostList } from 'valaxy'
6
6
 
7
- import { usePostLayout } from '../hooks/usePostLayout'
7
+ import { usePostLayout } from '../../hooks/usePostLayout'
8
8
 
9
9
  const props = withDefaults(defineProps<{
10
10
  type?: string
@@ -21,7 +21,7 @@ const reverse = computed(() => layout.value.includes('reverse'))
21
21
  <template>
22
22
  <ul class="divide-y divide-gray-200 dark:divide-gray-700">
23
23
  <Transition v-for="post, i in posts" :key="i" name="fade">
24
- <HairyArticleImage :post="post" :reverse="reverse && (i % 2) === 0" />
24
+ <HairyArticleImage :post="post" :reverse="reverse && !((i % 2) === 0)" />
25
25
  </Transition>
26
26
  </ul>
27
27
  </template>
Binary file
@@ -11,9 +11,15 @@ class RENDERER {
11
11
  MAX_INTERVAL_COUNT = 50
12
12
  INIT_HEIGHT_RATE = 0.5
13
13
  THRESHOLD = 50
14
- init() {
14
+
15
+ COLOR = 'hsl(0, 0%, 95%)'
16
+
17
+ constructor(color) {
15
18
  if (this.ENABLE)
16
19
  return
20
+
21
+ this.setColor(color)
22
+ this.removeCanvas()
17
23
  this.setParameters()
18
24
  this.reconstructMethods()
19
25
  this.setup()
@@ -22,6 +28,18 @@ class RENDERER {
22
28
  this.ENABLE = true
23
29
  }
24
30
 
31
+ removeCanvas() {
32
+ const container = document.querySelector('#jsi-flying-fish-container')
33
+ const canvas = container.querySelector('canvas')
34
+ if (canvas)
35
+ canvas.remove()
36
+ }
37
+
38
+ setColor(color) {
39
+ if (color)
40
+ this.COLOR = color
41
+ }
42
+
25
43
  setParameters() {
26
44
  this.$window = $(window)
27
45
  this.$container = $('#jsi-flying-fish-container')
@@ -148,7 +166,7 @@ class RENDERER {
148
166
  requestAnimationFrame(this.render)
149
167
  this.controlStatus()
150
168
  this.context.clearRect(0, 0, this.width, this.height)
151
- this.context.fillStyle = 'hsl(0, 0%, 95%)'
169
+ this.context.fillStyle = this.COLOR
152
170
  for (let i = 0, count = this.fishes.length; i < count; i++)
153
171
  this.fishes[i].render(this.context)
154
172
 
@@ -1,9 +1,16 @@
1
1
  import { useThemeConfig } from 'valaxy'
2
+ import { createSharedComposable, useStorage } from '@vueuse/core'
2
3
  import { computed } from 'vue'
3
- import type { HairyTheme } from '..'
4
+ import type { HairyPostLayout, HairyTheme } from '..'
5
+
6
+ const useSharedStorage = createSharedComposable(useStorage)
4
7
 
5
8
  export function usePostLayout() {
6
9
  const themeConfig = useThemeConfig<HairyTheme>()
7
- const layout = computed(() => themeConfig.value.post?.layout || 'text')
10
+ const cache = useSharedStorage<HairyPostLayout>('--hairy-theme:post-layout', null)
11
+ const layout = computed({
12
+ get: () => cache.value || themeConfig.value.post?.layout || 'image',
13
+ set: value => cache.value = value,
14
+ })
8
15
  return layout
9
16
  }
package/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type { ViteSSGContext } from 'vite-ssg'
2
2
 
3
+ export type HairyPostLayout = 'text' | 'markdown' | 'image' | 'image:slice' | 'image:slice:reverse'
4
+
3
5
  export interface HairyTheme {
4
6
  nav?: NavItem[];
5
7
  mode?: 'dark'
@@ -14,7 +16,7 @@ export interface HairyTheme {
14
16
  description?: string;
15
17
  };
16
18
  post?: {
17
- layout?: 'text' | 'markdown' | 'image' | 'image:slice' | 'image:slice:reverse'
19
+ layout?: HairyPostLayout
18
20
  images?: string[]
19
21
  }
20
22
  categories?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valaxy-theme-hairy",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "packageManager": "pnpm@7.5.0",
5
5
  "author": {
6
6
  "email": "wwu710632@gmail.com",
@@ -7,14 +7,12 @@ import { ElTimeline, ElTimelineItem } from 'element-plus/es/components/timeline/
7
7
  import { useCurrentCategory } from '../../hooks/useCategory'
8
8
  import { useCategoryPost } from '../../hooks/useCategoryPost'
9
9
 
10
- import { usePostLayout } from '../../hooks/usePostLayout'
11
10
  import type { HairyTheme } from '../..'
12
11
  import 'element-plus/es/components/timeline/style/index'
13
12
  import 'element-plus/es/components/timeline-item/style/index'
14
13
  const props = defineProps<{
15
14
  categories: string
16
15
  }>()
17
- const layout = usePostLayout()
18
16
  const themeConfig = useThemeConfig<HairyTheme>()
19
17
  const categoriesLayout = computed(() => themeConfig.value.categories?.layout || 'post')
20
18
 
@@ -76,8 +74,7 @@ const displayCategory = (key: string) => {
76
74
  </el-timeline-item>
77
75
  </el-timeline>
78
76
  <template v-else>
79
- <HairyPostImageList v-if="layout.includes('image')" :posts="posts" />
80
- <HairyPostList v-else :posts="posts" />
77
+ <HairyPostList :posts="posts" />
81
78
  </template>
82
79
  </template>
83
80
 
package/unocss.config.ts CHANGED
@@ -1,6 +1,15 @@
1
1
  import { defineConfig } from 'unocss'
2
2
  const safelist = [
3
3
  'i-ri:home-2-fill',
4
+ 'i-ri-list-check-2',
5
+ 'i-ri-flow-chart',
6
+ 'i-ri-user-line',
7
+
8
+ 'i-fluent-text-align-distributed-24-filled',
9
+ 'i-fluent-text-align-left-16-filled',
10
+ 'i-fluent-text-align-justify-20-filled',
11
+ 'i-fluent-markdown-20-filled',
12
+ 'i-fluent-code-text-16-filled',
4
13
  ]
5
14
  export default defineConfig({
6
15
  theme: {
@@ -20,6 +29,7 @@ export default defineConfig({
20
29
  ['font-frederick', { 'font-family': 'var(--hy-font-family-frederick)' }],
21
30
  ['text-primary', { color: 'var(--hy-c-primary)' }],
22
31
  ['border-primary', { 'border-color': 'var(--hy-c-primary)' }],
32
+ ['bg-primary', { background: 'var(--hy-c-primary)' }],
23
33
  ],
24
34
  shortcuts: [
25
35
  ['flex-center', 'flex justify-center items-center'],
@@ -1,17 +0,0 @@
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>
@@ -1,49 +0,0 @@
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-es'
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>