clarity-theme 0.1.0 → 0.1.2

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 (154) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +70 -4
  3. package/README.zh-CN.md +67 -4
  4. package/nuxt.config.ts +217 -214
  5. package/package.json +35 -37
  6. package/{app → src}/app.config.ts +110 -110
  7. package/{app → src}/app.vue +5 -5
  8. package/{app → src}/components/blog/BlogAside.vue +25 -25
  9. package/{app → src}/components/blog/BlogFooter.vue +25 -25
  10. package/{app → src}/components/blog/BlogHeader.global.vue +48 -48
  11. package/{app → src}/components/blog/BlogPanel.vue +43 -43
  12. package/{app → src}/components/blog/BlogSidebar.vue +53 -53
  13. package/{app → src}/components/blog/BlogWidget.vue +42 -42
  14. package/{app → src}/components/blog/Mask.vue +25 -25
  15. package/{app → src}/components/blog/SkipToContent.vue +9 -9
  16. package/{app → src}/components/blog/ThemeToggle.vue +22 -22
  17. package/{app → src}/components/content/Badge.vue +45 -45
  18. package/{app → src}/components/content/Blur.vue +14 -14
  19. package/{app → src}/components/content/CardList.vue +8 -8
  20. package/{app → src}/components/content/Chat.vue +41 -41
  21. package/{app → src}/components/content/Copy.vue +71 -71
  22. package/{app → src}/components/content/EmojiClock.vue +49 -49
  23. package/{app → src}/components/content/FeedCard.vue +96 -96
  24. package/{app → src}/components/content/FeedGroup.vue +61 -61
  25. package/{app → src}/components/content/Folding.vue +19 -19
  26. package/{app → src}/components/content/Key.vue +143 -143
  27. package/{app → src}/components/content/LinkBanner.vue +27 -27
  28. package/{app → src}/components/content/LinkCard.vue +28 -28
  29. package/{app → src}/components/content/MdTitle.vue +8 -8
  30. package/{app → src}/components/content/Mermaid.vue +99 -99
  31. package/{app → src}/components/content/MusicScore.vue +63 -63
  32. package/{app → src}/components/content/Pic.vue +49 -49
  33. package/{app → src}/components/content/Poetry.vue +27 -27
  34. package/{app → src}/components/content/ProseA.vue +24 -24
  35. package/{app → src}/components/content/ProseCode.vue +16 -16
  36. package/{app → src}/components/content/ProsePre.vue +117 -117
  37. package/{app → src}/components/content/ProseTable.vue +23 -23
  38. package/{app → src}/components/content/Quote.vue +21 -21
  39. package/{app → src}/components/content/Tab.vue +32 -32
  40. package/{app → src}/components/content/Timeline.vue +33 -33
  41. package/{app → src}/components/content/Tip.vue +40 -40
  42. package/{app → src}/components/content/VideoEmbed.vue +112 -112
  43. package/{app → src}/components/partial/Button.vue +36 -36
  44. package/{app → src}/components/partial/DlGroup.vue +31 -31
  45. package/{app → src}/components/partial/Dropdown.vue +21 -21
  46. package/{app → src}/components/partial/Error.vue +39 -39
  47. package/{app → src}/components/partial/Expand.vue +26 -26
  48. package/{app → src}/components/partial/IconNavList.vue +24 -24
  49. package/{app → src}/components/partial/Pagination.vue +62 -62
  50. package/{app → src}/components/partial/RadioGroup.vue +30 -30
  51. package/{app → src}/components/partial/Secret.vue +11 -11
  52. package/{app → src}/components/partial/Slider.vue +48 -48
  53. package/{app → src}/components/partial/Toggle.vue +17 -17
  54. package/{app → src}/components/popover/Lightbox.vue +44 -44
  55. package/{app → src}/components/popover/Search.vue +143 -143
  56. package/{app → src}/components/popover/SearchItem.vue +28 -28
  57. package/{app → src}/components/post/Archive.vue +40 -40
  58. package/{app → src}/components/post/Article.vue +49 -49
  59. package/{app → src}/components/post/Comment.vue +119 -119
  60. package/{app → src}/components/post/Excerpt.vue +40 -40
  61. package/{app → src}/components/post/OrderToggle.vue +70 -70
  62. package/{app → src}/components/post/PostFooter.vue +52 -52
  63. package/{app → src}/components/post/PostHeader.vue +66 -66
  64. package/{app → src}/components/post/PostSurround.vue +51 -51
  65. package/{app → src}/components/post/Slide.vue +85 -85
  66. package/{app → src}/components/util/Date.vue +62 -62
  67. package/{app → src}/components/util/HydrateSafe.vue +10 -10
  68. package/{app → src}/components/util/Img.vue +38 -38
  69. package/{app → src}/components/util/Link.vue +11 -11
  70. package/{app → src}/components/widget/BlogLog.vue +23 -23
  71. package/{app → src}/components/widget/BlogStats.vue +38 -38
  72. package/{app → src}/components/widget/BlogTech.vue +57 -57
  73. package/{app → src}/components/widget/Empty.vue +3 -3
  74. package/{app → src}/components/widget/Toc.vue +67 -67
  75. package/{app → src}/composables/useArticle.ts +115 -115
  76. package/{app → src}/composables/useAvoid.ts +87 -87
  77. package/{app → src}/composables/useCopy.ts +21 -21
  78. package/{app → src}/composables/usePagination.ts +70 -70
  79. package/{app → src}/composables/useShiki.ts +129 -129
  80. package/{app → src}/composables/useToc.ts +57 -57
  81. package/{app → src}/composables/useWidgets.ts +56 -56
  82. package/{config → src/config}/app.ts +62 -62
  83. package/{config → src/config}/content.ts +75 -75
  84. package/{config → src/config}/define.ts +3 -3
  85. package/src/config/feeds.empty.ts +6 -0
  86. package/{config → src/config}/index.ts +1 -1
  87. package/{config → src/config}/schema.ts +89 -89
  88. package/{app → src}/error.vue +31 -31
  89. package/{img → src/img}/index.ts +2 -2
  90. package/{app → src}/layouts/default.vue +25 -25
  91. package/{modules → src/modules}/clarity-config/index.ts +168 -162
  92. package/src/modules/clarity-source-layout/index.ts +78 -0
  93. package/{app → src}/pages/[...slug].vue +59 -59
  94. package/{app → src}/pages/archive.vue +129 -129
  95. package/{app → src}/pages/index.vue +76 -76
  96. package/{app → src}/pages/link.vue +62 -62
  97. package/{app → src}/pages/preview.vue +46 -46
  98. package/{app → src}/plugins/easter-egg.ts +23 -23
  99. package/{app → src}/plugins/init.ts +6 -6
  100. package/{app → src}/plugins/tippy.ts +22 -22
  101. package/{public → src/public}/assets/atom.css +274 -274
  102. package/{public → src/public}/assets/atom.xsl +109 -109
  103. package/{remark-plugins → src/remark-plugins}/rehype-meta-slots.ts +58 -58
  104. package/{remark-plugins → src/remark-plugins}/remark-code-component.ts +41 -41
  105. package/{server → src/server}/api/stats.get.ts +84 -83
  106. package/{server → src/server}/routes/atom.xml.get.ts +85 -85
  107. package/{server → src/server}/routes/subscriptions.opml.get.ts +64 -64
  108. package/{shared → src/shared}/utils/clarity.ts +24 -24
  109. package/{shared → src/shared}/utils/icon.ts +218 -218
  110. package/{shared → src/shared}/utils/link.ts +52 -52
  111. package/{shared → src/shared}/utils/str.ts +88 -88
  112. package/{shared → src/shared}/utils/time.ts +115 -115
  113. package/{app → src}/shiki.config.ts +8 -8
  114. package/{app → src}/stores/layout.ts +34 -34
  115. package/{app → src}/stores/search.ts +37 -37
  116. package/{app → src}/types/article.ts +16 -16
  117. package/src/types/feed.ts +1 -0
  118. package/{app → src}/types/index.ts +14 -14
  119. package/{app → src}/types/nav.ts +11 -11
  120. package/{app → src}/utils/anim.ts +37 -37
  121. package/{app → src}/utils/img.ts +83 -83
  122. package/app/types/feed.ts +0 -1
  123. package/config/feeds.empty.ts +0 -4
  124. package/{app → src}/assets/css/_variable.scss +0 -0
  125. package/{app → src}/assets/css/animation.scss +0 -0
  126. package/{app → src}/assets/css/article.scss +0 -0
  127. package/{app → src}/assets/css/color.scss +0 -0
  128. package/{app → src}/assets/css/font.scss +0 -0
  129. package/{app → src}/assets/css/main.scss +0 -0
  130. package/{app → src}/assets/css/reusable.scss +0 -0
  131. package/{app → src}/assets/icons/solar-rewind-back-bold-duotone.svg +0 -0
  132. package/{app → src}/assets/icons/solar-rewind-forward-bold-duotone.svg +0 -0
  133. package/{app → src}/components/content/Alert.vue +0 -0
  134. package/{config → src/config}/content.d.mts +0 -0
  135. package/{config → src/config}/content.mjs +0 -0
  136. package/{config → src/config}/define.mjs +0 -0
  137. package/{config → src/config}/feed.ts +0 -0
  138. package/{config → src/config}/index.d.mts +0 -0
  139. package/{config → src/config}/index.mjs +0 -0
  140. package/{config → src/config}/public.ts +1 -1
  141. package/{config → src/config}/schema.d.mts +0 -0
  142. package/{config → src/config}/schema.mjs +0 -0
  143. package/{config → src/config}/server.ts +1 -1
  144. /package/{img → src/img}/index.d.mts +0 -0
  145. /package/{img → src/img}/index.mjs +0 -0
  146. /package/{modules → src/modules}/clarity-config/anti-mirror-client.ts +0 -0
  147. /package/{public → src/public}/fonts/AlimamaFangYuanTi.woff2 +0 -0
  148. /package/{remark-plugins → src/remark-plugins}/rehype-meta-slots.d.mts +0 -0
  149. /package/{remark-plugins → src/remark-plugins}/rehype-meta-slots.mjs +0 -0
  150. /package/{remark-plugins → src/remark-plugins}/remark-code-component.d.mts +0 -0
  151. /package/{remark-plugins → src/remark-plugins}/remark-code-component.mjs +0 -0
  152. /package/{server → src/server}/utils/clarity.ts +0 -0
  153. /package/{shared → src/shared}/types/content.ts +0 -0
  154. /package/{app → src}/types/modal.ts +0 -0
