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,33 +1,33 @@
1
- <script setup lang="ts">
2
- import type { VNodeChild } from 'vue'
3
-
4
- interface DlItem {
5
- label: string
6
- value: MaybeRefOrGetter<VNodeChild>
7
- tip?: MaybeRefOrGetter<string>
8
- }
9
-
10
- withDefaults(defineProps<{
11
- items: DlItem[]
12
- size?: 'small' | 'medium' | 'large'
13
- }>(), {
14
- size: 'medium',
15
- })
16
- </script>
17
-
18
- <template>
19
- <dl class="dl-group" :class="size">
20
- <div v-for="{ label, value, tip } in items" :key="label">
21
- <dt>{{ label }}</dt>
22
- <dd :title="toValue(tip)">
23
- <!-- 支持 string, Ref<String>, VNode, () => VNode -->
24
- <component :is="() => toValue(value)" />
25
- </dd>
26
- </div>
27
- </dl>
28
- </template>
29
-
30
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ import type { VNodeChild } from 'vue'
3
+
4
+ interface DlItem {
5
+ label: string
6
+ value: MaybeRefOrGetter<VNodeChild>
7
+ tip?: MaybeRefOrGetter<string>
8
+ }
9
+
10
+ withDefaults(defineProps<{
11
+ items: DlItem[]
12
+ size?: 'small' | 'medium' | 'large'
13
+ }>(), {
14
+ size: 'medium',
15
+ })
16
+ </script>
17
+
18
+ <template>
19
+ <dl class="dl-group" :class="size">
20
+ <div v-for="{ label, value, tip } in items" :key="label">
21
+ <dt>{{ label }}</dt>
22
+ <dd :title="toValue(tip)">
23
+ <!-- 支持 string, Ref<String>, VNode, () => VNode -->
24
+ <component :is="() => toValue(value)" />
25
+ </dd>
26
+ </div>
27
+ </dl>
28
+ </template>
29
+
30
+ <style lang="scss" scoped>
31
31
  .dl-group {
32
32
  > div {
33
33
  padding: 0.2em 0;
@@ -66,4 +66,4 @@ withDefaults(defineProps<{
66
66
  }
67
67
  }
68
68
  }
69
- </style>
69
+ </style>
@@ -1,23 +1,23 @@
1
- <!-- https://vue-tippy.netlify.app/props/ -->
2
- <!-- 如果要点击触发,请使用 trigger="focusin" 并添加 tabindex="0" -->
3
-
4
- <template>
5
- <Tooltip
6
- class="dropdown"
7
- interactive
8
- placement="bottom"
9
- :arrow="false"
10
- :hide-on-click="false"
11
- :offset="[0, 0]"
12
- >
13
- <slot />
14
- <template #content="{ hide }">
15
- <slot name="content" :hide />
16
- </template>
17
- </Tooltip>
18
- </template>
19
-
20
- <style lang="scss" scoped>
1
+ <!-- https://vue-tippy.netlify.app/props/ -->
2
+ <!-- 如果要点击触发,请使用 trigger="focusin" 并添加 tabindex="0" -->
3
+
4
+ <template>
5
+ <Tooltip
6
+ class="dropdown"
7
+ interactive
8
+ placement="bottom"
9
+ :arrow="false"
10
+ :hide-on-click="false"
11
+ :offset="[0, 0]"
12
+ >
13
+ <slot />
14
+ <template #content="{ hide }">
15
+ <slot name="content" :hide />
16
+ </template>
17
+ </Tooltip>
18
+ </template>
19
+
20
+ <style lang="scss" scoped>
21
21
  // https://vue-tippy.netlify.app/props#appendto
22
22
  // Tooltip 位于组件根部时,interactive tippy 会插入到父组件
23
23
  :deep() ~ [data-tippy-root] > .tippy-box {
@@ -51,4 +51,4 @@
51
51
  }
52
52
  }
53
53
  }
