@maple-design/cli-vue 0.0.0-stage → 1.1.1

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 (228) hide show
  1. package/README.md +305 -2
  2. package/README.zh-CN.md +305 -0
  3. package/bin/cli.mjs +5 -0
  4. package/client.d.ts +1 -0
  5. package/client.js +1 -0
  6. package/lib/client/appType.d.ts +4 -0
  7. package/lib/client/appType.js +7 -0
  8. package/lib/client/index.d.ts +29 -0
  9. package/lib/client/index.js +170 -0
  10. package/lib/client/language.d.ts +11 -0
  11. package/lib/client/language.js +52 -0
  12. package/lib/client/theme-state.d.ts +3 -0
  13. package/lib/client/theme-state.js +6 -0
  14. package/lib/node/bin.d.ts +2 -0
  15. package/lib/node/bin.js +154 -0
  16. package/lib/node/commands/build.d.ts +1 -0
  17. package/lib/node/commands/build.js +15 -0
  18. package/lib/node/commands/checklist.d.ts +6 -0
  19. package/lib/node/commands/checklist.js +59 -0
  20. package/lib/node/commands/compile.d.ts +3 -0
  21. package/lib/node/commands/compile.js +41 -0
  22. package/lib/node/commands/create.d.ts +8 -0
  23. package/lib/node/commands/create.js +91 -0
  24. package/lib/node/commands/dev.d.ts +6 -0
  25. package/lib/node/commands/dev.js +58 -0
  26. package/lib/node/commands/extension.d.ts +3 -0
  27. package/lib/node/commands/extension.js +5 -0
  28. package/lib/node/commands/gen.d.ts +24 -0
  29. package/lib/node/commands/gen.js +91 -0
  30. package/lib/node/commands/icons.d.ts +4 -0
  31. package/lib/node/commands/icons.js +15 -0
  32. package/lib/node/commands/preview.d.ts +4 -0
  33. package/lib/node/commands/preview.js +18 -0
  34. package/lib/node/commands/styleVars.d.ts +1 -0
  35. package/lib/node/commands/styleVars.js +6 -0
  36. package/lib/node/commands/test.d.ts +7 -0
  37. package/lib/node/commands/test.js +16 -0
  38. package/lib/node/commands/vite.d.ts +2 -0
  39. package/lib/node/commands/vite.js +14 -0
  40. package/lib/node/compiler/compileModule.d.ts +4 -0
  41. package/lib/node/compiler/compileModule.js +78 -0
  42. package/lib/node/compiler/compileSFC.d.ts +6 -0
  43. package/lib/node/compiler/compileSFC.js +119 -0
  44. package/lib/node/compiler/compileScript.d.ts +27 -0
  45. package/lib/node/compiler/compileScript.js +221 -0
  46. package/lib/node/compiler/compileSiteEntry.d.ts +19 -0
  47. package/lib/node/compiler/compileSiteEntry.js +164 -0
  48. package/lib/node/compiler/compileStyle.d.ts +14 -0
  49. package/lib/node/compiler/compileStyle.js +55 -0
  50. package/lib/node/compiler/compileStyleVars.d.ts +3 -0
  51. package/lib/node/compiler/compileStyleVars.js +46 -0
  52. package/lib/node/compiler/compileTemplateHighlight.d.ts +18 -0
  53. package/lib/node/compiler/compileTemplateHighlight.js +133 -0
  54. package/lib/node/compiler/compileTypes.d.ts +2 -0
  55. package/lib/node/compiler/compileTypes.js +58 -0
  56. package/lib/node/compiler/generateLibraryEntry.d.ts +3 -0
  57. package/lib/node/compiler/generateLibraryEntry.js +152 -0
  58. package/lib/node/compiler/siteRoutes.d.ts +4 -0
  59. package/lib/node/compiler/siteRoutes.js +9 -0
  60. package/lib/node/config/maple.config.d.ts +215 -0
  61. package/lib/node/config/maple.config.js +32 -0
  62. package/lib/node/config/maple.default.config.d.ts +2 -0
  63. package/lib/node/config/maple.default.config.js +386 -0
  64. package/lib/node/config/site-resolver.d.ts +25 -0
  65. package/lib/node/config/site-resolver.js +138 -0
  66. package/lib/node/config/vite.config.d.ts +28 -0
  67. package/lib/node/config/vite.config.js +209 -0
  68. package/lib/node/config/vitest.config.d.ts +2 -0
  69. package/lib/node/config/vitest.config.js +54 -0
  70. package/lib/node/config/vitest.setup.d.ts +1 -0
  71. package/lib/node/config/vitest.setup.js +8 -0
  72. package/lib/node/index.d.ts +13 -0
  73. package/lib/node/index.js +13 -0
  74. package/lib/node/release/index.d.ts +20 -0
  75. package/lib/node/release/index.js +147 -0
  76. package/lib/node/shared/constant.d.ts +47 -0
  77. package/lib/node/shared/constant.js +53 -0
  78. package/lib/node/shared/fsUtils.d.ts +18 -0
  79. package/lib/node/shared/fsUtils.js +57 -0
  80. package/lib/node/shared/logger.d.ts +8 -0
  81. package/lib/node/shared/logger.js +18 -0
  82. package/lib/node/shared/serial-task.d.ts +2 -0
  83. package/lib/node/shared/serial-task.js +9 -0
  84. package/package.json +111 -3
  85. package/site/documentation-sections.spec.ts +45 -0
  86. package/site/documentation-sections.ts +48 -0
  87. package/site/index.html +89 -0
  88. package/site/mobile/App.vue +437 -0
  89. package/site/mobile/AppHome.vue +154 -0
  90. package/site/mobile/main.ts +99 -0
  91. package/site/mobile/route-sync.spec.ts +34 -0
  92. package/site/mobile/route-sync.ts +10 -0
  93. package/site/mobile.html +70 -0
  94. package/site/module.d.ts +5 -0
  95. package/site/pc/App.vue +44 -0
  96. package/site/pc/Layout.vue +641 -0
  97. package/site/pc/components/AppAd.vue +117 -0
  98. package/site/pc/components/AppHeader.vue +459 -0
  99. package/site/pc/components/AppMobile.spec.ts +204 -0
  100. package/site/pc/components/AppMobile.vue +201 -0
  101. package/site/pc/components/AppOutline.spec.ts +89 -0
  102. package/site/pc/components/AppOutline.vue +161 -0
  103. package/site/pc/components/AppSidebar.vue +144 -0
  104. package/site/pc/components/code-example/CodeExample.spec.ts +36 -0
  105. package/site/pc/components/code-example/CodeExample.vue +164 -0
  106. package/site/pc/components/code-example/codeExample.less +88 -0
  107. package/site/pc/components/code-example/index.ts +10 -0
  108. package/site/pc/components/demo-preview/DemoPreview.vue +92 -0
  109. package/site/pc/components/demo-preview/index.ts +8 -0
  110. package/site/pc/components/mobile-preview-url.ts +17 -0
  111. package/site/pc/components/preview-section.spec.ts +24 -0
  112. package/site/pc/components/preview-section.ts +23 -0
  113. package/site/pc/main.ts +108 -0
  114. package/site/pc/pages/index/index.vue +421 -0
  115. package/site/pc/pages/index/locale/en-US.ts +1 -0
  116. package/site/pc/pages/index/locale/zh-CN.ts +1 -0
  117. package/site/pc/responsive-layout.spec.ts +40 -0
  118. package/site/pc/route-state.spec.ts +34 -0
  119. package/site/pc/route-state.ts +25 -0
  120. package/site/pc/scroll-hash.spec.ts +49 -0
  121. package/site/pc/scroll-hash.ts +25 -0
  122. package/site/route-recovery.spec.ts +51 -0
  123. package/site/route-recovery.ts +38 -0
  124. package/site/tsconfig.json +16 -0
  125. package/site/types.ts +6 -0
  126. package/site/utils.ts +45 -0
  127. package/template/create/[ComponentName].tsx.ejs +16 -0
  128. package/template/create/[ComponentName].vue.ejs +21 -0
  129. package/template/create/[componentName].d.ts.ejs +13 -0
  130. package/template/create/__tests__/index.spec.js.ejs +10 -0
  131. package/template/create/docs/en-US.md +0 -0
  132. package/template/create/docs/zh-CN.md +0 -0
  133. package/template/create/example/index.vue.ejs +17 -0
  134. package/template/create/example/locale/_index.ts +17 -0
  135. package/template/create/example/locale/en-US.ts +3 -0
  136. package/template/create/example/locale/index.ts +24 -0
  137. package/template/create/example/locale/zh-CN.ts +3 -0
  138. package/template/create/index.ts.ejs +12 -0
  139. package/template/create/props.ts +1 -0
  140. package/template/create/style/[componentName].less.ejs +5 -0
  141. package/template/create/style/index.less.ejs +1 -0
  142. package/template/create/style/index.ts.ejs +1 -0
  143. package/template/generators/base/.browserslistrc +2 -0
  144. package/template/generators/base/.prettierignore +14 -0
  145. package/template/generators/base/README.md +90 -0
  146. package/template/generators/base/_gitignore +19 -0
  147. package/template/generators/base/eslint.config.js +15 -0
  148. package/template/generators/base/package.json +97 -0
  149. package/template/generators/base/prettier.config.js +3 -0
  150. package/template/generators/base/public/logo.svg +30 -0
  151. package/template/generators/base/shims/shims-md.d.ts +4 -0
  152. package/template/generators/base/shims/shims-vue.d.ts +6 -0
  153. package/template/generators/base/tsconfig.declaration.json +21 -0
  154. package/template/generators/base/tsconfig.json +28 -0
  155. package/template/generators/base/ui-build.config.mjs +16 -0
  156. package/template/generators/config/default/base/docs/home.zh-CN.md +3 -0
  157. package/template/generators/config/default/base/maple.config.mjs +49 -0
  158. package/template/generators/config/default/sfc/src/button/Button.vue +34 -0
  159. package/template/generators/config/default/sfc/src/button/__tests__/index.spec.js +8 -0
  160. package/template/generators/config/default/sfc/src/button/docs/zh-CN.md +39 -0
  161. package/template/generators/config/default/sfc/src/button/example/BasicUse.vue +7 -0
  162. package/template/generators/config/default/sfc/src/button/example/ModifyColor.vue +7 -0
  163. package/template/generators/config/default/sfc/src/button/example/index.vue +12 -0
  164. package/template/generators/config/default/sfc/src/button/index.ts +8 -0
  165. package/template/generators/config/default/sfc/src/button/style/button.less +14 -0
  166. package/template/generators/config/default/sfc/src/button/style/index.less +1 -0
  167. package/template/generators/config/default/sfc/src/button/style/index.ts +1 -0
  168. package/template/generators/config/default/sfc/src/utils/components.ts +20 -0
  169. package/template/generators/config/default/tsx/src/button/Button.tsx +30 -0
  170. package/template/generators/config/default/tsx/src/button/__tests__/index.spec.js +8 -0
  171. package/template/generators/config/default/tsx/src/button/docs/zh-CN.md +39 -0
  172. package/template/generators/config/default/tsx/src/button/example/BasicUse.vue +7 -0
  173. package/template/generators/config/default/tsx/src/button/example/ModifyColor.vue +7 -0
  174. package/template/generators/config/default/tsx/src/button/example/index.vue +12 -0
  175. package/template/generators/config/default/tsx/src/button/index.ts +8 -0
  176. package/template/generators/config/default/tsx/src/button/style/button.less +14 -0
  177. package/template/generators/config/default/tsx/src/button/style/index.less +1 -0
  178. package/template/generators/config/default/tsx/src/button/style/index.ts +1 -0
  179. package/template/generators/config/default/tsx/src/utils/components.ts +20 -0
  180. package/template/generators/config/i18n/base/docs/home.en-US.md +3 -0
  181. package/template/generators/config/i18n/base/docs/home.zh-CN.md +3 -0
  182. package/template/generators/config/i18n/base/maple.config.mjs +51 -0
  183. package/template/generators/config/i18n/sfc/src/button/Button.vue +38 -0
  184. package/template/generators/config/i18n/sfc/src/button/__tests__/index.spec.js +8 -0
  185. package/template/generators/config/i18n/sfc/src/button/docs/en-US.md +39 -0
  186. package/template/generators/config/i18n/sfc/src/button/docs/zh-CN.md +39 -0
  187. package/template/generators/config/i18n/sfc/src/button/example/BasicUse.vue +11 -0
  188. package/template/generators/config/i18n/sfc/src/button/example/ModifyColor.vue +11 -0
  189. package/template/generators/config/i18n/sfc/src/button/example/index.vue +15 -0
  190. package/template/generators/config/i18n/sfc/src/button/example/locale/en-US.ts +5 -0
  191. package/template/generators/config/i18n/sfc/src/button/example/locale/index.ts +23 -0
  192. package/template/generators/config/i18n/sfc/src/button/example/locale/zh-CN.ts +5 -0
  193. package/template/generators/config/i18n/sfc/src/button/index.ts +8 -0
  194. package/template/generators/config/i18n/sfc/src/button/style/button.less +14 -0
  195. package/template/generators/config/i18n/sfc/src/button/style/index.less +1 -0
  196. package/template/generators/config/i18n/sfc/src/button/style/index.ts +1 -0
  197. package/template/generators/config/i18n/sfc/src/locale/__tests__/index.spec.js +52 -0
  198. package/template/generators/config/i18n/sfc/src/locale/docs/en-US.md +26 -0
  199. package/template/generators/config/i18n/sfc/src/locale/docs/zh-CN.md +25 -0
  200. package/template/generators/config/i18n/sfc/src/locale/en-US.d.ts +5 -0
  201. package/template/generators/config/i18n/sfc/src/locale/en-US.ts +7 -0
  202. package/template/generators/config/i18n/sfc/src/locale/index.ts +82 -0
  203. package/template/generators/config/i18n/sfc/src/locale/zh-CN.d.ts +5 -0
  204. package/template/generators/config/i18n/sfc/src/locale/zh-CN.ts +7 -0
  205. package/template/generators/config/i18n/sfc/src/utils/components.ts +20 -0
  206. package/template/generators/config/i18n/tsx/src/button/Button.tsx +35 -0
  207. package/template/generators/config/i18n/tsx/src/button/__tests__/index.spec.js +8 -0
  208. package/template/generators/config/i18n/tsx/src/button/docs/en-US.md +39 -0
  209. package/template/generators/config/i18n/tsx/src/button/docs/zh-CN.md +39 -0
  210. package/template/generators/config/i18n/tsx/src/button/example/BasicUse.vue +11 -0
  211. package/template/generators/config/i18n/tsx/src/button/example/ModifyColor.vue +11 -0
  212. package/template/generators/config/i18n/tsx/src/button/example/index.vue +15 -0
  213. package/template/generators/config/i18n/tsx/src/button/example/locale/en-US.ts +5 -0
  214. package/template/generators/config/i18n/tsx/src/button/example/locale/index.ts +23 -0
  215. package/template/generators/config/i18n/tsx/src/button/example/locale/zh-CN.ts +5 -0
  216. package/template/generators/config/i18n/tsx/src/button/index.ts +8 -0
  217. package/template/generators/config/i18n/tsx/src/button/style/button.less +14 -0
  218. package/template/generators/config/i18n/tsx/src/button/style/index.less +1 -0
  219. package/template/generators/config/i18n/tsx/src/button/style/index.ts +1 -0
  220. package/template/generators/config/i18n/tsx/src/locale/__tests__/index.spec.js +52 -0
  221. package/template/generators/config/i18n/tsx/src/locale/docs/en-US.md +26 -0
  222. package/template/generators/config/i18n/tsx/src/locale/docs/zh-CN.md +25 -0
  223. package/template/generators/config/i18n/tsx/src/locale/en-US.d.ts +5 -0
  224. package/template/generators/config/i18n/tsx/src/locale/en-US.ts +7 -0
  225. package/template/generators/config/i18n/tsx/src/locale/index.ts +82 -0
  226. package/template/generators/config/i18n/tsx/src/locale/zh-CN.d.ts +5 -0
  227. package/template/generators/config/i18n/tsx/src/locale/zh-CN.ts +7 -0
  228. package/template/generators/config/i18n/tsx/src/utils/components.ts +20 -0