@@ -1,54 +1,54 @@
1
- <script setup lang="ts">
2
- import type { ArticleProps } from '../../types/article'
3
-
4
- defineOptions({ inheritAttrs: false })
5
- defineProps<ArticleProps>()
6
-
7
- const [DefineTemplate, ReuseTemplate] = createReusableTemplate<{
8
- title: string
9
- }>({ inheritAttrs: false })
10
-
11
- const clarity = useClarityConfig()
12
- </script>
13
-
14
- <template>
15
- <div class="post-footer">
16
- <DefineTemplate v-slot="{ $slots, title }">
17
- <section>
18
- <div class="title text-creative">
19
- {{ title }}
20
- </div>
21
-
22
- <div class="content">
23
- <component :is="$slots.default" />
24
- </div>
25
- </section>
26
- </DefineTemplate>
27
-
28
- <ReuseTemplate v-if="references" title="参考链接">
29
- <ul>
30
- <li v-for="{ title, link }, i in references" :key="i">
31
- <ProseA :href="link || ''">
32
- {{ title ?? link }}
33
- </ProseA>
34
- </li>
35
- </ul>
36
- </ReuseTemplate>
37
-
38
- <ReuseTemplate :title="meta?.slots?.copyright?.props?.title as string || '许可协议'">
39
- <ContentRenderer v-if="meta?.slots?.copyright" :value="meta?.slots?.copyright" />
40
- <p v-else>
41
- 本文采用 <ProseA v-if="clarity.site.copyright?.url" :href="clarity.site.copyright.url">
42
- {{ clarity.site.copyright.name }}
43
- </ProseA>
44
- <span v-else-if="clarity.site.copyright?.name">{{ clarity.site.copyright.name }}</span>
45
- 许可协议,转载请注明出处。
46
- </p>
47
- </ReuseTemplate>
48
- </div>
49
- </template>
50
-
51
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ import type { ArticleProps } from '../../types/article'
3
+
4
+ defineOptions({ inheritAttrs: false })
5
+ defineProps<ArticleProps>()
6
+
7
+ const [DefineTemplate, ReuseTemplate] = createReusableTemplate<{
8
+ title: string
9
+ }>({ inheritAttrs: false })
10
+
11
+ const clarity = useClarityConfig()
12
+ </script>
13
+
14
+ <template>
15
+ <div class="post-footer">
16
+ <DefineTemplate v-slot="{ $slots, title }">
17
+ <section>
18
+ <div class="title text-creative">
19
+ {{ title }}
20
+ </div>
21
+
22
+ <div class="content">
23
+ <component :is="$slots.default" />
24
+ </div>
25
+ </section>
26
+ </DefineTemplate>
27
+
28
+ <ReuseTemplate v-if="references" title="参考链接">
29
+ <ul>
30
+ <li v-for="{ title, link }, i in references" :key="i">
31
+ <ProseA :href="link || ''">
32
+ {{ title ?? link }}
33
+ </ProseA>
34
+ </li>
35
+ </ul>
36
+ </ReuseTemplate>
37
+
38
+ <ReuseTemplate :title="meta?.slots?.copyright?.props?.title as string || '许可协议'">
39
+ <ContentRenderer v-if="meta?.slots?.copyright" :value="meta?.slots?.copyright" />
40
+ <p v-else>
41
+ 本文采用 <ProseA v-if="clarity.site.copyright?.url" :href="clarity.site.copyright.url">
42
+ {{ clarity.site.copyright.name }}
43
+ </ProseA>
44
+ <span v-else-if="clarity.site.copyright?.name">{{ clarity.site.copyright.name }}</span>
45
+ 许可协议,转载请注明出处。
46
+ </p>
47
+ </ReuseTemplate>
48
+ </div>
49
+ </template>
50
+
51
+ <style lang="scss" scoped>
52
52
  .post-footer {
53
53
  margin: 2rem 0.5rem;
54
54
  border: 1px solid var(--c-border);
@@ -77,4 +77,4 @@ section {
77
77
  margin: 0.5em 0;
78
78
  }
79
79
  }