54
- </style>
54
+ </style>
@@ -1,41 +1,41 @@
1
- <script setup lang="ts">
2
- import type { BundledLanguage } from 'shiki'
3
-
4
- withDefaults(defineProps<{
5
- icon?: string
6
- title?: string
7
- message?: string
8
- code?: string
9
- language?: BundledLanguage
10
- }>(), {
11
- icon: 'line-md:cloud-alt-print-twotone-loop',
12
- language: 'log',
13
- })
14
- </script>
15
-
16
- <template>
17
- <div class="error proper-height">
18
- <div />
19
- <Icon class="error-icon" :name="icon" />
20
- <div class="error-title" v-html="title" />
21
-
22
- <div class="operation">
23
- <slot name="operation" />
24
- </div>
25
-
26
- <ProsePre
27
- v-if="code"
28
- :filename="message"
29
- :language
30
- :code
31
- meta="wrap"
32
- />
33
-
34
- <slot />
35
- </div>
36
- </template>
37
-
38
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ import type { BundledLanguage } from 'shiki'
3
+
4
+ withDefaults(defineProps<{
5
+ icon?: string
6
+ title?: string
7
+ message?: string
8
+ code?: string
9
+ language?: BundledLanguage
10
+ }>(), {
11
+ icon: 'line-md:cloud-alt-print-twotone-loop',
12
+ language: 'log',
13
+ })
14
+ </script>
15
+
16
+ <template>
17
+ <div class="error proper-height">
18
+ <div />
19
+ <Icon class="error-icon" :name="icon" />
20
+ <div class="error-title" v-html="title" />
21
+
22
+ <div class="operation">
23
+ <slot name="operation" />
24
+ </div>
25
+
26
+ <ProsePre
27
+ v-if="code"
28
+ :filename="message"
29
+ :language
30
+ :code
31
+ meta="wrap"
32
+ />
33
+
34
+ <slot />
35
+ </div>
36
+ </template>
37
+
38
+ <style lang="scss" scoped>
39
39
  .error {
40
40
  display: flex;
41
41
  flex-direction: column;
@@ -67,4 +67,4 @@ withDefaults(defineProps<{
67
67
  }
68
68
  }
69
69
  }
70
- </style>
70
+ </style>
@@ -1,28 +1,28 @@
1
- <script setup lang="ts">
2
- defineProps<{
3
- name?: string
4
- inPlace?: boolean
5
- }>()
6
-
7
- const expand = defineModel<boolean>()
8
- </script>
9
-
10
- <template>
11
- <div class="z-expand">
12
- <Transition name="collapse">
13
- <div v-show="expand" class="expand-content">
14
- <slot />
15
- </div>
16
- </Transition>
17
-
18
- <button class="toggle-btn" :class="{ 'in-place': inPlace }" @click="expand = !expand">
19
- <Icon class="toggle-icon" :class="{ expand }" name="tabler:chevrons-down" />
20
- <span>{{ `${expand ? '收起' : '展开'}${name}` }}</span>
21
- </button>
22
- </div>
23
- </template>
24
-
25
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ name?: string
4
+ inPlace?: boolean
5
+ }>()
6
+
7
+ const expand = defineModel<boolean>()
8
+ </script>
9
+
10
+ <template>
11
+ <div class="z-expand">
12
+ <Transition name="collapse">
13
+ <div v-show="expand" class="expand-content">
14
+ <slot />
15
+ </div>
16
+ </Transition>
17
+
18
+ <button class="toggle-btn" :class="{ 'in-place': inPlace }" @click="expand = !expand">
19
+ <Icon class="toggle-icon" :class="{ expand }" name="tabler:chevrons-down" />
20
+ <span>{{ `${expand ? '收起' : '展开'}${name}` }}</span>
21
+ </button>
22
+ </div>
23
+ </template>
24
+
25
+ <style lang="scss" scoped>
26
26
  .z-expand {
27
27
  display: flex;
28
28
  flex-direction: column;
@@ -48,4 +48,4 @@ const expand = defineModel<boolean>()
48
48
  transform: scaleY(-1);
49
49
  }
50
50
  }
