@sugarat/theme 0.2.8 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,7 +18,7 @@ npm create @sugarat/theme@latest
18
18
  yarn create @sugarat/theme
19
19
 
20
20
  # With Bun
21
- bunx @sugarat/create-theme --bun
21
+ bun create @sugarat/theme
22
22
  ```
23
23
 
24
24
  ![](https://img.cdn.sugarat.top/mdImg/MTY4OTQyMDE1NTcxMA==689420155710)
@@ -54,4 +54,5 @@ pnpm serve
54
54
  * [charles7c.github.io](https://github.com/Charles7c/charles7c.github.io)
55
55
  * [vitepress-blog-zaun](https://github.com/clark-cui/vitepress-blog-zaun)
56
56
  * [surmon](https://surmon.me/)
57
- * [vuejs/blog](https://github.com/vuejs/blog/tree/main)
57
+ * [vuejs/blog](https://github.com/vuejs/blog/tree/main)
58
+ * [列表无限滚动轮播效果](https://code.juejin.cn/pen/7145007064350195748)
package/node.d.ts CHANGED
@@ -181,6 +181,23 @@ declare namespace Theme {
181
181
  url: string;
182
182
  avatar: ThemeableImage;
183
183
  }
184
+ interface FriendConfig {
185
+ list: FriendLink[];
186
+ /**
187
+ * 是否随机展示
188
+ * @default false
189
+ */
190
+ random?: boolean;
191
+ /**
192
+ * 是否限制展示数量(超出自动切换)
193
+ */
194
+ limit?: number;
195
+ /**
196
+ * 滚动速度(ms),设置为 0 不滚动直接截取
197
+ * @default "动态计算"
198
+ */
199
+ scrollSpeed?: number;
200
+ }
184
201
  interface UserWork {
185
202
  title: string;
186
203
  description: string;
@@ -264,7 +281,7 @@ declare namespace Theme {
264
281
  */
265
282
  alert?: Alert;
266
283
  popover?: Popover;
267
- friend?: FriendLink[];
284
+ friend?: FriendLink[] | FriendConfig;
268
285
  authorList?: Omit<FriendLink, 'avatar'>[];
269
286
  /**
270
287
  * 启用 [vitepress-plugin-tabs](https://www.npmjs.com/package/vitepress-plugin-tabs)
package/node.js CHANGED
@@ -36,7 +36,7 @@ __export(node_exports, {
36
36
  });
37
37
  module.exports = __toCommonJS(node_exports);
38
38
 
39
- // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@1.0.0-rc.29_vue@3.3.4/node_modules/vitepress-plugin-tabs/dist/index.js
39
+ // ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@1.0.0-rc.32_vue@3.3.4/node_modules/vitepress-plugin-tabs/dist/index.js
40
40
  var tabsMarker = "=tabs";
41
41
  var tabsMarkerLen = tabsMarker.length;
42
42
  var ruleBlockTabs = (state, startLine, endLine, silent) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sugarat/theme",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "description": "简约风的 Vitepress 博客主题,sugarat vitepress blog theme",
5
5
  "author": "sugar",
6
6
  "license": "MIT",
@@ -54,7 +54,7 @@
54
54
  "pagefind": "1.0.3",
55
55
  "sass": "^1.56.1",
56
56
  "typescript": "^4.8.2",
57
- "vitepress": "1.0.0-rc.29",
57
+ "vitepress": "1.0.0-rc.32",
58
58
  "vue": "^3.3.4"
59
59
  },
60
60
  "scripts": {
@@ -5,9 +5,9 @@ import { useBlogConfig } from '../composables/config/blog'
5
5
 
6
6
  const { alert: alertProps } = useBlogConfig()
7
7
  const show = ref(false)
8
-
8
+ const storageKey = 'theme-blog-alert'
9
+ const closeFlag = `${storageKey}-close`
9
10
  onMounted(() => {
10
- const storageKey = 'theme-blog-alert'
11
11
  // 取旧值
12
12
  const oldValue = localStorage.getItem(storageKey)
13
13
  const newValue = JSON.stringify(alertProps)
@@ -21,13 +21,28 @@ onMounted(() => {
21
21
  show.value = false
22
22
  }, alertProps?.duration)
23
23
  }
24
+ return
24
25
  }
25
26
 
26
27
  if (oldValue !== newValue && alertProps?.duration === -1) {
27
28
  // 当做新值处理
28
29
  show.value = true
30
+ localStorage.removeItem(closeFlag)
31
+ return
32
+ }
33
+
34
+ // 新旧相等,判断是否点击过close,没点击关闭依然展示
35
+ if (oldValue === newValue && alertProps?.duration === -1 && !localStorage.getItem(closeFlag)) {
36
+ show.value = true
29
37
  }
30
38
  })
39
+
40
+ function handleClose() {
41
+ show.value = false
42
+ if (alertProps?.duration === -1) {
43
+ localStorage.setItem(closeFlag, `${+new Date()}`)
44
+ }
45
+ }
31
46
  </script>
32
47
 
33
48
  <template>
@@ -40,6 +55,7 @@ onMounted(() => {
40
55
  :closable="alertProps?.closable"
41
56
  :close-text="alertProps?.closeText"
42
57
  :description="alertProps?.description"
58
+ @close="handleClose"
43
59
  >
44
60
  <div v-if="alertProps?.html" v-html="alertProps?.html" />
45
61
  </ElAlert>
@@ -3,15 +3,48 @@ import { ElAvatar } from 'element-plus'
3
3
  import { useDark } from '@vueuse/core'
4
4
  import { computed } from 'vue'
5
5
  import { useBlogConfig } from '../composables/config/blog'
6
- import { getImageUrl } from '../utils/client'
6
+ import { getImageUrl, shuffleArray } from '../utils/client'
7
+ import type { Theme } from '../'
7
8
 
8
9
  const isDark = useDark({
9
10
  storageKey: 'vitepress-theme-appearance'
10
11
  })
11
12
 
12
13
  const { friend } = useBlogConfig()
14
+ const friendConfig = computed<Theme.FriendConfig>(() => ({
15
+ list: [],
16
+ random: false,
17
+ limit: Number.MAX_SAFE_INTEGER,
18
+ ...(Array.isArray(friend) ? { list: friend } : friend)
19
+ }))
20
+
21
+ // TODO: 待优化
22
+ const limit = computed(() => {
23
+ const { limit } = friendConfig.value
24
+ return (!limit || limit <= 0) ? 0 : limit || Number.MAX_SAFE_INTEGER
25
+ })
26
+
27
+ const scrollSpeed = computed(() => {
28
+ const { scrollSpeed } = friendConfig.value
29
+ return (!scrollSpeed || scrollSpeed <= 0) ? (friendConfig.value.list.length - 1) * (friendConfig.value.list.length / limit.value) * 1000 : scrollSpeed
30
+ })
31
+
32
+ const openScroll = computed(() => {
33
+ return scrollSpeed.value > 0 && limit.value < friendConfig.value.list.length
34
+ })
35
+
13
36
  const friendList = computed(() => {
14
- return friend?.map((v) => {
37
+ const data = [...friendConfig.value.list]
38
+ // 简单的随机打乱
39
+ if (friendConfig.value.random) {
40
+ data.splice(0, data.length, ...shuffleArray(data))
41
+ }
42
+
43
+ // 展示个数限制,删除多余的
44
+ if (scrollSpeed.value === 0 && limit.value) {
45
+ data.splice(limit.value)
46
+ }
47
+ const list = data.map((v) => {
15
48
  const { avatar, nickname } = v
16
49
  const avatarUrl = getImageUrl(avatar, isDark.value)
17
50
  let alt = nickname
@@ -25,6 +58,18 @@ const friendList = computed(() => {
25
58
  alt
26
59
  }
27
60
  })
61
+ return openScroll.value ? [...list, ...list] : list
62
+ })
63
+
64
+ const cardHeight = 83
65
+ const scrollWrapperHeight = computed(() => {
66
+ return openScroll.value ? limit.value * cardHeight : 0
67
+ })
68
+ const containerHeight = computed(() => {
69
+ return scrollWrapperHeight.value ? `${scrollWrapperHeight.value}px` : 'auto'
70
+ })
71
+ const scrollTop = computed(() => {
72
+ return `-${scrollWrapperHeight.value * 2}px`
28
73
  })
29
74
  </script>
30
75
 
@@ -51,18 +96,30 @@ const friendList = computed(() => {
51
96
  />
52
97
  </svg> 友情链接</span>
53
98
  </div>
54
- <!-- 文章列表 -->
55
- <ol class="friend-list">
56
- <li v-for="v in friendList" :key="v.nickname">
57
- <a :href="v.url" target="_blank">
58
- <ElAvatar :size="50" :src="v.avatar" :alt="v.alt" />
59
- <div>
60
- <span class="nickname">{{ v.nickname }}</span>
61
- <p class="des">{{ v.des }}</p>
62
- </div>
63
- </a>
64
- </li>
65
- </ol>
99
+ <!-- 友链列表 -->
100
+ <div
101
+ class="scroll-wrapper" :style="{
102
+ height: containerHeight,
103
+ }"
104
+ >
105
+ <ol
106
+ class="friend-list" :style="{
107
+ animationPlayState: openScroll ? 'running' : 'paused',
108
+ animationDuration: `${scrollSpeed / 1000}s`,
109
+ }
110
+ "
111
+ >
112
+ <li v-for="v in friendList" :key="v.nickname">
113
+ <a :href="v.url" target="_blank">
114
+ <ElAvatar :size="50" :src="v.avatar" :alt="v.alt" />
115
+ <div>
116
+ <span class="nickname">{{ v.nickname }}</span>
117
+ <p class="des">{{ v.des }}</p>
118
+ </div>
119
+ </a>
120
+ </li>
121
+ </ol>
122
+ </div>
66
123
  </div>
67
124
  </template>
68
125
 
@@ -102,6 +159,20 @@ const friendList = computed(() => {
102
159
  flex-direction: column;
103
160
  }
104
161
 
162
+ @keyframes scrollList {
163
+ 0% {
164
+ top: 0;
165
+ }
166
+ 100% {
167
+ top: v-bind(scrollTop);
168
+ }
169
+ }
170
+
171
+ .scroll-wrapper {
172
+ overflow: hidden;
173
+ position: relative;
174
+ }
175
+
105
176
  .friend-list {
106
177
  display: flex;
107
178
  flex-direction: column;
@@ -110,6 +181,16 @@ const friendList = computed(() => {
110
181
  padding: 0 10px 0 0px;
111
182
  width: 100%;
112
183
 
184
+ position: relative;
185
+ width: 100%;
186
+ animation-name: scrollList;
187
+ animation-timing-function:linear;
188
+ animation-iteration-count:infinite;
189
+
190
+ &:hover {
191
+ animation-play-state: paused !important;
192
+ }
193
+
113
194
  li {
114
195
  padding: 6px;
115
196
  margin-top: 10px;
@@ -1,14 +1,15 @@
1
1
  <script lang="ts" setup>
2
2
  import { computed, watch } from 'vue'
3
3
  import { ElTag } from 'element-plus'
4
- import { useBrowserLocation, useDark } from '@vueuse/core'
5
- import { useRouter } from 'vitepress'
4
+ import { useBrowserLocation, useDark, useUrlSearchParams } from '@vueuse/core'
5
+ import { useRouter, useRoute } from 'vitepress'
6
6
  import {
7
7
  useActiveTag,
8
8
  useArticles,
9
9
  useCurrentPageNum
10
10
  } from '../composables/config/blog'
11
11
 
12
+ const route = useRoute()
12
13
  const docs = useArticles()
13
14
 
14
15
  const tags = computed(() => {
@@ -62,6 +63,17 @@ watch(
62
63
  immediate: true
63
64
  }
64
65
  )
66
+
67
+ watch(
68
+ route,
69
+ () => {
70
+ const params = useUrlSearchParams()
71
+ if (!params.tag) {
72
+ activeTag.value.type = ''
73
+ activeTag.value.label = ''
74
+ }
75
+ }
76
+ )
65
77
  </script>
66
78
 
67
79
  <template>
@@ -17,13 +17,14 @@ const bodyContent = computed(() => {
17
17
  const footerContent = computed(() => {
18
18
  return popoverProps?.footer || []
19
19
  })
20
+ const storageKey = 'theme-blog-popover'
21
+ const closeFlag = `${storageKey}-close`
20
22
 
21
23
  onMounted(() => {
22
24
  if (!popoverProps?.title) {
23
25
  return
24
26
  }
25
27
 
26
- const storageKey = 'theme-blog-popover'
27
28
  // 取旧值
28
29
  const oldValue = localStorage.getItem(storageKey)
29
30
  const newValue = JSON.stringify(popoverProps)
@@ -37,14 +38,29 @@ onMounted(() => {
37
38
  show.value = false
38
39
  }, popoverProps?.duration)
39
40
  }
41
+ return
40
42
  }
41
43
 
42
44
  if (oldValue !== newValue && popoverProps?.duration === -1) {
43
45
  // 当做新值处理
44
46
  show.value = true
47
+ localStorage.removeItem(closeFlag)
48
+ return
49
+ }
50
+
51
+ // 新旧相等,判断是否点击过close,没点击关闭依然展示
52
+ if (oldValue === newValue && popoverProps?.duration === -1 && !localStorage.getItem(closeFlag)) {
53
+ show.value = true
45
54
  }
46
55
  })
47
56
 
57
+ function handleClose() {
58
+ show.value = false
59
+ if (popoverProps?.duration === -1) {
60
+ localStorage.setItem(closeFlag, `${+new Date()}`)
61
+ }
62
+ }
63
+
48
64
  function PopoverValue(props: { key: number; item: BlogPopover.Value },
49
65
  { slots }: any) {
50
66
  const { key, item } = props
@@ -105,7 +121,7 @@ function PopoverValue(props: { key: number; item: BlogPopover.Value },
105
121
  </ElIcon>
106
122
  <span class="title">{{ popoverProps?.title }}</span>
107
123
  </div>
108
- <ElIcon class="close-icon" size="20px" @click="show = false">
124
+ <ElIcon class="close-icon" size="20px" @click="handleClose">
109
125
  <CircleCloseFilled />
110
126
  </ElIcon>
111
127
  </div>
@@ -193,6 +193,24 @@ export namespace Theme {
193
193
  avatar: ThemeableImage
194
194
  }
195
195
 
196
+ export interface FriendConfig {
197
+ list: FriendLink[]
198
+ /**
199
+ * 是否随机展示
200
+ * @default false
201
+ */
202
+ random?: boolean
203
+ /**
204
+ * 是否限制展示数量(超出自动切换)
205
+ */
206
+ limit?: number
207
+ /**
208
+ * 滚动速度(ms),设置为 0 不滚动直接截取
209
+ * @default "动态计算"
210
+ */
211
+ scrollSpeed?: number
212
+ }
213
+
196
214
  export interface UserWork {
197
215
  title: string
198
216
  description: string
@@ -295,7 +313,7 @@ export namespace Theme {
295
313
  */
296
314
  alert?: Alert
297
315
  popover?: Popover
298
- friend?: FriendLink[]
316
+ friend?: FriendLink[] | FriendConfig
299
317
  authorList?: Omit<FriendLink, 'avatar'>[]
300
318
  /**
301
319
  * 启用 [vitepress-plugin-tabs](https://www.npmjs.com/package/vitepress-plugin-tabs)
@@ -1,5 +1,13 @@
1
1
  import type { ThemeableImage } from '../../composables/config'
2
2
 
3
+ export function shuffleArray(arr: any[]) {
4
+ const array = [...arr]
5
+ for (let i = array.length - 1; i > 0; i--) {
6
+ const j = Math.floor(Math.random() * (i + 1));
7
+ [array[i], array[j]] = [array[j], array[i]]
8
+ }
9
+ return array
10
+ }
3
11
  export function formatDate(d: any, fmt = 'yyyy-MM-dd hh:mm:ss') {
4
12
  if (!(d instanceof Date)) {
5
13
  d = new Date(d)