@wot-ui/vitepress-theme 2.0.0-alpha.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.
Files changed (57) hide show
  1. package/README.md +153 -0
  2. package/dist/config.js +118 -0
  3. package/dist/index.js +68 -0
  4. package/dist/locales/md-component-links.js +15 -0
  5. package/dist/plugins/md-component-links.js +34 -0
  6. package/dist/plugins/md-scss-vars.js +73 -0
  7. package/dist/plugins/md-version-badge.js +51 -0
  8. package/dist/plugins/virtual-version-data.js +83 -0
  9. package/dist/src/config.d.ts +3 -0
  10. package/dist/src/index.d.ts +21 -0
  11. package/dist/src/locales/md-component-links.d.ts +13 -0
  12. package/dist/src/plugins/md-component-links.d.ts +3 -0
  13. package/dist/src/plugins/md-scss-vars.d.ts +3 -0
  14. package/dist/src/plugins/md-version-badge.d.ts +6 -0
  15. package/dist/src/plugins/virtual-version-data.d.ts +3 -0
  16. package/dist/src/theme/composables/adSponsor.d.ts +30 -0
  17. package/dist/src/theme/composables/adsData.d.ts +16 -0
  18. package/dist/src/theme/composables/banner.d.ts +16 -0
  19. package/dist/src/theme/composables/cases.d.ts +16 -0
  20. package/dist/src/theme/composables/friendly.d.ts +19 -0
  21. package/dist/src/theme/composables/sponsor.d.ts +3 -0
  22. package/dist/src/theme/composables/team.d.ts +28 -0
  23. package/dist/src/theme/options.d.ts +3 -0
  24. package/dist/src/types.d.ts +86 -0
  25. package/dist/theme/Layout.vue +45 -0
  26. package/dist/theme/components/AsideSponsors.vue +105 -0
  27. package/dist/theme/components/Banner.vue +377 -0
  28. package/dist/theme/components/CustomFooter.vue +123 -0
  29. package/dist/theme/components/ExternalLink.vue +36 -0
  30. package/dist/theme/components/HomeCases.vue +122 -0
  31. package/dist/theme/components/HomeFriendly.vue +96 -0
  32. package/dist/theme/components/HomeTeam.vue +250 -0
  33. package/dist/theme/components/QrCode.vue +45 -0
  34. package/dist/theme/components/SidebarAds.vue +86 -0
  35. package/dist/theme/components/SpecialSponsor.vue +115 -0
  36. package/dist/theme/components/SvgImage.vue +22 -0
  37. package/dist/theme/components/VPContent.vue +92 -0
  38. package/dist/theme/components/VPDoc.vue +222 -0
  39. package/dist/theme/components/VPFeature.vue +150 -0
  40. package/dist/theme/components/VPIframe.vue +445 -0
  41. package/dist/theme/components/VPLocalNav.vue +151 -0
  42. package/dist/theme/components/VPNavBar.vue +253 -0
  43. package/dist/theme/components/VPSidebar.vue +120 -0
  44. package/dist/theme/components/VPSidebarItem.vue +271 -0
  45. package/dist/theme/components/WwAds.vue +74 -0
  46. package/dist/theme/composables/adSponsor.js +29 -0
  47. package/dist/theme/composables/adsData.js +35 -0
  48. package/dist/theme/composables/banner.js +35 -0
  49. package/dist/theme/composables/cases.js +43 -0
  50. package/dist/theme/composables/friendly.js +35 -0
  51. package/dist/theme/composables/sponsor.js +35 -0
  52. package/dist/theme/composables/team.js +48 -0
  53. package/dist/theme/options.js +4 -0
  54. package/dist/theme/styles/custom.css +206 -0
  55. package/dist/theme/styles/vars.css +136 -0
  56. package/dist/types.js +0 -0
  57. package/package.json +41 -0