80
- </style>
80
+ </style>
@@ -1,69 +1,69 @@
1
- <script setup lang="ts">
2
- import type { ArticleProps } from '../../types/article'
3
-
4
- defineOptions({ inheritAttrs: false })
5
- const props = defineProps<ArticleProps>()
6
-
7
- const clarity = useClarityConfig()
8
-
9
- const coverFilter = computed(() => props.meta?.coverFilter || (props.meta?.coverDim && 'brightness(0.75)') || undefined)
10
-
11
- const shareText = `【${clarity.site.title}】${props.title}\n\n${
12
- props.description ? `${props.description}\n\n` : ''}${
13
- new URL(props.path!, clarity.site.url).href}`
14
-
15
- const { copy, copied } = useCopy(shareText)
16
- </script>
17
-
18
- <template>
19
- <div class="post-header" :class="{ 'has-cover': image }">
20
- <Pic v-if="image" class="post-cover" :src="image" :alt="title" :filter="coverFilter" />
21
- <div class="post-nav">
22
- <div class="operations">
23
- <Icon v-show="false" name="tabler:check" />
24
- <ZButton
25
- :icon="copied ? 'tabler:check' : 'tabler:share'"
26
- text="文字分享"
27
- @click="copy()"
28
- />
29
- </div>
30
-
31
- <div v-if="!meta?.hideInfo" class="post-info">
32
- <UtilDate
33
- v-if="date"
34
- v-tip
35
- :tip-transform="d => `创建于${d}`"
36
- :date
37
- icon="tabler:pencil-minus"
38
- />
39
-
40
- <UtilDate
1
+ <script setup lang="ts">
2
+ import type { ArticleProps } from '../../types/article'
3
+
4
+ defineOptions({ inheritAttrs: false })
5
+ const props = defineProps<ArticleProps>()
6
+
7
+ const clarity = useClarityConfig()
8
+
9
+ const coverFilter = computed(() => props.meta?.coverFilter || (props.meta?.coverDim && 'brightness(0.75)') || undefined)
10
+
11
+ const shareText = `【${clarity.site.title}】${props.title}\n\n${
12
+ props.description ? `${props.description}\n\n` : ''}${
13
+ new URL(props.path!, clarity.site.url).href}`
14
+
15
+ const { copy, copied } = useCopy(shareText)
16
+ </script>
17
+
18
+ <template>
19
+ <div class="post-header" :class="{ 'has-cover': image }">
20
+ <Pic v-if="image" class="post-cover" :src="image" :alt="title" :filter="coverFilter" />
21
+ <div class="post-nav">
22
+ <div class="operations">
23
+ <Icon v-show="false" name="tabler:check" />
24
+ <ZButton
25
+ :icon="copied ? 'tabler:check' : 'tabler:share'"
26
+ text="文字分享"
27
+ @click="copy()"
28
+ />
29
+ </div>
30
+
31
+ <div v-if="!meta?.hideInfo" class="post-info">
32
+ <UtilDate
33
+ v-if="date"
34
+ v-tip
35
+ :tip-transform="d => `创建于${d}`"
36
+ :date
37
+ icon="tabler:pencil-minus"
38
+ />
39
+
40
+ <UtilDate
41
41
  v-if="updated && isTimeDiffSignificant(date, updated, undefined, 1)"