51
- </style>
51
+ </style>
@@ -1,26 +1,26 @@
1
- <script setup lang="ts">
2
- import type { NavItem } from '../../types/nav'
3
-
4
- defineProps<{
5
- list: NavItem[]
6
- }>()
7
- </script>
8
-
9
- <template>
10
- <menu>
11
- <UtilLink
12
- v-for="{ text, icon, url } in list"
13
- :key="text"
14
- v-tip="text"
15
- :to="url"
16
- :aria-label="text"
17
- >
18
- <Icon :name="icon" />
19
- </UtilLink>
20
- </menu>
21
- </template>
22
-
23
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ import type { NavItem } from '../../types/nav'
3
+
4
+ defineProps<{
5
+ list: NavItem[]
6
+ }>()
7
+ </script>
8
+
9
+ <template>
10
+ <menu>
11
+ <UtilLink
12
+ v-for="{ text, icon, url } in list"
13
+ :key="text"
14
+ v-tip="text"
15
+ :to="url"
16
+ :aria-label="text"
17
+ >
18
+ <Icon :name="icon" />
19
+ </UtilLink>
20
+ </menu>
21
+ </template>
22
+
23
+ <style lang="scss" scoped>
24
24
  menu {
25
25
  display: flex;
26
26
  justify-content: center;
@@ -39,4 +39,4 @@ menu {
39
39
  }
40
40
  }
41
41
  }
42
- </style>
42
+ </style>
@@ -1,64 +1,64 @@
1
- <script setup lang="ts">
2
- const props = defineProps<{
3
- totalPages: number
4
- expandPages?: number
5
- sticky?: boolean
6
- avoid?: boolean
7
- }>()
8
-
9
- const page = defineModel<number>({ required: true })
10
- const pageArr = computed(() => getPaginationIndicator(page.value, props.totalPages, props.expandPages ?? 2))
11
-
12
- const paginationEl = useTemplateRef('pagination')
13
- const anchorEl = useTemplateRef('pagination-anchor')
14
- const expand = useElementVisibility(anchorEl)
15
-
16
- if (props.avoid) {
17
- useAvoidTarget(paginationEl, toRef(props, 'avoid'))
18
- }
19
- </script>
20
-
21
- <template>
22
- <nav
23
- ref="pagination"
24
- class="pagination"
25
- :class="{ sticky, expand }"
26
- :aria-label="`第${page}页,共${totalPages}页`"
27
- :style="{ '--collapsed-width': `${pageArr.length * 2 + 6}em` }"
28
- >
29
- <ZButton
30
- :disabled="page <= 1"
31
- class="pagination-button rtl-flip"
32
- icon="tabler:arrow-left"
33
- aria-label="上一页"
34
- @click="page--"
35
- />
36
- <template v-for="i in pageArr" :key="i">
37
- <button
38
- v-if="Number.isFinite(i)"
39
- class="pagination-num"
40
- :class="{ active: i === page }"
41
- :aria-label="`第${i}页`"
42
- @click="page = i"
43
- v-text="i"
44
- />
45
- <!-- TODO: 点击后自主选择目标页面 -->
46
- <button v-else disabled class="pagination-num">
47
- …
48
- </button>
49
- </template>
50
- <ZButton
51
- :disabled="page >= totalPages"
52
- class="pagination-button rtl-flip"
53
- icon="tabler:arrow-right"
54
- aria-label="下一页"
55
- @click="page++"
56
- />
57
- </nav>
58
- <div ref="pagination-anchor" />
59
- </template>
60
-
61
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ const props = defineProps<{
3
+ totalPages: number
4
+ expandPages?: number
5
+ sticky?: boolean
6
+ avoid?: boolean
7
+ }>()
8
+
9
+ const page = defineModel<number>({ required: true })
10
+ const pageArr = computed(() => getPaginationIndicator(page.value, props.totalPages, props.expandPages ?? 2))
11
+
12
+ const paginationEl = useTemplateRef('pagination')
13
+ const anchorEl = useTemplateRef('pagination-anchor')
14
+ const expand = useElementVisibility(anchorEl)
15
+
16
+ if (props.avoid) {
17
+ useAvoidTarget(paginationEl, toRef(props, 'avoid'))
18
+ }
19
+ </script>
20
+
21
+ <template>
22
+ <nav
23
+ ref="pagination"
24
+ class="pagination"
25
+ :class="{ sticky, expand }"
26
+ :aria-label="`第${page}页,共${totalPages}页`"
27
+ :style="{ '--collapsed-width': `${pageArr.length * 2 + 6}em` }"
28
+ >
29
+ <ZButton
30
+ :disabled="page <= 1"
31
+ class="pagination-button rtl-flip"
32
+ icon="tabler:arrow-left"
33
+ aria-label="上一页"
34
+ @click="page--"
35
+ />
36
+ <template v-for="i in pageArr" :key="i">
37
+ <button
38
+ v-if="Number.isFinite(i)"
39
+ class="pagination-num"
40
+ :class="{ active: i === page }"
41
+ :aria-label="`第${i}页`"
42
+ @click="page = i"
43
+ v-text="i"
44
+ />
45
+ <!-- TODO: 点击后自主选择目标页面 -->
46
+ <button v-else disabled class="pagination-num">
47
+ …
48
+ </button>
49
+ </template>
50
+ <ZButton
51
+ :disabled="page >= totalPages"
52
+ class="pagination-button rtl-flip"
53
+ icon="tabler:arrow-right"
54
+ aria-label="下一页"
55
+ @click="page++"
56
+ />
57
+ </nav>
58
+ <div ref="pagination-anchor" />
59
+ </template>
60
+
61
+ <style lang="scss" scoped>
62
62
  .pagination {
63
63
  display: flex;
64
64
  max-width: calc(100vw);
@@ -110,4 +110,4 @@ if (props.avoid) {
110
110
  }
111
111
  }