@@ -0,0 +1,108 @@
1
+ import { createApp, h } from 'vue'
2
+ import config from '@config'
3
+ import {
4
+ currentStyleVars,
5
+ currentThemePreset,
6
+ getSavedLanguage,
7
+ normalizeLanguage,
8
+ saveLanguage,
9
+ } from '@maple-design/cli-vue/client'
10
+ import { Locale, ThemeProvider } from '@maple-design/ui-vue'
11
+ import routes from '@pc-routes'
12
+ import { createRouter, createWebHashHistory } from 'vue-router'
13
+ import { getDesktopPath } from '../mobile/route-sync'
14
+ import { installRouteRecovery } from '../route-recovery'
15
+ import App from './App.vue'
16
+ import CodeExample from './components/code-example'
17
+ import DemoPreview from './components/demo-preview'
18
+ import '@maple-design/touch-emulator-vue'
19
+ import '@maple-foundation'
20
+
21
+ const defaultLanguage = getSavedLanguage(normalizeLanguage(config?.defaultLanguage))
22
+ Locale.add('zh-CN', Locale.zhCN)
23
+ Locale.add('en-US', Locale.enUS)
24
+ const redirect = config?.pc?.redirect
25
+ const mobileRedirect = config?.mobile?.redirect
26
+
27
+ redirect &&
28
+ routes.push({
29
+ path: '/:pathMatch(.*)*',
30
+ redirect: `/${defaultLanguage}${redirect}`,
31
+ })
32
+
33
+ const router = createRouter({
34
+ history: createWebHashHistory(),
35
+ routes,
36
+ scrollBehavior(to: any) {
37
+ if (to.hash) {
38
+ return {
39
+ el: to.hash,
40
+ behavior: 'smooth',
41
+ top: 100,
42
+ }
43
+ }
44
+
45
+ return { top: 0 }
46
+ },
47
+ })
48
+
49
+ installRouteRecovery(router)
50
+
51
+ router.beforeEach((to: any, from: any) => {
52
+ const language = normalizeLanguage(to.path.split('/')[1], defaultLanguage)
53
+ Locale.use(language)
54
+ saveLanguage(language)
55
+ if (to.fullPath === from.fullPath) {
56
+ return false
57
+ }
58
+
59
+ // @ts-ignore
60
+ if (
61
+ window._hmt &&
62
+ (from.matched.length === 0 || to.path !== from.path || JSON.stringify(to.query) !== JSON.stringify(from.query))
63
+ ) {
64
+ if (to.path) {
65
+ // @ts-ignore
66
+ window._hmt.push(['_trackPageview', `/#${to.fullPath}`])
67
+ }
68
+ }
69
+ })
70
+
71
+ Object.defineProperty(window, 'onMobileRouteChange', {
72
+ value: (path: string, language: string, replace: string, hash: string, query: Record<string, any> = {}) => {
73
+ const { language: _language, platform: _platform, replace: _replace, ...businessQuery } = query
74
+ router.replace({
75
+ path: path === mobileRedirect ? `/${language}/${replace}` : getDesktopPath(path, language),
76
+ hash,
77
+ query: businessQuery,
78
+ })
79
+ },
80
+ })
81
+
82
+ Object.defineProperty(window, 'scrollToMenu', {
83
+ value: (docName: string) => {
84
+ setTimeout(() => {
85
+ const cell = document.getElementById(docName) as HTMLElement
86
+ const scroller = cell.parentNode as HTMLElement
87
+ scroller.scrollTo({ top: cell.offsetTop - scroller.offsetHeight / 2 })
88
+ })
89
+ },
90
+ })
91
+
92
+ createApp({
93
+ render: () =>
94
+ h(
95
+ ThemeProvider,
96
+ {
97
+ theme: currentThemePreset.value,
98
+ styleVars: currentStyleVars.value,
99
+ style: { minHeight: '100vh', background: 'var(--mp-color-body)', color: 'var(--mp-color-text)' },
100
+ },
101
+ () => h(App),
102
+ ),
103
+ })
104
+ .use(router)
105
+ // @ts-ignore
106
+ .use(CodeExample)
107
+ .use(DemoPreview)
108
+ .mount('#app')
@@ -0,0 +1,421 @@
1
+ <script setup lang="ts">
2
+ import { ref, watch, type Ref } from 'vue'
3
+ import config from '@config'
4
+ import { getPCLocationInfo, onThemeChange } from '@maple-design/cli-vue/client'
5
+ import { IconGithub as MIconGithub, IconTwitter as MIconTwitter } from '@maple-design/icons-vue'
6
+ import { useRoute, useRouter } from 'vue-router'
7
+ import { resolveSiteAsset } from '../../../utils'
8
+ import AppAd from '../../components/AppAd.vue'
9
+ import AppHeader from '../../components/AppHeader.vue'
10
+
11
+ const route = useRoute()
12
+ const router = useRouter()
13
+ const github = config?.pc?.header?.github
14
+ const siteLogo = resolveSiteAsset(config?.logo)
15
+ const title: Ref<string> = ref(config?.title)
16
+ const language: Ref<string> = ref(config?.defaultLanguage)
17
+ const indexPage: Ref<Record<string, any>> = ref(config?.pc?.indexPage)
18
+
19
+ const getStar = () => {
20
+ router.push(`/${language.value}/quickstart`)
21
+ }
22
+
23
+ const toGithub = () => {
24
+ window.open(github)
25
+ }
26
+
27
+ const setLocale = () => {
28
+ const { language: lang } = getPCLocationInfo()
29
+ if (!lang) {
30
+ return
31
+ }
32
+
33
+ language.value = lang
34
+ document.title = config?.pc.title[lang] as string
35
+ }
36
+
37
+ const to = (url: string) => {
38
+ window.open(url)
39
+ }
40
+
41
+ onThemeChange(undefined, 'site')
42
+
43
+ watch(
44
+ () => route.path,
45
+ () => {
46
+ language.value = getPCLocationInfo().language
47
+ setLocale()
48
+ },
49
+ { immediate: true },
50
+ )
51
+ </script>
52
+
53
+ <template>
54
+ <div class="maple-doc-index">
55
+ <app-ad :language="language" />
56
+ <app-header :language="language" />
57
+ <div class="maple-doc-index__layout">
58
+ <div class="maple-doc-index__main-container">
59
+ <div class="maple-doc-index__logo-container">
60
+ <div class="maple-doc-index__logo-background-mask"></div>
61
+ <img class="maple-doc-index__logo" :src="siteLogo" alt="" />
62
+ </div>
63
+ <div class="maple-doc-index__info-container">
64
+ <p v-if="indexPage.eyebrow" class="maple-doc-index__eyebrow">{{ indexPage.eyebrow[language] }}</p>
65
+ <p class="maple-doc-index__product">{{ title }}</p>
66
+ <h1 class="maple-doc-index__title">{{ indexPage.headline?.[language] || title }}</h1>
67
+ <div class="maple-doc-index__description">{{ indexPage.description[language] }}</div>
68
+
69
+ <mp-space size="lg">
70
+ <mp-button class="maple-doc-index__link-button" type="primary" style="line-height: 1.2" @click="getStar">
71
+ {{ indexPage.started[language] }}
72
+ </mp-button>
73
+ <a v-if="indexPage.companion" class="maple-doc-index__companion-link" :href="indexPage.companion.href">
74
+ {{ indexPage.companion.label[language] }}
75
+ </a>
76
+ <mp-button v-else class="maple-doc-index__github-button" style="line-height: 1.2" @click="toGithub">
77
+ {{ indexPage.viewOnGithub[language] }}
78
+ </mp-button>
79
+ </mp-space>
80
+ </div>
81
+ </div>
82
+
83
+ <section v-if="indexPage.companion" class="maple-doc-index__pair">
84
+ <div>
85
+ <h2>{{ indexPage.companion.title[language] }}</h2>
86
+ <p>{{ indexPage.companion.description[language] }}</p>
87
+ </div>
88
+ <div class="maple-doc-index__platforms">
89
+ <a :href="indexPage.companion.href"><span>01 / DESKTOP</span><strong>Cedar ↗</strong></a>
90
+ <div aria-current="page"><span>02 / MOBILE</span><strong>Maple</strong></div>
91
+ </div>
92
+ </section>
93
+
94
+ <div v-if="indexPage.features" class="maple-doc-index__features">
95
+ <div v-for="feature in indexPage.features" :key="feature.name[language]" class="maple-doc-index__feature">
96
+ <div class="maple-doc-index__feature-name">{{ feature.name[language] }}</div>
97
+ <div class="maple-doc-index__feature-description">{{ feature.description[language] }}</div>
98
+ </div>
99
+ </div>
100
+
101
+ <div v-if="indexPage.teamMembers" class="maple-doc-index__team-members">
102
+ <div class="maple-doc-index__team-members-title">{{ indexPage.teamMembers.label[language] }}</div>
103
+
104
+ <div class="maple-doc-index__team-members-container">
105
+ <div
106
+ v-for="member in indexPage.teamMembers.members"
107
+ :key="member.name[language]"
108
+ class="maple-doc-index__team-member"
109
+ >
110
+ <img
111
+ v-if="member.avatar"
112
+ class="maple-doc-index__team-member-avatar"
113
+ :src="member.avatar"
114
+ :alt="member.name[language]"
115
+ />
116
+ <div
117
+ v-else
118
+ class="maple-doc-index__team-member-avatar maple-doc-index__team-member-avatar--placeholder"
119
+ aria-hidden="true"
120
+ >
121
+ {{ member.name[language].slice(0, 1) }}
122
+ </div>
123
+ <div class="maple-doc-index__team-member-name">{{ member.name[language] }}</div>
124
+ <div class="maple-doc-index__team-member-title">{{ member.title[language] }}</div>
125
+ <div class="maple-doc-index__team-member-description">{{ member.description[language] }}</div>
126
+ <div v-if="member.github || member.twitter" class="maple-doc-index__team-member-social">
127
+ <mp-icon
128
+ v-if="member.github"
129
+ class="maple-doc-index__team-member-social-icon"
130
+ :size="24"
131
+ @click="to(member.github)"
132
+ ><MIconGithub
133
+ /></mp-icon>
134
+ <mp-icon
135
+ v-if="member.twitter"
136
+ class="maple-doc-index__team-member-social-icon"
137
+ :size="24"
138
+ @click="to(member.twitter)"
139
+ ><MIconTwitter
140
+ /></mp-icon>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ </div>
145
+
146
+ <div v-if="indexPage.contributors" class="maple-doc-index__contributors">
147
+ <div class="maple-doc-index__contributors-title">{{ indexPage.contributors.label[language] }}</div>
148
+
149
+ <a class="maple-doc-index__contributors-link" :href="indexPage.contributors.link">
150
+ <img class="maple-doc-index__contributors-image" :src="indexPage.contributors.image" />
151
+ </a>
152
+ </div>
153
+
154
+ <div v-if="indexPage.sponsors" class="maple-doc-index__sponsors">
155
+ <div class="maple-doc-index__sponsors-title">{{ indexPage.sponsors.label[language] }}</div>
156
+
157
+ <a class="maple-doc-index__sponsors-link" :href="indexPage.sponsors.link">
158
+ <img class="maple-doc-index__sponsors-image" :src="indexPage.sponsors.image" />
159
+ </a>
160
+ </div>
161
+
162
+ <div class="maple-doc-index__footer">
163
+ <div class="maple-doc-index__license">{{ indexPage.license[language] }}</div>
164
+ <div class="maple-doc-index__copyright">{{ indexPage.copyright[language] }}</div>
165
+ </div>
166
+ </div>
167
+ </div>
168
+ </template>
169
+
170
+ <style lang="less">
171
+ .maple-doc-index {
172
+ min-height: 100vh;
173
+ background: var(--site-config-color-index-page-background);
174
+ color: var(--site-config-color-text);
175
+
176
+ &__layout {
177
+ width: 100%;
178
+ max-width: 1120px;
179
+ margin: auto;
180
+ padding: 80px 32px 0;
181
+ box-sizing: border-box;
182
+ }
183
+ &__main-container {
184
+ display: flex;
185
+ flex-direction: row-reverse;
186
+ align-items: center;
187
+ gap: 64px;
188
+ padding: 36px 0 80px;
189
+ }
190
+ &__info-container {
191
+ flex: 1;
192
+ min-width: 0;
193
+ }
194
+ &__eyebrow {
195
+ color: var(--site-config-color-text);
196
+ font-size: 12px;
197
+ font-weight: 700;
198
+ letter-spacing: 2px;
199
+ margin: 0 0 24px;
200
+ }
201
+ &__product {
202
+ font-size: 18px;
203
+ margin: 0 0 12px;
204
+ color: var(--site-config-color-index-page-second-text-color);
205
+ }
206
+ &__title {
207
+ font-size: clamp(34px, 4.4vw, 56px);
208
+ font-weight: 750;
209
+ line-height: 1.2;
210
+ letter-spacing: -1.5px;
211
+ white-space: pre-line;
212
+ margin: 0;
213
+ }
214
+ &__description {
215
+ max-width: 660px;
216
+ font-size: 17px;
217
+ line-height: 1.9;
218
+ margin: 24px 0 30px;
219
+ color: var(--site-config-color-index-page-second-text-color);
220
+ }
221
+ &__logo-container {
222
+ width: 160px;
223
+ flex: 0 0 160px;
224
+ }
225
+ &__logo {
226
+ display: block;
227
+ width: 100%;
228
+ }
229
+ &__logo-background-mask {
230
+ display: none;
231
+ }
232
+ &__link-button,
233
+ &__github-button {
234
+ min-height: 44px;
235
+ padding: 0 24px;
236
+ }
237
+ &__companion-link {
238
+ display: inline-flex;
239
+ align-items: center;
240
+ min-height: 44px;
241
+ color: var(--site-config-color-text);
242
+ text-decoration: none;
243
+ font-weight: 600;
244
+ }
245
+ a:focus-visible {
246
+ outline: 2px solid var(--site-config-color-primary);
247
+ outline-offset: 5px;
248
+ border-radius: 4px;
249
+ }
250
+ a:hover {
251
+ color: var(--site-config-color-text);
252
+ }
253
+ &__pair {
254
+ display: grid;
255
+ grid-template-columns: 1fr 1fr;
256
+ align-items: center;
257
+ gap: 40px;
258
+ padding: 32px;
259
+ border: 1px solid var(--site-config-color-border);
260
+ border-radius: 16px;
261
+ }
262
+ &__pair h2 {
263
+ font-size: 24px;
264
+ line-height: 1.4;
265
+ margin: 0 0 12px;
266
+ }
267
+ &__pair p {
268
+ line-height: 1.8;
269
+ margin: 0;
270
+ color: var(--site-config-color-index-page-second-text-color);
271
+ }
272
+ &__platforms {
273
+ display: grid;
274
+ grid-template-columns: 1fr 1fr;
275
+ gap: 12px;
276
+ }
277
+ &__platforms > * {
278
+ display: flex;
279
+ flex-direction: column;
280
+ gap: 16px;
281
+ padding: 24px;
282
+ border-radius: 10px;
283
+ background: var(--site-config-color-side-bar-active-background);
284
+ color: inherit;
285
+ text-decoration: none;
286
+ }
287
+ &__platforms span {
288
+ font-size: 11px;
289
+ letter-spacing: 1px;
290
+ color: var(--site-config-color-index-page-second-text-color);
291
+ }
292
+ &__platforms strong {
293
+ font-size: 24px;
294
+ }
295
+ &__features {
296
+ display: grid;
297
+ grid-template-columns: repeat(3, minmax(0, 1fr));
298
+ gap: 20px;
299
+ margin: 32px 0 72px;
300
+ }
301
+ &__feature {
302
+ padding: 28px;
303
+ border: 1px solid var(--site-config-color-border);
304
+ border-radius: 12px;
305
+ background: var(--site-config-color-index-page-feature-background);
306
+ }
307
+ &__feature-name {
308
+ font-size: 18px;
309
+ font-weight: 650;
310
+ }
311
+ &__feature-description {
312
+ font-size: 14px;
313
+ line-height: 1.9;
314
+ margin-top: 12px;
315
+ color: var(--site-config-color-index-page-second-text-color);
316
+ }
317
+ &__team-members,
318
+ &__contributors,
319
+ &__sponsors {
320
+ padding-top: 32px;
321
+ margin-bottom: 48px;
322
+ border-top: 1px solid var(--site-config-color-border);
323
+ }
324
+ &__team-members-title,
325
+ &__contributors-title,
326
+ &__sponsors-title {
327
+ font-size: 20px;
328
+ font-weight: 600;
329
+ }
330
+ &__team-members-container {
331
+ display: flex;
332
+ flex-wrap: wrap;
333
+ gap: 32px;
334
+ margin-top: 24px;
335
+ }
336
+ &__team-member {
337
+ display: grid;
338
+ grid-template-columns: 48px auto;
339
+ column-gap: 16px;
340
+ align-content: start;
341
+ }
342
+ &__team-member-avatar {
343
+ width: 48px;
344
+ height: 48px;
345
+ border-radius: 50%;
346
+ grid-row: span 3;
347
+ }
348
+ &__team-member-avatar--placeholder {
349
+ display: flex;
350
+ align-items: center;
351
+ justify-content: center;
352
+ color: var(--site-config-color-text);
353
+ background: var(--site-config-color-side-bar-active-background);
354
+ font-size: 20px;
355
+ }
356
+ &__team-member-name {
357
+ font-weight: 600;
358
+ line-height: 24px;
359
+ }
360
+ &__team-member-title,
361
+ &__team-member-description {
362
+ font-size: 12px;
363
+ line-height: 22px;
364
+ color: var(--site-config-color-index-page-second-text-color);
365
+ }
366
+ &__team-member-social {
367
+ grid-column: 2;
368
+ }
369
+ &__team-member-social-icon {
370
+ margin-right: 12px;
371
+ cursor: pointer;
372
+ }
373
+ &__contributors-image,
374
+ &__sponsors-image {
375
+ max-width: 100%;
376
+ }
377
+ &__footer {
378
+ padding: 24px 0;
379
+ border-top: 1px solid var(--site-config-color-border);
380
+ font-size: 12px;
381
+ line-height: 1.8;
382
+ color: var(--site-config-color-index-page-second-text-color);
383
+ }
384
+
385
+ @media (max-width: 767px) {
386
+ &__layout {
387
+ padding: 48px 20px 0;
388
+ }
389
+ &__main-container {
390
+ gap: 20px;
391
+ padding: 0 0 40px;
392
+ }
393
+ &__logo-container {
394
+ display: none;
395
+ }
396
+ &__pair {
397
+ grid-template-columns: 1fr;
398
+ gap: 24px;
399
+ padding: 24px;
400
+ }
401
+ &__features {
402
+ grid-template-columns: repeat(2, minmax(0, 1fr));
403
+ gap: 12px;
404
+ }
405
+ &__feature {
406
+ padding: 20px;
407
+ }
408
+ &__team-members-container {
409
+ gap: 24px;
410
+ }
411
+ }
412
+ @media (max-width: 420px) {
413
+ &__features {
414
+ grid-template-columns: 1fr;
415
+ }
416
+ &__platforms > * {
417
+ padding: 18px;
418
+ }
419
+ }
420
+ }
421
+ </style>
@@ -0,0 +1 @@
1
+ // this file used to generate route path ('/en-US/index')
@@ -0,0 +1 @@
1
+ // this file used to generate route path ('/zh-CN/index')
@@ -0,0 +1,40 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { dirname, resolve } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+ import { parse } from '@vue/compiler-sfc'
5
+ import less from 'less'
6
+ import { describe, expect, it } from 'vitest'
7
+
8
+ const directory = dirname(fileURLToPath(import.meta.url))
9
+
10
+ describe('documentation responsive layout contract', () => {
11
+ it('compiles shrinking columns, stacked preview and an accessible narrow navigation', async () => {
12
+ const source = readFileSync(resolve(directory, 'Layout.vue'), 'utf8')
13
+ const { descriptor } = parse(source)
14
+ const { css } = await less.render(descriptor.styles.map((style) => style.content).join('\n'))
15
+
16
+ expect(css).not.toContain('min-width: 1200px')
17
+ expect(css).not.toContain('min-width: 500px')
18
+ expect(css).toContain('grid-template-columns: 220px minmax(0, 1fr)')
19
+ expect(css).toContain('@media (max-width: 1199px)')
20
+ expect(css).toContain('@media (max-width: 767px)')
21
+ expect(css).toMatch(/\.maple-site-content--navigation-open \.maple-site-sidebar\s*\{\s*display: block/)
22
+ expect(css).toMatch(/\.maple-site-mobile\s*\{[^}]*position: static/)
23
+ expect(source).toContain('aria-controls="maple-site-navigation"')
24
+ expect(source).toContain(':aria-expanded="sidebarOpen"')
25
+ expect(source).toContain('@keydown.esc="closeSidebar"')
26
+ expect(source).toContain('doc.value?.focus()')
27
+ expect(source).toContain('sidebarToggle.value?.focus()')
28
+ })
29
+
30
+ it('keeps language selection and visible keyboard focus available on narrow screens', async () => {
31
+ const header = parse(readFileSync(resolve(directory, 'components/AppHeader.vue'), 'utf8')).descriptor
32
+ const sidebar = parse(readFileSync(resolve(directory, 'components/AppSidebar.vue'), 'utf8')).descriptor
33
+ const { css } = await less.render([...header.styles, ...sidebar.styles].map((style) => style.content).join('\n'))
34
+ expect(css).not.toMatch(/\.maple-site-header__tail\s*\{\s*display: none/)
35
+ expect(css.indexOf('@media screen and (max-width: 562px)')).toBeGreaterThan(
36
+ css.indexOf('.maple-site-header__link {'),
37
+ )
38
+ expect(css).toMatch(/\.maple-site-sidebar__link:focus-visible\s*\{[^}]*outline: 2px solid/)
39
+ })
40
+ })
@@ -0,0 +1,34 @@
1
+ import { computed, ref } from 'vue'
2
+ import { describe, expect, it } from 'vitest'
3
+ import { MenuTypes, type Menu } from '../utils'
4
+ import { getPcSiteRoute } from './route-state'
5
+
6
+ const menu: Menu[] = [
7
+ { doc: 'button', type: MenuTypes.COMPONENT, text: {} },
8
+ { doc: 'input', type: MenuTypes.COMPONENT, text: {} },
9
+ { doc: 'guide', type: MenuTypes.DOCUMENTATION, text: {} },
10
+ ]
11
+
12
+ describe('PC site mobile preview route', () => {
13
+ it('tracks component and hash changes from the reactive router state', () => {
14
+ const path = ref('/zh-CN/button')
15
+ const hash = ref('')
16
+ const state = computed(() => getPcSiteRoute(path.value, hash.value, menu, '/home', true))
17
+
18
+ expect(state.value).toMatchObject({ componentName: 'button', menuName: 'button', useMobile: true })
19
+ path.value = '/zh-CN/input'
20
+ hash.value = '#basic'
21
+ expect(state.value).toMatchObject({ componentName: 'input', menuName: 'input', hash: 'basic' })
22
+
23
+ path.value = '/zh-CN/guide'
24
+ expect(state.value).toMatchObject({ componentName: 'home', menuName: 'guide' })
25
+
26
+ path.value = '/en-US/button'
27
+ expect(state.value).toMatchObject({ language: 'en-US', componentName: 'button' })
28
+ })
29
+ })
30
+
31
+ it('previews opted-in root documentation on the phone route', () => {
32
+ expect(getPcSiteRoute('/zh-CN/guide', '', menu, '/home', true, ['guide']).componentName).toBe('zh-CN/guide')
33
+ expect(getPcSiteRoute('/en-US/guide', '', menu, '/home', true, ['guide']).componentName).toBe('en-US/guide')
34
+ })
@@ -0,0 +1,25 @@
1
+ import { MenuTypes, type Menu } from '../utils'
2
+
3
+ export function getPcSiteRoute(
4
+ path: string,
5
+ hash: string,
6
+ menu: Menu[],
7
+ mobileRedirect: string | undefined,
8
+ defaultUseMobile: boolean | undefined,
9
+ mobileDocs: string[] = [],
10
+ ) {
11
+ const [, language = '', menuName = ''] = path.split('/')
12
+ const component = menu.find((item) => item.doc === menuName && item.type === MenuTypes.COMPONENT)
13
+
14
+ return {
15
+ language,
16
+ menuName,
17
+ hash: hash.replace(/^#/, ''),
18
+ componentName: component
19
+ ? menuName
20
+ : mobileDocs.includes(menuName)
21
+ ? `${language}/${menuName}`
22
+ : mobileRedirect?.replace(/^\//, ''),
23
+ useMobile: component?.useMobile ?? defaultUseMobile,
24
+ }
25
+ }
@@ -0,0 +1,49 @@
1
+ import { describe, expect, it, vi } from 'vitest'
2
+ import { createMemoryHistory, createRouter } from 'vue-router'
3
+ import { createScrollHashSync } from './scroll-hash'
4
+
5
+ function setup() {
6
+ const scrollBehavior = vi.fn(() => false as const)
7
+ const router = createRouter({
8
+ history: createMemoryHistory(),
9
+ routes: [{ path: '/:pathMatch(.*)*', component: {} }],
10
+ scrollBehavior,
11
+ })
12
+ return { router, scrollBehavior, sync: createScrollHashSync(router) }
13
+ }
14
+
15
+ describe('document scroll hash synchronization', () => {
16
+ it('replaces only the hash, preserves query, skips scrolling and adds no back entry', async () => {
17
+ const { router, scrollBehavior, sync } = setup()
18
+ await router.push('/previous')
19
+ await router.push('/zh-CN/button?scene=test')
20
+ scrollBehavior.mockClear()
21
+ const replace = vi.spyOn(router, 'replace')
22
+ await sync.replace('#sizes')
23
+ expect(router.currentRoute.value.fullPath).toBe('/zh-CN/button?scene=test#sizes')
24
+ expect(scrollBehavior).not.toHaveBeenCalled()
25
+ expect(router.options.scrollBehavior).toBe(scrollBehavior)
26
+ await sync.replace('#sizes')
27
+ expect(replace).toHaveBeenCalledTimes(1)
28
+ await sync.replace('')
29
+ expect(router.currentRoute.value.hash).toBe('')
30
+ const back = new Promise<void>((resolve) => router.afterEach(() => resolve()))
31
+ router.back()
32
+ await back
33
+ expect(router.currentRoute.value.path).toBe('/previous')
34
+ })
35
+
36
+ it('restores normal scrolling after cancelled or failed navigation', async () => {
37
+ const { router, scrollBehavior, sync } = setup()
38
+ await router.push('/zh-CN/button')
39
+ router.beforeEach(() => false)
40
+ await sync.replace('#cancelled')
41
+ expect(router.currentRoute.value.hash).toBe('')
42
+ expect(router.options.scrollBehavior).toBe(scrollBehavior)
43
+ expect(sync.pending).toBe(false)
44
+ vi.spyOn(router, 'replace').mockRejectedValueOnce(new Error('Failed navigation'))
45
+ await expect(sync.replace('#failed')).rejects.toThrow('Failed navigation')
46
+ expect(router.options.scrollBehavior).toBe(scrollBehavior)
47
+ expect(sync.pending).toBe(false)
48
+ })
49
+ })