42
- v-tip
43
- :tip-transform="d => `修改于${d}`"
44
- :date="updated"
45
- icon="tabler:clock-edit"
46
- />
47
-
48
- <span v-if="categories">
49
- <Icon :name="getCategoryIcon(categories[0])" />
50
- {{ categories[0] }}
51
- </span>
52
-
53
- <span>
54
- <Icon name="tabler:pilcrow" />
55
- {{ formatNumber(readingTime?.words) }} 字
56
- </span>
57
- </div>
58
- </div>
59
-
60
- <h1 class="post-title" :class="getPostTypeClassName(type)">
61
- {{ title }}
62
- </h1>
63
- </div>
64
- </template>
65
-
66
- <style lang="scss" scoped>
42
+ v-tip
43
+ :tip-transform="d => `修改于${d}`"
44
+ :date="updated"
45
+ icon="tabler:clock-edit"
46
+ />
47
+
48
+ <span v-if="categories">
49
+ <Icon :name="getCategoryIcon(categories[0])" />
50
+ {{ categories[0] }}
51
+ </span>
52
+
53
+ <span>
54
+ <Icon name="tabler:pilcrow" />
55
+ {{ formatNumber(readingTime?.words) }} 字
56
+ </span>
57
+ </div>
58
+ </div>
59
+
60
+ <h1 class="post-title" :class="getPostTypeClassName(type)">
61
+ {{ title }}
62
+ </h1>
63
+ </div>
64
+ </template>
65
+
66
+ <style lang="scss" scoped>
67
67
  .post-header {
68
68
  contain: paint; // overflow hidden + position relative
69
69
  display: flex;
@@ -144,4 +144,4 @@ const { copy, copied } = useCopy(shareText)
144
144
  column-gap: clamp(1em, 3%, 1.5em);
145
145
  }
