+page.svelte 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. <script lang="ts">
  2. import { onMount } from 'svelte';
  3. import { fly } from 'svelte/transition';
  4. import { isShowNavStore, isCmdKStore } from '../../store';
  5. import { menuList, type MenuList } from '../../data/menuList';
  6. import { page } from '$app/stores';
  7. import hljs from 'highlight.js';
  8. import type { MenuListChild } from '../../data/menuList.js';
  9. import Menu from '$lib/menu/Menu.svelte';
  10. import Tab from '$lib/tab/Tab.svelte';
  11. import { goto } from '$app/navigation';
  12. import Component from './Component.svelte';
  13. import Guide from './Guide.svelte';
  14. import Api from './Api.svelte';
  15. import FAQ from './FAQ.svelte';
  16. import Version from './Version.svelte';
  17. let loading = $state(true);
  18. const isZh = localStorage.getItem('lang') === 'zh_CN';
  19. const params = $page.url.searchParams;
  20. //数组二级组成新数组
  21. const ArrChildFun = (arr: MenuList[]) => {
  22. let newArr = [];
  23. for (let e = 0; e < arr.length; e++) {
  24. newArr.push(...arr[e].childs);
  25. }
  26. return newArr;
  27. };
  28. let menuChildList = $derived(ArrChildFun(menuList));
  29. let currentNav = $state<MenuListChild | null>(null);
  30. let currentTab = $state(0);
  31. let titleDom: HTMLElement | null = $state(null);
  32. let demoHeight = $state(0);
  33. let highlightedCode = $state('');
  34. let titleWidth = $state(0);
  35. let visible = $state(true);
  36. let isShowIframe = $state(true);
  37. let navBarHeight = $state(0);
  38. let menuChange = $state(true);
  39. // 动态导入组件字符串
  40. const getComponentStrFunc = async (name: string) => {
  41. const rawObj = await import(`../../../../../packages/stdf/src/routes/${name}/${isZh ? 'zh_CN' : 'en_US'}/+page.svelte?raw`);
  42. // 将 componentStr 中所有的 from '$lib/index.js' 替换为 from 'stdf'
  43. // 将 componentStr 中所有的 from '$lib/types/index.js' 替换为 from 'stdf/types'
  44. return rawObj.default
  45. .replace(/from ['"]\$lib\/index\.js['"]/g, "from 'stdf'")
  46. .replace(/from ['"]\$lib\/types\/index\.js['"]/g, "from 'stdf/types'");
  47. };
  48. const getDemoHeightFun = () => {
  49. if (titleDom) {
  50. demoHeight = document.documentElement.clientHeight - 70 - titleDom.offsetHeight - 100;
  51. navBarHeight = document.documentElement.clientHeight - 56;
  52. }
  53. };
  54. const initFn = async () => {
  55. if (params.get('tab')) {
  56. currentTab = Number(params.get('tab'));
  57. }
  58. if (params.get('nav')) {
  59. currentNav = menuChildList.filter((item) => item.nav === params.get('nav'))[0];
  60. } else {
  61. currentNav = menuList[0].childs[1];
  62. }
  63. loading = true;
  64. const componentStr = await getComponentStrFunc(currentNav.nav);
  65. loading = false;
  66. highlightedCode = hljs.highlight(componentStr, {
  67. language: 'svelte',
  68. ignoreIllegals: true
  69. }).value;
  70. };
  71. onMount(() => {
  72. if (titleDom) {
  73. demoHeight = document.documentElement.clientHeight - 70 - titleDom.offsetHeight - 100;
  74. navBarHeight = document.documentElement.clientHeight - 56;
  75. }
  76. window.addEventListener('resize', getDemoHeightFun);
  77. initFn();
  78. return () => {
  79. window.removeEventListener('resize', getDemoHeightFun);
  80. };
  81. });
  82. //监听箭头按键,方便键盘操作
  83. const handleKeydown = (event: KeyboardEvent) => {
  84. if ($isCmdKStore) {
  85. return;
  86. }
  87. //左右箭头切换 Tab
  88. if (event.code === 'ArrowLeft') {
  89. if (currentTab > 0) {
  90. isShowIframe = false;
  91. setTimeout(() => {
  92. isShowIframe = true;
  93. }, 10);
  94. currentTab--;
  95. params.set('tab', currentTab.toString());
  96. goto(`/components?nav=${params.get('nav') ? params.get('nav') : 'button'}&tab=${currentTab}`);
  97. }
  98. }
  99. if (event.code === 'ArrowRight') {
  100. if (currentTab < 4) {
  101. isShowIframe = false;
  102. setTimeout(() => {
  103. isShowIframe = true;
  104. }, 10);
  105. currentTab++;
  106. params.set('tab', currentTab.toString());
  107. goto(`/components?nav=${params.get('nav') ? params.get('nav') : 'button'}&tab=${currentTab}`);
  108. }
  109. }
  110. //上下箭头切换组件
  111. if (event.code === 'ArrowUp') {
  112. let currentMenuIndex = menuChildList.findIndex((item) => item.nav === currentNav?.nav);
  113. if (currentMenuIndex > 0) {
  114. visible = false;
  115. menuChange = false;
  116. setTimeout(async () => {
  117. visible = true;
  118. currentNav = menuChildList[currentMenuIndex - 1];
  119. params.set('nav', currentNav.nav);
  120. goto(`/components?nav=${currentNav.nav}&tab=${currentTab}`);
  121. const componentStr = await getComponentStrFunc(currentNav.nav);
  122. highlightedCode = hljs.highlight(componentStr, {
  123. language: 'svelte',
  124. ignoreIllegals: true
  125. }).value;
  126. menuChange = true;
  127. }, 10);
  128. }
  129. }
  130. if (event.code === 'ArrowDown') {
  131. let currentMenuIndex = menuChildList.findIndex((item) => item.nav === currentNav?.nav);
  132. if (currentMenuIndex < menuChildList.length - 1) {
  133. visible = false;
  134. setTimeout(async () => {
  135. visible = true;
  136. menuChange = false;
  137. currentNav = menuChildList[currentMenuIndex + 1];
  138. params.set('nav', currentNav.nav);
  139. goto(`/components?nav=${currentNav.nav}&tab=${currentTab}`);
  140. const componentStr = await getComponentStrFunc(currentNav.nav);
  141. highlightedCode = hljs.highlight(componentStr, {
  142. language: 'svelte',
  143. ignoreIllegals: true
  144. }).value;
  145. menuChange = true;
  146. }, 10);
  147. }
  148. }
  149. };
  150. const menuClickFun = (currentMenu: MenuListChild) => {
  151. $isShowNavStore && ($isShowNavStore = false);
  152. visible = false;
  153. menuChange = false;
  154. setTimeout(async () => {
  155. visible = true;
  156. currentNav = currentMenu;
  157. params.set('nav', currentMenu.nav);
  158. goto(`/components?nav=${currentMenu.nav}&tab=${params.get('tab') ? params.get('tab') : '0'}`);
  159. const componentStr = await getComponentStrFunc(currentMenu.nav);
  160. highlightedCode = hljs.highlight(componentStr, {
  161. language: 'svelte',
  162. ignoreIllegals: true
  163. }).value;
  164. menuChange = true;
  165. }, 10);
  166. };
  167. const tabClickFun = (i: number) => {
  168. isShowIframe = false;
  169. setTimeout(() => {
  170. isShowIframe = true;
  171. }, 10);
  172. currentTab = i;
  173. params.set('tab', currentTab.toString());
  174. goto(`/components?nav=${params.get('nav') ? params.get('nav') : 'button'}&tab=${currentTab}`);
  175. };
  176. // main -> next
  177. let QRValue = $derived(
  178. (import.meta.env.DEV ? location.protocol + '//' + location.hostname + ':8888/' : 'https://next-demo.stdf.design/') +
  179. currentNav?.nav +
  180. (isZh ? '/zh_CN' : '/en_US')
  181. );
  182. </script>
  183. <svelte:window onkeydown={handleKeydown} />
  184. <div class="flex">
  185. <div
  186. class="fixed -left-52 top-14 z-[100] w-52 overflow-y-scroll border-r border-black/10 bg-white transition-all duration-300 md:left-0 md:bg-transparent dark:border-white/20 dark:bg-black dark:md:bg-transparent"
  187. class:left-0={$isShowNavStore}
  188. class:-left-52={!$isShowNavStore}
  189. style="height:{navBarHeight + 'px'}"
  190. >
  191. <Menu {menuList} currentNav={currentNav?.nav} onclickMenu={menuClickFun} />
  192. </div>
  193. {#if visible}
  194. <div class="flex w-full flex-col md:pl-48" in:fly={{ x: 100, duration: 500 }} out:fly={{ x: 100, duration: 100 }}>
  195. <div class="relative z-20 px-4 pt-4 md:flex md:space-x-4 md:px-8" bind:this={titleDom}>
  196. {#if loading}
  197. {isZh ? '请等待...' : 'Please wait...'}
  198. {:else}
  199. <div class="flex items-center text-2xl font-bold md:text-4xl">
  200. <div>
  201. {isZh ? currentNav?.title : currentNav?.title_en}
  202. </div>
  203. <a href={QRValue} target="_blank" aria-label={isZh ? '扫码预览' : 'Scan to preview'}>
  204. <div class="ml-2 h-8 w-8 rounded bg-gray-100 p-1 text-gray-500 md:hidden dark:bg-gray-700">
  205. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" style="fill: currentColor">
  206. <path fill="none" d="M0 0h24v24H0z" />
  207. <path
  208. d="M13 21v2h-2v-2H3a1 1 0 0 1-1-1V6h20v14a1 1 0 0 1-1 1h-8zm-9-2h16V8H4v11zm9-9h5v2h-5v-2zm0 4h5v2h-5v-2zm-4-4v3h3a3 3 0 1 1-3-3zM2 3h20v2H2V3z"
  209. />
  210. </svg>
  211. </div>
  212. </a>
  213. </div>
  214. <div class="mt-4 text-xs text-gray-500 md:text-sm">
  215. {isZh ? currentNav?.tip : currentNav?.tip_en}
  216. </div>
  217. {/if}
  218. </div>
  219. <div class="relative z-10 -mt-14 p-4 md:px-8 md:py-4" bind:clientWidth={titleWidth}>
  220. <Tab {currentTab} onclickTab={tabClickFun} />
  221. </div>
  222. <div class="my-4 ml-4">
  223. <div class="h-px bg-black/10 dark:bg-white/20"></div>
  224. </div>
  225. <div>
  226. <!-- 示例 -->
  227. {#if currentTab === 0}
  228. <div
  229. in:fly={{ y: 100, duration: 500 }}
  230. out:fly={{ y: 100, duration: 100 }}
  231. class="mt-2 flex flex-1 justify-around py-4 md:pl-8 md:pr-4"
  232. style="width:{titleWidth}px;"
  233. >
  234. <div class="bg-codeLight dark:bg-codeDark grow overflow-y-scroll rounded" style="height:{demoHeight}px;">
  235. <Component {highlightedCode} />
  236. </div>
  237. <div
  238. class="ml-2 hidden w-[392px] shrink-0 grow-0 border border-black/10 md:block dark:border-white/10"
  239. style="height:{demoHeight}px"
  240. >
  241. {#if currentNav?.nav}
  242. {#if isShowIframe}
  243. <!-- main -> next -->
  244. <iframe
  245. title="Demo"
  246. id="iframe-id"
  247. src={import.meta.env.DEV
  248. ? `http://localhost:8888/${currentNav?.nav}/${isZh ? 'zh_CN' : 'en_US'}?channel=iframe`
  249. : `https://next-demo.stdf.design/${currentNav?.nav}/${isZh ? 'zh_CN' : 'en_US'}?channel=iframe`}
  250. height={demoHeight - 2}
  251. width="390"
  252. ></iframe>
  253. {:else}
  254. <div class="text-primary dark:text-dark flex flex-col justify-center" style="width:390px;height:{demoHeight - 2}px;">
  255. <div>
  256. <svg class="mx-auto my-1 h-10 w-10 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
  257. <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
  258. <path
  259. class="opacity-75"
  260. fill="currentColor"
  261. d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
  262. />
  263. </svg>
  264. </div>
  265. </div>
  266. {/if}
  267. {/if}
  268. </div>
  269. </div>
  270. {/if}
  271. {#if currentTab === 1}
  272. <!-- api -->
  273. {#if menuChange}
  274. <div
  275. in:fly={{ y: 100, duration: 500 }}
  276. out:fly={{ y: 100, duration: 100 }}
  277. class={` left-full mt-4 flex-1 p-4 md:px-8 md:py-4`}
  278. style="width:{titleWidth}px;"
  279. >
  280. <Api api={currentNav?.nav} />
  281. </div>
  282. {/if}
  283. {/if}
  284. {#if currentTab === 2}
  285. <!-- 指南 -->
  286. {#if menuChange}
  287. <div
  288. in:fly={{ y: 100, duration: 500 }}
  289. out:fly={{ y: 100, duration: 100 }}
  290. class={`mt-4 flex-1 rounded p-4 md:px-8 md:py-4 `}
  291. style="width:{titleWidth}px;"
  292. >
  293. <Guide guide={currentNav?.nav} />
  294. </div>
  295. {/if}
  296. {/if}
  297. {#if currentTab === 3}
  298. <!-- FAQ -->
  299. {#if menuChange}
  300. <div
  301. in:fly={{ y: 100, duration: 500 }}
  302. out:fly={{ y: 100, duration: 100 }}
  303. class={`mt-4 flex-1 rounded p-4 md:px-8 md:py-4 `}
  304. style="width:{titleWidth}px;"
  305. >
  306. <FAQ guide={currentNav?.nav} />
  307. </div>
  308. {/if}
  309. {/if}
  310. {#if currentTab === 4}
  311. <!-- Version -->
  312. {#if menuChange}
  313. <div
  314. in:fly={{ y: 100, duration: 500 }}
  315. out:fly={{ y: 100, duration: 100 }}
  316. class={`mt-4 flex-1 rounded p-4 md:px-8 md:py-4 `}
  317. style="width:{titleWidth}px;"
  318. >
  319. <Version guide={currentNav?.nav} />
  320. </div>
  321. {/if}
  322. {/if}
  323. </div>
  324. </div>
  325. {/if}
  326. </div>