@@ -0,0 +1,445 @@
1
+ <template>
2
+ <ClientOnly>
3
+ <!-- 大屏幕:原有的固定容器 -->
4
+ <div
5
+ v-if="href && !isSmallScreen"
6
+ class="demo-model"
7
+ :class="{
8
+ collapsed: !expanded,
9
+ 'transition-end': transitionEnd
10
+ }"
11
+ @transitionend="onTransitionEnd"
12
+ >
13
+ <!-- 头部控制栏 -->
14
+ <div class="demo-header">
15
+ <ExternalLink :href="href" class="demo-link" :style="`${expanded ? '' : 'height:0;width:0;opacity:0'}`"></ExternalLink>
16
+ <QrCode class="demo-qrcode" :src="qrcode" v-if="expanded && qrcode"></QrCode>
17
+ <el-icon class="expand-icon" style="cursor: pointer" @click="toggleExpand">
18
+ <component :is="expanded ? Fold : Expand" />
19
+ </el-icon>
20
+ </div>
21
+ <!-- iframe 容器 -->
22
+ <div class="iframe-container">
23
+ <iframe v-if="expanded && transitionEnd" ref="iframe" id="demo" class="iframe" scrolling="auto" frameborder="0" :src="href" />
24
+ </div>
25
+ </div>
26
+
27
+ <!-- 小屏幕:触发按钮 -->
28
+ <div v-if="href && isSmallScreen && !isTinyScreen" class="demo-trigger" @click="openDrawer">
29
+ <el-icon class="trigger-icon">
30
+ <component :is="Expand" />
31
+ </el-icon>
32
+ </div>
33
+
34
+ <!-- 小屏幕:Drawer抽屉 -->
35
+ <view v-if="isSmallScreen && !isTinyScreen" class="drawer-container">
36
+ <el-drawer v-model="drawerVisible" direction="rtl" size="380px" :modal="false" :lock-scroll="false" :before-close="closeDrawer">
37
+ <div class="demo-header">
38
+ <ExternalLink :href="href" class="demo-link"></ExternalLink>
39
+ <QrCode class="demo-qrcode" :src="qrcode" v-if="qrcode"></QrCode>
40
+ </div>
41
+ <div class="drawer-content">
42
+ <iframe v-if="drawerVisible" ref="drawerIframe" class="drawer-iframe" scrolling="auto" frameborder="0" :src="href" />
43
+ </div>
44
+ </el-drawer>
45
+ </view>
46
+ </ClientOnly>
47
+ </template>
48
+
49
+ <script setup lang="ts">
50
+ import { Expand, Fold } from '@element-plus/icons-vue'
51
+ import { ElDrawer, ElIcon } from 'element-plus'
52
+ import { useRoute, useData } from 'vitepress'
53
+ import { computed, inject, onMounted, onUnmounted, ref, watch } from 'vue'
54
+ import QrCode from './QrCode.vue'
55
+ import { wotThemeOptionsKey } from '../options'
56
+
57
+ interface Props {
58
+ /** 是否展开状态 */
59
+ expanded?: boolean
60
+ }
61
+
62
+ const props = withDefaults(defineProps<Props>(), {
63
+ expanded: true
64
+ })
65
+
66
+ // 状态管理
67
+ const baseUrl = ref('')
68
+ const iframe = ref<HTMLIFrameElement | null>(null)
69
+ const drawerIframe = ref<HTMLIFrameElement | null>(null)
70
+ const transitionEnd = ref(true)
71
+ const drawerVisible = ref(false)
72
+ const isSmallScreen = ref(false)
73
+ const isTinyScreen = ref(false)
74
+
75
+ const emit = defineEmits<{
76
+ 'update:expanded': [boolean] // 更新展开状态
77
+ 'state-change': [boolean] // 状态变化通知
78
+ }>()
79
+
80
+ const route = useRoute()
81
+ const vitepressData = useData()
82
+ const themeOptions = inject(wotThemeOptionsKey)
83
+ const demoIframeOptions = themeOptions?.demoIframe
84
+ const assetBase = demoIframeOptions && demoIframeOptions.assetBase ? demoIframeOptions.assetBase : '/wxqrcode'
85
+
86
+ const href = computed(() => {
87
+ const path = route.path
88
+ const paths = path ? path.split('.')[0].split('/') : []
89
+
90
+ if (!paths.length) return baseUrl.value
91
+
92
+ return baseUrl.value + `subPages/${kebabToCamel(paths[paths.length - 1])}/Index`
93
+ })
94
+
95
+ const qrcode = computed(() => {
96
+ const path = route.path
97
+ const paths = path ? path.split('.')[0].split('/') : []
98
+ if (!paths.length) return ''
99
+
100
+ return `${assetBase}/${paths[paths.length - 1]}.png`
101
+ })
102
+
103
+ // 获取页面标题
104
+ const pageTitle = computed(() => {
105
+ const path = route.path
106
+ const paths = path ? path.split('.')[0].split('/') : []
107
+ if (!paths.length) return '预览Demo'
108
+
109
+ // 将kebab-case转换为更友好的标题格式
110
+ const pageName = paths[paths.length - 1]
111
+ return pageName
112
+ .split('-')
113
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
114
+ .join(' ')
115
+ })
116
+
117
+ // 工具函数:转换 kebab-case 为 camelCase
118
+ function kebabToCamel(input: string): string {
119
+ return input.replace(/-([a-z])/g, (match, group) => group.toUpperCase())
120
+ }
121
+
122
+ // 监听 props.expanded 变化
123
+ watch(
124
+ () => props.expanded,
125
+ (newVal) => {
126
+ if (newVal) {
127
+ transitionEnd.value = false
128
+ }
129
+ }
130
+ )
131
+
132
+ // 展开/收起控制
133
+ function toggleExpand() {
134
+ // 触发事件通知父组件
135
+ emit('update:expanded', !props.expanded)
136
+ emit('state-change', !props.expanded)
137
+ transitionEnd.value = false
138
+ }
139
+
140
+ // 小屏幕抽屉控制
141
+ function openDrawer() {
142
+ drawerVisible.value = true
143
+ }
144
+
145
+ function closeDrawer() {
146
+ drawerVisible.value = false
147
+ }
148
+
149
+ // 检查屏幕尺寸
150
+ function checkScreenSize() {
151
+ isSmallScreen.value = window.innerWidth <= 1439
152
+ isTinyScreen.value = window.innerWidth < 1280
153
+ }
154
+
155
+ // 监听窗口大小变化
156
+ function handleResize() {
157
+ checkScreenSize()
158
+ }
159
+
160
+ // 过渡结束处理
161
+ function onTransitionEnd() {
162
+ transitionEnd.value = true
163
+ }
164
+
165
+ // iframe 消息通信
166
+ function sendMessage() {
167
+ const targetIframe = isSmallScreen.value ? drawerIframe.value : iframe.value
168
+ if (targetIframe?.contentWindow) {
169
+ const targetOrigin = new URL(href.value, location.origin).origin
170
+ targetIframe.contentWindow.postMessage(vitepressData.isDark.value, targetOrigin)
171
+ }
172
+ }
173
+
174
+ // 发送语言信息给iframe
175
+ function sendLanguageMessage() {
176
+ const targetIframe = isSmallScreen.value ? drawerIframe.value : iframe.value
177
+ if (targetIframe?.contentWindow) {
178
+ const targetOrigin = new URL(href.value, location.origin).origin
179
+ targetIframe.contentWindow.postMessage(vitepressData.lang.value, targetOrigin)
180
+ }
181
+ }
182
+
183
+ onMounted(() => {
184
+ baseUrl.value =
185
+ process.env.NODE_ENV === 'production' ? `${location.origin}/demo/?timestamp=${new Date().getTime()}#/` : 'http://localhost:5173/demo/#/'
186
+
187
+ // 初始化屏幕尺寸检查
188
+ checkScreenSize()
189
+ window.addEventListener('resize', handleResize)
190
+
191
+ // 监听 iframe 加载完成事件
192
+ iframe.value?.addEventListener('load', () => {
193
+ sendMessage()
194
+ sendLanguageMessage()
195
+ })
196
+ })
197
+
198
+ onUnmounted(() => {
199
+ window.removeEventListener('resize', handleResize)
200
+ })
201
+
202
+ // 监听抽屉iframe加载完成
203
+ watch(drawerIframe, (newVal) => {
204
+ if (newVal) {
205
+ newVal.addEventListener('load', () => {
206
+ sendMessage()
207
+ sendLanguageMessage()
208
+ })
209
+ }
210
+ })
211
+
212
+ watch(() => vitepressData.isDark.value, sendMessage)
213
+
214
+ // 监听语言变化
215
+ watch(() => vitepressData.lang.value, sendLanguageMessage)
216
+ </script>
217
+
218
+ <style scoped>
219
+ ::-webkit-scrollbar {
220
+ width: 0;
221
+ height: 0;
222
+ }
223
+
224
+ .demo-model {
225
+ position: fixed;
226
+ z-index: 10;
227
+ right: 32px;
228
+ top: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 32px);
229
+ width: 330px;
230
+ font-size: 16px;
231
+ background: var(--vp-c-bg-alt);
232
+ border-radius: 12px;
233
+ box-shadow: var(--vp-shadow-4);
234
+ overflow: hidden;
235
+ transition: all 0.3s ease-in-out;
236
+ }
237
+
238
+ .iframe-container {
239
+ height: calc(100% - 56px);
240
+ overflow: hidden;
241
+ transition: all 0.3s ease-in-out;
242
+ }
243
+
244
+ .collapsed .iframe-container {
245
+ height: 0;
246
+ }
247
+
248
+ .fade-enter-active,
249
+ .fade-leave-active,
250
+ .fade-enter,
251
+ .fade-leave-to {
252
+ display: none;
253
+ }
254
+
255
+ .iframe {
256
+ height: 100%;
257
+ width: 100%;
258
+ }
259
+
260
+ .demo-header {
261
+ position: relative;
262
+ height: 48px;
263
+ width: 100%;
264
+ padding: 8px 12px;
265
+ border-radius: 6px 6px 0px 0px;
266
+ box-sizing: border-box;
267
+ background: var(--vp-sidebar-bg-color);
268
+ margin-bottom: 8px;
269
+ display: flex;
270
+ align-items: center;
271
+ font-size: 28px;
272
+ cursor: pointer;
273
+ }
274
+
275
+ .demo-link {
276
+ font-size: 28px !important;
277
+ transition: all 0.3s ease-in-out;
278
+ position: absolute;
279
+ left: 0;
280
+ --color: inherit;
281
+ fill: currentColor;
282
+ color: var(--color);
283
+ }
284
+
285
+ .demo-qrcode {
286
+ font-size: 28px !important;
287
+ transition: all 0.3s ease-in-out;
288
+ position: absolute;
289
+ left: calc(50% - 14px);
290
+ --color: inherit;
291
+ fill: currentColor;
292
+ color: var(--color);
293
+ }
294
+
295
+ .expand-icon {
296
+ position: absolute;
297
+ right: 8px;
298
+ cursor: pointer;
299
+ }
300
+
301
+ .collapsed {
302
+ width: 48px !important;
303
+ height: 48px;
304
+ border-radius: 12px;
305
+ }
306
+
307
+ .collapsed.transition-end .demo-header {
308
+ justify-content: center;
309
+ /* 动画结束后居中对齐 */
310
+ }
311
+
312
+ .fade-enter-active,
313
+ .fade-leave-active {
314
+ transition: opacity 0.3s;
315
+ }
316
+
317
+ .fade-enter,
318
+ .fade-leave-to
319
+
320
+ /* .fade-leave-active in <2.1.8 */ {
321
+ opacity: 0;
322
+ }
323
+
324
+ @media screen and (min-width: 1440px) {
325
+ .demo-model {
326
+ width: 280px;
327
+ height: calc(320px * 143.6 / 70.9);
328
+ right: 12px;
329
+ }
330
+
331
+ .collapsed {
332
+ height: 48px;
333
+ }
334
+ }
335
+
336
+ @media screen and (min-width: 1600px) {
337
+ .demo-model {
338
+ width: 340px;
339
+ height: calc(340px * 143.6 / 70.9);
340
+ right: 24px;
341
+ }
342
+
343
+ .collapsed {
344
+ height: 48px;
345
+ }
346
+ }
347
+
348
+ /* 小屏幕触发按钮样式 */
349
+ .demo-trigger {
350
+ position: fixed;
351
+ z-index: 10;
352
+ right: 16px;
353
+ top: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 32px);
354
+ width: 48px;
355
+ height: 48px;
356
+ background: var(--vp-c-bg-alt);
357
+ border-radius: 12px;
358
+ box-shadow: var(--vp-shadow-4);
359
+ display: flex;
360
+ align-items: center;
361
+ justify-content: center;
362
+ cursor: pointer;
363
+ transition: all 0.3s ease-in-out;
364
+ }
365
+
366
+ .demo-trigger:hover {
367
+ transform: scale(1.05);
368
+ }
369
+
370
+ .trigger-icon {
371
+ font-size: 24px;
372
+ color: var(--vp-c-text-1);
373
+ }
374
+
375
+ /* 抽屉内容样式 */
376
+ .drawer-header {
377
+ display: flex;
378
+ align-items: center;
379
+ justify-content: space-between;
380
+ padding: 16px 0;
381
+ border-bottom: 1px solid var(--vp-c-divider);
382
+ margin-bottom: 16px;
383
+ }
384
+
385
+ .drawer-title {
386
+ font-size: 18px;
387
+ font-weight: 600;
388
+ color: var(--vp-c-text-1);
389
+ flex: 1;
390
+ }
391
+
392
+ .drawer-actions {
393
+ display: flex;
394
+ align-items: center;
395
+ gap: 12px;
396
+ }
397
+
398
+ .drawer-actions .demo-link {
399
+ font-size: 20px !important;
400
+ color: var(--vp-c-text-2);
401
+ transition: color 0.3s ease;
402
+ }
403
+
404
+ .drawer-actions .demo-link:hover {
405
+ color: var(--vp-c-brand-1);
406
+ }
407
+
408
+ .drawer-actions .demo-qrcode {
409
+ font-size: 20px !important;
410
+ color: var(--vp-c-text-2);
411
+ }
412
+
413
+ .drawer-content {
414
+ display: flex;
415
+ flex-direction: column;
416
+ align-items: center;
417
+ width: 340px;
418
+ height: calc(340px * 143.6 / 70.9);
419
+ }
420
+
421
+ .drawer-iframe {
422
+ width: 340px;
423
+ height: calc(340px * 143.6 / 70.9);
424
+ border: none;
425
+ border-radius: 20px;
426
+ box-shadow: var(--vp-shadow-3);
427
+ background: var(--vp-c-bg);
428
+ overflow: hidden;
429
+ }
430
+
431
+ .drawer-container {
432
+ --el-drawer-bg-color: var(--vp-c-bg);
433
+ :deep() {
434
+ .el-drawer {
435
+ background-color: var(--vp-c-bg) !important;
436
+ }
437
+ }
438
+ }
439
+
440
+ @media (max-width: 768px) {
441
+ .demo-trigger {
442
+ display: none;
443
+ }
444
+ }
445
+ </style>
@@ -0,0 +1,151 @@
1
+ <script lang="ts" setup>
2
+ import { onContentUpdated } from 'vitepress'
3
+ import { useWindowScroll } from '@vueuse/core'
4
+ import { computed, onMounted, ref } from 'vue'
5
+ import VPLocalNavOutlineDropdown from 'vitepress/dist/client/theme-default/components/VPLocalNavOutlineDropdown.vue'
6
+ import { useData } from 'vitepress'
7
+ import { useLayout } from 'vitepress/theme'
8
+ import { getHeaders } from 'vitepress/dist/client/theme-default/composables/outline.js'
9
+
10
+ defineProps<{
11
+ open: boolean
12
+ }>()
13
+
14
+ defineEmits<{
15
+ (e: 'open-menu'): void
16
+ }>()
17
+
18
+ const { theme, frontmatter }: any = useData()
19
+ const { hasSidebar, headers: layoutHeaders } = useLayout()
20
+ const { y } = useWindowScroll()
21
+
22
+ const navHeight = ref(0)
23
+ const headers = ref(layoutHeaders.value)
24
+
25
+ onMounted(() => {
26
+ navHeight.value = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--vp-nav-height'))
27
+ })
28
+
29
+ onContentUpdated(() => {
30
+ headers.value = getHeaders(frontmatter.value.outline ?? theme.value.outline)
31
+ })
32
+
33
+ const empty = computed(() => {
34
+ return headers.value.length === 0
35
+ })
36
+
37
+ const emptyAndNoSidebar = computed(() => {
38
+ return empty.value && !hasSidebar.value
39
+ })
40
+
41
+ const classes = computed(() => {
42
+ return {
43
+ VPLocalNav: true,
44
+ 'has-sidebar': hasSidebar.value,
45
+ empty: empty.value,
46
+ fixed: emptyAndNoSidebar.value
47
+ }
48
+ })
49
+ </script>
50
+
51
+ <template>
52
+ <div v-if="frontmatter.layout !== 'home' && (!emptyAndNoSidebar || y >= navHeight)" :class="classes">
53
+ <div class="container">
54
+ <button v-if="hasSidebar" class="menu" :aria-expanded="open" aria-controls="VPSidebarNav" @click="$emit('open-menu')">
55
+ <span class="vpi-align-left menu-icon"></span>
56
+ <span class="menu-text">
57
+ {{ theme.sidebarMenuLabel || 'Menu' }}
58
+ </span>
59
+ </button>
60
+
61
+ <VPLocalNavOutlineDropdown :headers="headers" :navHeight="navHeight" />
62
+ </div>
63
+ </div>
64
+ </template>
65
+
66
+ <style scoped>
67
+ .VPLocalNav {
68
+ position: sticky;
69
+ top: 0;
70
+ /*rtl:ignore*/
71
+ left: 0;
72
+ z-index: var(--vp-z-index-local-nav);
73
+ border-bottom: 1px solid var(--vp-c-gutter);
74
+ padding-top: var(--vp-layout-top-height, 0px);
75
+ width: 100%;
76
+ background-color: var(--vp-local-nav-bg-color);
77
+ }
78
+
79
+ .VPLocalNav.fixed {
80
+ position: fixed;
81
+ }
82
+
83
+ @media (min-width: 960px) {
84
+ .VPLocalNav {
85
+ top: var(--vp-nav-height);
86
+ }
87
+
88
+ .VPLocalNav.has-sidebar {
89
+ padding-left: var(--vp-sidebar-width);
90
+ }
91
+
92
+ .VPLocalNav.empty {
93
+ display: none;
94
+ }
95
+ }
96
+
97
+ @media (min-width: 1280px) {
98
+ .VPLocalNav {
99
+ display: none;
100
+ }
101
+ }
102
+
103
+ .container {
104
+ display: flex;
105
+ justify-content: space-between;
106
+ align-items: center;
107
+ }
108
+
109
+ .menu {
110
+ display: flex;
111
+ align-items: center;
112
+ padding: 12px 24px 11px;
113
+ line-height: 24px;
114
+ font-size: 12px;
115
+ font-weight: 500;
116
+ color: var(--vp-c-text-2);
117
+ transition: color 0.5s;
118
+ }
119
+
120
+ .menu:hover {
121
+ color: var(--vp-c-text-1);
122
+ transition: color 0.25s;
123
+ }
124
+
125
+ @media (min-width: 768px) {
126
+ .menu {
127
+ padding: 0 32px;
128
+ }
129
+ }
130
+
131
+ @media (min-width: 960px) {
132
+ .menu {
133
+ display: none;
134
+ }
135
+ }
136
+
137
+ .menu-icon {
138
+ margin-right: 8px;
139
+ font-size: 14px;
140
+ }
141
+
142
+ .VPOutlineDropdown {
143
+ padding: 12px 24px 11px;
144
+ }
145
+
146
+ @media (min-width: 768px) {
147
+ .VPOutlineDropdown {
148
+ padding: 12px 32px 11px;
149
+ }
150
+ }
151
+ </style>