146
146
  }
147
- </style>
147
+ </style>
@@ -1,53 +1,53 @@
1
- <script setup lang="ts">
2
- import type { ArticleProps } from '../../types/article'
3
-
4
- const route = useRoute()
5
-
6
- const { data: surrounds } = await useAsyncData(
7
- `surround:${route.path}`,
8
- () => queryCollectionItemSurroundings('content', route.path, { fields: ['date', 'title', 'type'] })
9
- .order('date', 'ASC')
10
- .where('stem', 'LIKE', `posts/%`),
11
- )
12
-
13
- const [prev = null, next = null] = surrounds.value ?? []
14
-
15
- const [DefineTemplate, ReuseTemplate] = createReusableTemplate<{
16
- post: ArticleProps | null
17
- icon: string
18
- fallbackIcon: string
19
- fallbackText: string
20
- alignEnd?: boolean
21
- }>({ inheritAttrs: false })
22
- </script>
23
-
24
- <template>
25
- <DefineTemplate v-slot="{ post, icon, fallbackIcon, fallbackText, alignEnd }">
26
- <UtilLink :to="post?.path" class="surround-link" :align-end>
27
- <Icon :class="{ 'rtl-flip': post }" :name="post ? icon : fallbackIcon" />
28
- <div class="surround-text">
29
- <strong class="title" :class="getPostTypeClassName(post?.type)">
30
- {{ post?.title || fallbackText }}
31
- </strong>
32
- <UtilDate v-if="post?.date" class="date" :date="post.date" />
33
- </div>
34
- </UtilLink>
35
- </DefineTemplate>
36
-
37
- <div v-if="prev || next" class="surround-post" dir="ltr">
38
- <ReuseTemplate
39
- :post="next" icon="clarity:solar-rewind-back-bold-duotone"
40
- fallback-icon="line-md:coffee-twotone-loop" fallback-text="新故事即将发生"
41
- />
42
- <ReuseTemplate
43
- :post="prev" icon="clarity:solar-rewind-forward-bold-duotone"
44
- fallback-icon="line-md:construction-twotone" fallback-text="已抵达博客尽头"
45
- align-end
46
- />
47
- </div>
48
- </template>
49
-
50
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ import type { ArticleProps } from '../../types/article'
3
+
4
+ const route = useRoute()
5
+
6
+ const { data: surrounds } = await useAsyncData(
7
+ `surround:${route.path}`,
8
+ () => queryCollectionItemSurroundings('content', route.path, { fields: ['date', 'title', 'type'] })
9
+ .order('date', 'ASC')
10
+ .where('stem', 'LIKE', `posts/%`),
11
+ )
12
+
13
+ const [prev = null, next = null] = surrounds.value ?? []
14
+
15
+ const [DefineTemplate, ReuseTemplate] = createReusableTemplate<{
16
+ post: ArticleProps | null
17
+ icon: string
18
+ fallbackIcon: string
19
+ fallbackText: string
20
+ alignEnd?: boolean
21
+ }>({ inheritAttrs: false })
22
+ </script>
23
+
24
+ <template>
25
+ <DefineTemplate v-slot="{ post, icon, fallbackIcon, fallbackText, alignEnd }">
26
+ <UtilLink :to="post?.path" class="surround-link" :align-end>
27
+ <Icon :class="{ 'rtl-flip': post }" :name="post ? icon : fallbackIcon" />
28
+ <div class="surround-text">
29
+ <strong class="title" :class="getPostTypeClassName(post?.type)">
30
+ {{ post?.title || fallbackText }}
31
+ </strong>
32
+ <UtilDate v-if="post?.date" class="date" :date="post.date" />
33
+ </div>
34
+ </UtilLink>
35
+ </DefineTemplate>
36
+
37
+ <div v-if="prev || next" class="surround-post" dir="ltr">
38
+ <ReuseTemplate
39
+ :post="next" icon="clarity:solar-rewind-back-bold-duotone"
40
+ fallback-icon="line-md:coffee-twotone-loop" fallback-text="新故事即将发生"
41
+ />
42
+ <ReuseTemplate
43
+ :post="prev" icon="clarity:solar-rewind-forward-bold-duotone"
44
+ fallback-icon="line-md:construction-twotone" fallback-text="已抵达博客尽头"
45
+ align-end
46
+ />
47
+ </div>
48
+ </template>
49
+
50
+ <style lang="scss" scoped>
51
51
  .surround-post {
52
52
  contain: layout;
53
53
  display: flex;
@@ -108,4 +108,4 @@ const [DefineTemplate, ReuseTemplate] = createReusableTemplate<{
108
108
  }
109
109
  }