112
112
  }
113
- </style>
113
+ </style>
@@ -1,32 +1,32 @@
1
- <script setup lang="ts" generic="T">
2
- interface RadioItem {
3
- label?: string
4
- value: T
5
- }
6
-
7
- defineProps<{
8
- items: RadioItem[]
9
- }>()
10
-
11
- const modelValue = defineModel<T>()
12
- </script>
13
-
14
- <template>
15
- <div class="z-radio-group">
16
- <button
17
- v-for="{ label, value }, i in items"
18
- :key="i"
19
- type="button"
20
- class="radio-item"
21
- :class="{ active: value === modelValue }"
22
- @click="modelValue = value"
23
- >
24
- {{ label || value }}
25
- </button>
26
- </div>
27
- </template>
28
-
29
- <style lang="scss" scoped>
1
+ <script setup lang="ts" generic="T">
2
+ interface RadioItem {
3
+ label?: string
4
+ value: T
5
+ }
6
+
7
+ defineProps<{
8
+ items: RadioItem[]
9
+ }>()
10
+
11
+ const modelValue = defineModel<T>()
12
+ </script>
13
+
14
+ <template>
15
+ <div class="z-radio-group">
16
+ <button
17
+ v-for="{ label, value }, i in items"
18
+ :key="i"
19
+ type="button"
20
+ class="radio-item"
21
+ :class="{ active: value === modelValue }"
22
+ @click="modelValue = value"
23
+ >
24
+ {{ label || value }}
25
+ </button>
26
+ </div>
27
+ </template>
28
+
29
+ <style lang="scss" scoped>
30
30
  .z-radio-group {
31
31
  display: flex;
32
32
  margin: 1em 0;
@@ -74,4 +74,4 @@ const modelValue = defineModel<T>()
74
74
  border-inline-start: 1px solid var(--c-bg-soft);
75
75
  }
76
76
  }
77
- </style>
77
+ </style>
@@ -1,13 +1,13 @@
1
- <template>
2
- <!-- 外层元素用于占位 -->
3
- <div class="secret-container">
4
- <div class="secret">
5
- <slot />
6
- </div>
7
- </div>
8
- </template>
9
-
10
- <style lang="scss" scoped>
1
+ <template>
2
+ <!-- 外层元素用于占位 -->
3
+ <div class="secret-container">
4
+ <div class="secret">
5
+ <slot />
6
+ </div>
7
+ </div>
8
+ </template>
9
+
10
+ <style lang="scss" scoped>
11
11
  .secret {
12
12
  position: relative;
13
13
  opacity: 0;
@@ -20,4 +20,4 @@
20
20
  z-index: 0;
21
21
  }