110
110
  }
111
- </style>
111
+ </style>
@@ -1,87 +1,87 @@
1
- <script setup lang="ts">
2
- import type { ArticleProps } from '../../types/article'
3
- import Autoplay from 'embla-carousel-autoplay'
4
- import emblaCarouselVue from 'embla-carousel-vue'
5
- import { WheelGesturesPlugin } from 'embla-carousel-wheel-gestures'
6
-
7
- defineProps<{ list: ArticleProps[] }>()
8
-
9
- const clarity = useClarityConfig()
10
- const compConf = computed(() => clarity.component.slide)
11
-
12
- // @keep-sorted
13
- const [carouselEl, carouselApi] = emblaCarouselVue({
14
- containScroll: false,
15
- loop: true,
16
- skipSnaps: true,
17
- }, [
18
- Autoplay({ stopOnInteraction: false, stopOnMouseEnter: true }),
19
- WheelGesturesPlugin(),
20
- ])
21
-
22
- // 鼠标横向滚动 / Shift + 纵向滚轮事件
23
- useEventListener(carouselEl, 'wheel', (e) => {
24
- const delta = e.deltaX + (e.shiftKey ? e.deltaY : 0)
25
- if (Math.abs(delta) < 80)
26
- return
27
- delta > 0 ? carouselApi.value?.scrollNext() : carouselApi.value?.scrollPrev()
28
- }, { passive: true })
29
- </script>
30
-
31
- <template>
32
- <div class="z-slide">
33
- <div class="z-slide-header">
34
- <span class="title text-creative">精选文章</span>
35
- <div class="at-slide-hover">
36
- <Icon name="tabler:mouse" />
37
- 按住 Shift 横向滚动
38
- </div>
39
- </div>
40
-
41
- <div ref="carouselEl" class="z-slide-body" dir="ltr">
42
- <div class="slide-list">
43
- <UtilLink
44
- v-for="(article, index) in list"
45
- :key="index"
46
- class="slide-item"
47
- :title="article.description"
48
- :to="article.path"
49
- >
50
- <NuxtImg class="cover" :src="article.image" :alt="compConf.showTitle ? '' : article.title" />
51
-
52
- <div v-if="compConf.showTitle" class="stable-info text-creative">
53
- {{ article.title }}
54
- </div>
55
-
56
- <div class="hover-info">
57
- <div class="title text-creative">
58
- {{ article.title }}
59
- </div>
60
- <UtilDate v-if="article.date" class="desc" :date="article.date" />
61
- </div>
62
- </UtilLink>
63
- </div>
64
-
65
- <ZButton
66
- class="carousel-action prev at-slide-hover"
67
- aria-label="上一页"
68
- icon="tabler:chevron-left"
69
- tabindex="-1"
70
- @click="carouselApi?.scrollPrev()"
71
- />
72
-
73
- <ZButton
74
- class="carousel-action next at-slide-hover"
75
- aria-label="下一页"
76
- icon="tabler:chevron-right"
77
- tabindex="-1"
78
- @click="carouselApi?.scrollNext()"
79
- />
80
- </div>
81
- </div>
82
- </template>
83
-
84
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ import type { ArticleProps } from '../../types/article'
3
+ import Autoplay from 'embla-carousel-autoplay'
4
+ import emblaCarouselVue from 'embla-carousel-vue'
5
+ import { WheelGesturesPlugin } from 'embla-carousel-wheel-gestures'
6
+
7
+ defineProps<{ list: ArticleProps[] }>()
8
+
9
+ const clarity = useClarityConfig()
10
+ const compConf = computed(() => clarity.component.slide)
11
+
12
+ // @keep-sorted
13
+ const [carouselEl, carouselApi] = emblaCarouselVue({
14
+ containScroll: false,
15
+ loop: true,
16
+ skipSnaps: true,
17
+ }, [
18
+ Autoplay({ stopOnInteraction: false, stopOnMouseEnter: true }),
19
+ WheelGesturesPlugin(),
20
+ ])
21
+
22
+ // 鼠标横向滚动 / Shift + 纵向滚轮事件
23
+ useEventListener(carouselEl, 'wheel', (e) => {
24
+ const delta = e.deltaX + (e.shiftKey ? e.deltaY : 0)
25
+ if (Math.abs(delta) < 80)
26
+ return
27
+ delta > 0 ? carouselApi.value?.scrollNext() : carouselApi.value?.scrollPrev()
28
+ }, { passive: true })
29
+ </script>
30
+
31
+ <template>
32
+ <div class="z-slide">
33
+ <div class="z-slide-header">
34
+ <span class="title text-creative">精选文章</span>
35
+ <div class="at-slide-hover">
36
+ <Icon name="tabler:mouse" />
37
+ 按住 Shift 横向滚动
38
+ </div>
39
+ </div>
40
+
41
+ <div ref="carouselEl" class="z-slide-body" dir="ltr">
42
+ <div class="slide-list">
43
+ <UtilLink
44
+ v-for="(article, index) in list"
45
+ :key="index"
46
+ class="slide-item"
47
+ :title="article.description"
48
+ :to="article.path"
49
+ >
50
+ <NuxtImg class="cover" :src="article.image" :alt="compConf.showTitle ? '' : article.title" />
51
+
52
+ <div v-if="compConf.showTitle" class="stable-info text-creative">
53
+ {{ article.title }}
54
+ </div>
55
+
56
+ <div class="hover-info">
57
+ <div class="title text-creative">
58
+ {{ article.title }}
59
+ </div>
60
+ <UtilDate v-if="article.date" class="desc" :date="article.date" />
61
+ </div>
62
+ </UtilLink>
63
+ </div>
64
+
65
+ <ZButton
66
+ class="carousel-action prev at-slide-hover"
67
+ aria-label="上一页"
68
+ icon="tabler:chevron-left"
69
+ tabindex="-1"
70
+ @click="carouselApi?.scrollPrev()"
71
+ />
72
+
73
+ <ZButton
74
+ class="carousel-action next at-slide-hover"
75
+ aria-label="下一页"
76
+ icon="tabler:chevron-right"
77
+ tabindex="-1"
78
+ @click="carouselApi?.scrollNext()"
79
+ />
80
+ </div>
81
+ </div>
82
+ </template>
83
+
84
+ <style lang="scss" scoped>
85
85
  .z-slide {
86
86
  margin: 1rem;
87
87
 
@@ -212,4 +212,4 @@ useEventListener(carouselEl, 'wheel', (e) => {
212
212
  }
213
213
  }
214
214
  }
215
- </style>
215
+ </style>
@@ -1,62 +1,62 @@
1
- <script setup lang="ts">
2
- import { Temporal } from 'temporal-polyfill'
3
-
4
- const props = withDefaults(defineProps<{
5
- icon?: string
6
- date?: string | Temporal.ZonedDateTime
7
- format?: dateTimeFormatOptions
8
- absolute?: boolean
9
- relative?: boolean
10
- nospace?: boolean
11
- tipFormat?: dateTimeFormatOptions
12
- tipTransform?: (formattedDate: string) => string
13
- }>(), {
14
- tipTransform: String,
15
- })
16
-
17
- const today = Temporal.Now.plainDateISO()
18
- const zdt = computed(() => {
19
- try {
20
- return typeof props.date === 'string' ? toZonedTemporal(props.date) : props.date
21
- }
22
- catch {
23
- return null
24
- }
25
- })
26
-
27
- const relative = computed(() => props.absolute || !zdt.value
28
- ? false
29
- : props.relative || today.since(zdt.value, { largestUnit: 'week' }).weeks < 1,
30
- )
31
-
32
- const mounted = useMounted()
33
- const tooltip = computed(() => mounted.value && zdt.value
34
- ? props.tipTransform(toZdtLocaleString(zdt.value, props.tipFormat))
35
- : props.date as string,
36
- )
37
- </script>
38
-
39
- <template>
40
- <span :title="tooltip">
41
- <Icon v-if="icon" :name="icon" />
42
- <template v-if="icon && !nospace">&nbsp;</template>
43
-
44
- <span v-if="!zdt">Invalid Date</span>
45
-
46
- <time
47
- v-else-if="format"
48
- :datetime="toInstantString(zdt)"
49
- v-text="toZdtLocaleString(zdt, format)"
50
- />
51
-
52
- <NuxtTime
53
- v-else
54
- :datetime="toInstantString(zdt)"
55
- :relative
56
- :year="zdt.year === today.year ? undefined : '2-digit'"
57
- month="long"
58
- day="numeric"
59
- numeric="auto"
60
- />
61
- </span>
62
- </template>
1
+ <script setup lang="ts">
2
+ import { Temporal } from 'temporal-polyfill'
3
+
4
+ const props = withDefaults(defineProps<{
5
+ icon?: string
6
+ date?: string | Temporal.ZonedDateTime
7
+ format?: dateTimeFormatOptions
8
+ absolute?: boolean
9
+ relative?: boolean
10
+ nospace?: boolean
11
+ tipFormat?: dateTimeFormatOptions
12
+ tipTransform?: (formattedDate: string) => string
13
+ }>(), {
14
+ tipTransform: String,
15
+ })
16
+
17
+ const today = Temporal.Now.plainDateISO()
18
+ const zdt = computed(() => {
19
+ try {
20
+ return typeof props.date === 'string' ? toZonedTemporal(props.date) : props.date
21
+ }
22
+ catch {
23
+ return null
24
+ }
25
+ })
26
+
27
+ const relative = computed(() => props.absolute || !zdt.value
28
+ ? false
29
+ : props.relative || today.since(zdt.value, { largestUnit: 'week' }).weeks < 1,
30
+ )
31
+
32
+ const mounted = useMounted()
33
+ const tooltip = computed(() => mounted.value && zdt.value
34
+ ? props.tipTransform(toZdtLocaleString(zdt.value, props.tipFormat))
35
+ : props.date as string,
36
+ )
37
+ </script>
38
+
39
+ <template>
40
+ <span :title="tooltip">
41
+ <Icon v-if="icon" :name="icon" />
42
+ <template v-if="icon && !nospace">&nbsp;</template>
43
+
44
+ <span v-if="!zdt">Invalid Date</span>
45
+
46
+ <time
47
+ v-else-if="format"
48
+ :datetime="toInstantString(zdt)"
49
+ v-text="toZdtLocaleString(zdt, format)"
50
+ />
51
+
52
+ <NuxtTime
53
+ v-else
54
+ :datetime="toInstantString(zdt)"
55
+ :relative
56
+ :year="zdt.year === today.year ? undefined : '2-digit'"
57
+ month="long"
58
+ day="numeric"
59
+ numeric="auto"
60
+ />
61
+ </span>
62
+ </template>