22
22
  }
23
- </style>
23
+ </style>
@@ -1,50 +1,50 @@
1
- <script setup lang="ts">
2
- const props = defineProps<{
3
- label?: string
4
- springMin?: number
5
- springMax?: number
6
- list?: (string | { value: string, label?: string })[]
7
- }>()
8
-
9
- const modelValue = defineModel<number>({ required: true })
10
- const initialValue = modelValue.value
11
- const id = useId()
12
- const list = computed(() => props.list?.map(v => typeof v === 'string' ? { value: v } : v))
13
-
14
- function spring() {
15
- if (props.springMin !== undefined && modelValue.value < props.springMin) {
16
- modelValue.value = props.springMin
17
- }
18
- if (props.springMax !== undefined && modelValue.value > props.springMax) {
19
- modelValue.value = props.springMax
20
- }
21
- }
22
- const debounceSpring = useDebounceFn(spring, 500)
23
- </script>
24
-
25
- <template>
26
- <label class="z-slider">
27
- <slot><span v-if="label" v-text="label" /></slot>
28
-
29
- <input
30
- v-bind="$attrs"
31
- v-model.number="modelValue"
32
- class="input-slider"
33
- type="range"
34
- :list="id"
35
- @change="debounceSpring"
36
- @dblclick="modelValue = initialValue"
37
- >
38
-
39
- <data class="data-value" :value="modelValue" v-text="modelValue" />
40
-
41
- <datalist v-if="list" :id="id">
42
- <option v-for="{ value, label: text } in list" :key="value" :value :label="text" />
43
- </datalist>
44
- </label>
45
- </template>
46
-
47
- <style lang="scss" scoped>
1
+ <script setup lang="ts">
2
+ const props = defineProps<{
3
+ label?: string
4
+ springMin?: number
5
+ springMax?: number
6
+ list?: (string | { value: string, label?: string })[]
7
+ }>()
8
+
9
+ const modelValue = defineModel<number>({ required: true })
10
+ const initialValue = modelValue.value
11
+ const id = useId()
12
+ const list = computed(() => props.list?.map(v => typeof v === 'string' ? { value: v } : v))
13
+
14
+ function spring() {
15
+ if (props.springMin !== undefined && modelValue.value < props.springMin) {
16
+ modelValue.value = props.springMin
17
+ }
18
+ if (props.springMax !== undefined && modelValue.value > props.springMax) {
19
+ modelValue.value = props.springMax
20
+ }
21
+ }
22
+ const debounceSpring = useDebounceFn(spring, 500)
23
+ </script>
24
+
25
+ <template>
26
+ <label class="z-slider">
27
+ <slot><span v-if="label" v-text="label" /></slot>
28
+
29
+ <input
30
+ v-bind="$attrs"
31
+ v-model.number="modelValue"
32
+ class="input-slider"
33
+ type="range"
34
+ :list="id"
35
+ @change="debounceSpring"
36
+ @dblclick="modelValue = initialValue"
37
+ >
38
+
39
+ <data class="data-value" :value="modelValue" v-text="modelValue" />
40
+
41
+ <datalist v-if="list" :id="id">
42
+ <option v-for="{ value, label: text } in list" :key="value" :value :label="text" />
43
+ </datalist>
44
+ </label>
45
+ </template>
46
+
47
+ <style lang="scss" scoped>
48
48
  .z-slider {
49
49
  display: flex;
50
50
  align-items: center;
@@ -93,4 +93,4 @@ const debounceSpring = useDebounceFn(spring, 500)
93
93
  min-width: var(--slider-value, 3ch);
94
94
  text-align: right;
95
95
  }
96
- </style>
96
+ </style>