@umijs/plugin-docs 4.0.0-rc.11 → 4.0.0-rc.14
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.
- package/client/theme-doc/Head.tsx +28 -16
- package/client/theme-doc/Layout.tsx +28 -23
- package/client/theme-doc/Logo.tsx +9 -5
- package/client/theme-doc/NavBar.tsx +58 -15
- package/client/theme-doc/Search.tsx +40 -21
- package/client/theme-doc/Sidebar.tsx +4 -2
- package/client/theme-doc/ThemeSwitch.tsx +8 -0
- package/client/theme-doc/Toc.tsx +12 -14
- package/client/theme-doc/components/Features.tsx +1 -1
- package/client/theme-doc/components/Message.tsx +31 -19
- package/client/theme-doc/context.ts +7 -1
- package/client/theme-doc/tailwind.css +125 -27
- package/client/theme-doc/tailwind.out.css +450 -134
- package/client/theme-doc/utils/getLinkFromTitle.ts +15 -0
- package/client/theme-doc/utils/getTocTitle.ts +9 -0
- package/compiled/@mdx-js/mdx/index.js +1 -1
- package/compiled/remark-gfm/LICENSE +22 -0
- package/compiled/remark-gfm/index.js +1 -0
- package/compiled/remark-gfm/package.json +1 -0
- package/dist/compiler.js +33 -4
- package/dist/index.js +3 -3
- package/dist/loader.js +13 -4
- package/package.json +15 -9
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import cx from 'classnames';
|
|
2
2
|
import React from 'react';
|
|
3
|
+
import { useThemeContext } from './context';
|
|
3
4
|
import Github from './Github';
|
|
4
5
|
import LangSwitch from './LangSwitch';
|
|
5
6
|
import Logo from './Logo';
|
|
@@ -13,27 +14,41 @@ interface HeadProps {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export default (props: HeadProps) => {
|
|
17
|
+
const { themeConfig } = useThemeContext()!;
|
|
16
18
|
return (
|
|
17
19
|
<div
|
|
18
20
|
className="w-full flex flex-row items-center justify-between
|
|
19
|
-
border-b-gray-100 border-b-2 pt-4 pb-4 px-
|
|
21
|
+
border-b-gray-100 border-b-2 pt-4 pb-4 px-4 lg:px-12 dark:border-b-gray-800"
|
|
20
22
|
>
|
|
21
|
-
<
|
|
23
|
+
<div className="flex flex-row items-center">
|
|
24
|
+
<Logo />
|
|
25
|
+
{themeConfig.extraNavLeft && <themeConfig.extraNavLeft />}
|
|
26
|
+
</div>
|
|
22
27
|
<div className="flex flex-row items-center">
|
|
23
28
|
<Search />
|
|
24
|
-
|
|
29
|
+
{/* 小屏幕显示打开菜单的按钮 */}
|
|
30
|
+
<div
|
|
31
|
+
className="block lg:hidden ml-2 cursor-pointer"
|
|
32
|
+
onClick={() => props.setMenuOpened((o) => !o)}
|
|
33
|
+
>
|
|
34
|
+
<HamburgerButton {...props} />
|
|
35
|
+
</div>
|
|
36
|
+
{/* 大屏幕显示完整的操作按钮 */}
|
|
25
37
|
<div className="hidden lg:block">
|
|
26
38
|
<NavBar />
|
|
27
39
|
</div>
|
|
28
40
|
<div className="ml-4 hidden lg:block">
|
|
29
41
|
<LangSwitch />
|
|
30
42
|
</div>
|
|
31
|
-
|
|
32
|
-
<
|
|
33
|
-
|
|
43
|
+
{themeConfig.themeSwitch && (
|
|
44
|
+
<div className="ml-4 hidden lg:block">
|
|
45
|
+
<ThemeSwitch />
|
|
46
|
+
</div>
|
|
47
|
+
)}
|
|
34
48
|
<div className="ml-4 hidden lg:block">
|
|
35
49
|
<Github />
|
|
36
50
|
</div>
|
|
51
|
+
{themeConfig.extraNavRight && <themeConfig.extraNavRight />}
|
|
37
52
|
</div>
|
|
38
53
|
</div>
|
|
39
54
|
);
|
|
@@ -41,30 +56,27 @@ export default (props: HeadProps) => {
|
|
|
41
56
|
|
|
42
57
|
interface HamburgerButtonProps {
|
|
43
58
|
isMenuOpened: boolean;
|
|
44
|
-
setMenuOpened: React.Dispatch<React.SetStateAction<boolean>>;
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
function HamburgerButton(props: HamburgerButtonProps) {
|
|
62
|
+
const { isMenuOpened } = props;
|
|
48
63
|
const barClass =
|
|
49
|
-
'
|
|
50
|
-
'
|
|
64
|
+
'absolute h-0.5 w-5 -translate-x-2.5 bg-current transform dark:bg-white ' +
|
|
65
|
+
'transition duration-500 ease-in-out';
|
|
51
66
|
|
|
52
67
|
return (
|
|
53
|
-
<div
|
|
54
|
-
className="relative py-3 sm:max-w-xl mx-auto mx-5 lg:hidden"
|
|
55
|
-
onClick={() => props.setMenuOpened((o) => !o)}
|
|
56
|
-
>
|
|
68
|
+
<div className="p-4">
|
|
57
69
|
<span
|
|
58
70
|
className={cx(
|
|
59
71
|
barClass,
|
|
60
|
-
|
|
72
|
+
isMenuOpened ? 'rotate-45 ' : '-translate-y-1.5',
|
|
61
73
|
)}
|
|
62
74
|
/>
|
|
63
|
-
<span className={cx(barClass,
|
|
75
|
+
<span className={cx(barClass, isMenuOpened && 'opacity-0')} />
|
|
64
76
|
<span
|
|
65
77
|
className={cx(
|
|
66
78
|
barClass,
|
|
67
|
-
|
|
79
|
+
isMenuOpened ? '-rotate-45' : 'translate-y-1.5',
|
|
68
80
|
)}
|
|
69
81
|
/>
|
|
70
82
|
</div>
|
|
@@ -44,7 +44,10 @@ export default (props: any) => {
|
|
|
44
44
|
location: props.location,
|
|
45
45
|
}}
|
|
46
46
|
>
|
|
47
|
-
<div
|
|
47
|
+
<div
|
|
48
|
+
className="flex flex-col dark:bg-gray-900 min-h-screen transition-all"
|
|
49
|
+
id={isHomePage ? 'home-page' : 'doc-page'}
|
|
50
|
+
>
|
|
48
51
|
<div
|
|
49
52
|
id="head-container"
|
|
50
53
|
className="z-30 sticky top-0 dark:before:bg-gray-800 before:bg-white before:bg-opacity-[.85]
|
|
@@ -60,6 +63,7 @@ export default (props: any) => {
|
|
|
60
63
|
|
|
61
64
|
{isHomePage ? (
|
|
62
65
|
<div id="article-body">
|
|
66
|
+
{/* @ts-ignore */}
|
|
63
67
|
<Helmet>
|
|
64
68
|
<title>
|
|
65
69
|
{title}
|
|
@@ -74,31 +78,32 @@ export default (props: any) => {
|
|
|
74
78
|
id="article-body"
|
|
75
79
|
className="w-full flex flex-row justify-center overflow-x-hidden"
|
|
76
80
|
>
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
{/* 左侧菜单 */}
|
|
82
|
+
<div
|
|
83
|
+
className="fixed left-0 top-0 w-1/4 flex flex-row
|
|
84
|
+
justify-center h-screen z-10 pt-20"
|
|
85
|
+
>
|
|
86
|
+
<div className="container flex flex-row justify-end">
|
|
87
|
+
<div className="hidden lg:block">
|
|
88
|
+
<Sidebar />
|
|
89
|
+
</div>
|
|
80
90
|
</div>
|
|
81
91
|
</div>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
justify-center h-screen z-10 pt-20"
|
|
87
|
-
>
|
|
88
|
-
<div className="container flex flex-row justify-end">
|
|
89
|
-
<div className="hidden lg:block">
|
|
90
|
-
<Sidebar />
|
|
92
|
+
{/* 文章内容 */}
|
|
93
|
+
<div className="container flex flex-row justify-center">
|
|
94
|
+
<div className="w-full lg:w-1/2 px-4 lg:px-2 m-8 z-20 lg:pb-12 lg:pt-6">
|
|
95
|
+
<article className="flex-1">{props.children}</article>
|
|
91
96
|
</div>
|
|
92
97
|
</div>
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
{/* 右侧 Toc */}
|
|
99
|
+
<div
|
|
100
|
+
className="fixed right-0 top-0 w-1/4 hidden lg:block flex-row
|
|
101
|
+
justify-center h-screen z-10 pt-20"
|
|
102
|
+
>
|
|
103
|
+
<div className="container flex flex-row justify-start">
|
|
104
|
+
<div className="w-2/3 top-32">
|
|
105
|
+
<Toc />
|
|
106
|
+
</div>
|
|
102
107
|
</div>
|
|
103
108
|
</div>
|
|
104
109
|
</div>
|
|
@@ -109,7 +114,7 @@ export default (props: any) => {
|
|
|
109
114
|
<div
|
|
110
115
|
className={cx(
|
|
111
116
|
'fixed top-12 w-screen bg-white z-20 dark:bg-gray-800',
|
|
112
|
-
'overflow-hidden transition-all duration-500',
|
|
117
|
+
'overflow-hidden transition-all duration-500 lg:hidden',
|
|
113
118
|
isMenuOpened ? 'max-h-screen' : 'max-h-0',
|
|
114
119
|
)}
|
|
115
120
|
>
|
|
@@ -5,15 +5,19 @@ import useLanguage from './useLanguage';
|
|
|
5
5
|
export default () => {
|
|
6
6
|
const { themeConfig, components } = useThemeContext()!;
|
|
7
7
|
const { isFromPath, currentLanguage } = useLanguage();
|
|
8
|
-
|
|
9
|
-
// @ts-ignore
|
|
10
|
-
const { logo } = themeConfig;
|
|
8
|
+
const Logo = themeConfig.logo;
|
|
11
9
|
|
|
12
10
|
return (
|
|
13
11
|
<components.Link to={isFromPath ? '/' + currentLanguage?.locale : '/'}>
|
|
14
12
|
<div className="flex flex-row items-center">
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
{typeof Logo === 'string' && (
|
|
14
|
+
<img src={Logo} className="w-8 h-8" alt="logo" />
|
|
15
|
+
)}
|
|
16
|
+
{typeof Logo === 'function' && <Logo />}
|
|
17
|
+
<div
|
|
18
|
+
id="header-title"
|
|
19
|
+
className="text-xl font-extrabold ml-2 dark:text-white"
|
|
20
|
+
>
|
|
17
21
|
{themeConfig.title}
|
|
18
22
|
</div>
|
|
19
23
|
</div>
|
|
@@ -1,27 +1,70 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import { useThemeContext } from './context';
|
|
3
3
|
import useLanguage from './useLanguage';
|
|
4
4
|
|
|
5
5
|
export default () => {
|
|
6
|
-
const {
|
|
7
|
-
const lang = useLanguage();
|
|
6
|
+
const { themeConfig } = useThemeContext()!;
|
|
8
7
|
return (
|
|
9
8
|
<ul className="flex">
|
|
10
|
-
{themeConfig.navs.map((nav: any) =>
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
{themeConfig.navs.map((nav: any) => (
|
|
10
|
+
<NavItem nav={nav} key={nav.path} />
|
|
11
|
+
))}
|
|
12
|
+
</ul>
|
|
13
|
+
);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
interface NavItemProps {
|
|
17
|
+
nav: {
|
|
18
|
+
path: string;
|
|
19
|
+
title: string;
|
|
20
|
+
dropdown?: {
|
|
21
|
+
title: string;
|
|
22
|
+
path: string;
|
|
23
|
+
}[];
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function NavItem(props: NavItemProps) {
|
|
28
|
+
const { components } = useThemeContext()!;
|
|
29
|
+
const { nav } = props;
|
|
30
|
+
const lang = useLanguage();
|
|
31
|
+
const [isExpanded, setExpanded] = useState(false);
|
|
32
|
+
return (
|
|
33
|
+
<li
|
|
34
|
+
className="ml-8 dark:text-white relative"
|
|
35
|
+
onMouseEnter={() => nav.dropdown && setExpanded(true)}
|
|
36
|
+
onMouseLeave={() => nav.dropdown && setExpanded(false)}
|
|
37
|
+
>
|
|
38
|
+
<components.Link
|
|
39
|
+
to={
|
|
40
|
+
lang.isFromPath ? lang.currentLanguage?.locale + nav.path : nav.path
|
|
41
|
+
}
|
|
42
|
+
>
|
|
43
|
+
{lang.render(nav.title)}
|
|
44
|
+
</components.Link>
|
|
45
|
+
{nav.dropdown && (
|
|
46
|
+
<div
|
|
47
|
+
style={{ maxHeight: isExpanded ? nav.dropdown.length * 48 : 0 }}
|
|
48
|
+
className="absolute transition-all duration-300 w-32 rounded-lg
|
|
49
|
+
cursor-pointer shadow overflow-hidden top-8"
|
|
50
|
+
>
|
|
51
|
+
{nav.dropdown.map((n) => (
|
|
13
52
|
<components.Link
|
|
53
|
+
key={n.path}
|
|
14
54
|
to={
|
|
15
|
-
lang.isFromPath
|
|
16
|
-
? lang.currentLanguage?.locale + nav.path
|
|
17
|
-
: nav.path
|
|
55
|
+
lang.isFromPath ? lang.currentLanguage?.locale + n.path : n.path
|
|
18
56
|
}
|
|
19
57
|
>
|
|
20
|
-
|
|
58
|
+
<p
|
|
59
|
+
className="p-2 bg-white dark:bg-gray-700 dark:text-white
|
|
60
|
+
hover:bg-gray-50 transition duration-300"
|
|
61
|
+
>
|
|
62
|
+
{n.title}
|
|
63
|
+
</p>
|
|
21
64
|
</components.Link>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
</
|
|
65
|
+
))}
|
|
66
|
+
</div>
|
|
67
|
+
)}
|
|
68
|
+
</li>
|
|
26
69
|
);
|
|
27
|
-
}
|
|
70
|
+
}
|
|
@@ -3,9 +3,11 @@ import key from 'keymaster';
|
|
|
3
3
|
import React, { Fragment, useEffect, useState } from 'react';
|
|
4
4
|
import { useThemeContext } from './context';
|
|
5
5
|
import useLanguage from './useLanguage';
|
|
6
|
+
import getLinkFromTitle from './utils/getLinkFromTitle';
|
|
6
7
|
|
|
7
8
|
export default () => {
|
|
8
|
-
const {
|
|
9
|
+
const { components } = useThemeContext()!;
|
|
10
|
+
const { isFromPath, currentLanguage, render } = useLanguage();
|
|
9
11
|
const [isFocused, setIsFocused] = useState(false);
|
|
10
12
|
const [keyword, setKeyword] = useState('');
|
|
11
13
|
|
|
@@ -34,6 +36,7 @@ export default () => {
|
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
useEffect(() => {
|
|
39
|
+
if (!themeConfig.searchHotKey) return;
|
|
37
40
|
key.filter = () => true;
|
|
38
41
|
|
|
39
42
|
// 在页面中按下 ⌘+k 可以打开搜索框
|
|
@@ -63,6 +66,7 @@ export default () => {
|
|
|
63
66
|
return (
|
|
64
67
|
<Fragment>
|
|
65
68
|
<div
|
|
69
|
+
id="search-input-wrapper"
|
|
66
70
|
className="rounded-lg w-40 lg:w-64 flex items-center pr-2 flex-row hover:bg-gray-50
|
|
67
71
|
transition duration-300 bg-gray-100 border border-white focus-within:border-gray-100
|
|
68
72
|
focus-within:bg-white dark:bg-gray-700 dark:border-gray-700 relative
|
|
@@ -72,18 +76,22 @@ export default () => {
|
|
|
72
76
|
onFocus={() => setIsFocused(true)}
|
|
73
77
|
onBlur={() => setIsFocused(false)}
|
|
74
78
|
value={keyword}
|
|
79
|
+
autoComplete="off"
|
|
75
80
|
onChange={(e) => setKeyword(e.target.value)}
|
|
76
81
|
id="search-input"
|
|
77
|
-
className="w-full bg-transparent outline-none text-sm px-4 py-2
|
|
82
|
+
className="w-full bg-transparent outline-none text-sm px-4 py-2"
|
|
78
83
|
placeholder={render('Search anything ...')}
|
|
79
84
|
/>
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
{themeConfig.searchHotKey && (
|
|
86
|
+
<div
|
|
87
|
+
className="bg-gray-200 rounded px-2 h-6 flex flex-row text-gray-400
|
|
82
88
|
items-center justify-center border border-gray-300 text-xs"
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
>
|
|
90
|
+
{isMac ? macSearchKey : windowsSearchKey}
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
86
93
|
<div
|
|
94
|
+
id="search-results-wrapper"
|
|
87
95
|
className={cx(
|
|
88
96
|
'absolute transition-all duration-500 top-12 w-96 rounded-lg',
|
|
89
97
|
'cursor-pointer shadow overflow-hidden',
|
|
@@ -91,8 +99,8 @@ export default () => {
|
|
|
91
99
|
)}
|
|
92
100
|
>
|
|
93
101
|
{result.map((r, i) => (
|
|
94
|
-
<
|
|
95
|
-
|
|
102
|
+
<components.Link
|
|
103
|
+
to={(isFromPath ? currentLanguage?.locale : '') + r.href}
|
|
96
104
|
key={i}
|
|
97
105
|
className="group outline-none search-result"
|
|
98
106
|
onFocus={() => setIsFocused(true)}
|
|
@@ -104,7 +112,7 @@ export default () => {
|
|
|
104
112
|
>
|
|
105
113
|
{r.path}
|
|
106
114
|
</p>
|
|
107
|
-
</
|
|
115
|
+
</components.Link>
|
|
108
116
|
))}
|
|
109
117
|
</div>
|
|
110
118
|
</div>
|
|
@@ -122,9 +130,15 @@ function search(routes: any, keyword: string): SearchResultItem[] {
|
|
|
122
130
|
|
|
123
131
|
const result: SearchResultItem[] = [];
|
|
124
132
|
|
|
133
|
+
function addResult(newResult: { path: string; href: string }) {
|
|
134
|
+
const { path, href } = newResult;
|
|
135
|
+
if (result.find((r) => r.path === path)) return;
|
|
136
|
+
result.push({ path, href });
|
|
137
|
+
}
|
|
138
|
+
|
|
125
139
|
Object.keys(routes).map((path) => {
|
|
126
140
|
if (path.toLowerCase().includes(keyword.toLowerCase())) {
|
|
127
|
-
|
|
141
|
+
addResult({
|
|
128
142
|
path: path.split('/').slice(1).join(' > '),
|
|
129
143
|
href: '/' + path,
|
|
130
144
|
});
|
|
@@ -132,16 +146,21 @@ function search(routes: any, keyword: string): SearchResultItem[] {
|
|
|
132
146
|
|
|
133
147
|
const route = routes[path];
|
|
134
148
|
if (!route.titles) return;
|
|
135
|
-
route.titles
|
|
136
|
-
.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
149
|
+
route.titles.map((title: any) => {
|
|
150
|
+
if (title.title.toLowerCase().includes(keyword.toLowerCase())) {
|
|
151
|
+
addResult({
|
|
152
|
+
path:
|
|
153
|
+
path
|
|
154
|
+
.split('/')
|
|
155
|
+
.map((s) => s.replace(/\.[a-z]{2}-[A-Z]{2}\/?/g, ''))
|
|
156
|
+
.slice(1)
|
|
157
|
+
.join(' > ') +
|
|
158
|
+
' > ' +
|
|
159
|
+
title.title,
|
|
160
|
+
href: '/' + path + '#' + getLinkFromTitle(title.title),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
});
|
|
145
164
|
});
|
|
146
165
|
|
|
147
166
|
if (result.length > 8) return result.slice(0, 8);
|
|
@@ -28,7 +28,7 @@ export default (props: SidebarProps) => {
|
|
|
28
28
|
<ul
|
|
29
29
|
className={cx(
|
|
30
30
|
'h-screen lg:h-[calc(100vh-8rem)] overflow-y-scroll',
|
|
31
|
-
'lg:w-64
|
|
31
|
+
'lg:w-64 px-8 pt-12 lg:pt-8 pb-36 fadeout w-full',
|
|
32
32
|
)}
|
|
33
33
|
>
|
|
34
34
|
{(matchedNav.children || []).map((item) => {
|
|
@@ -49,7 +49,8 @@ export default (props: SidebarProps) => {
|
|
|
49
49
|
if (to === window.location.pathname) {
|
|
50
50
|
return (
|
|
51
51
|
<div
|
|
52
|
-
|
|
52
|
+
id="active-nav-item"
|
|
53
|
+
key={route.path}
|
|
53
54
|
className="my-2 hover:text-blue-400 transition-all
|
|
54
55
|
bg-blue-50 text-blue-400 px-4 py-1
|
|
55
56
|
rounded-lg cursor-default dark:bg-blue-900 dark:text-blue-200"
|
|
@@ -61,6 +62,7 @@ export default (props: SidebarProps) => {
|
|
|
61
62
|
|
|
62
63
|
return (
|
|
63
64
|
<components.Link
|
|
65
|
+
key={route.path}
|
|
64
66
|
to={route.path}
|
|
65
67
|
onClick={() =>
|
|
66
68
|
props.setMenuOpened && props.setMenuOpened((o) => !o)
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import cx from 'classnames';
|
|
2
2
|
import React, { useEffect, useState } from 'react';
|
|
3
|
+
import { useThemeContext } from './context';
|
|
3
4
|
import MoonIcon from './icons/moon.png';
|
|
4
5
|
import SunIcon from './icons/sun.png';
|
|
5
6
|
|
|
6
7
|
export default () => {
|
|
7
8
|
const [toggle, setToggle] = useState<Boolean>();
|
|
9
|
+
const { themeConfig } = useThemeContext()!;
|
|
8
10
|
|
|
9
11
|
useEffect(() => {
|
|
12
|
+
// If themeConfig disabled the themeSwitch, just set to light theme
|
|
13
|
+
if (!themeConfig.themeSwitch) {
|
|
14
|
+
document.body.classList.remove('dark');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
10
18
|
// 初始化,获取过去曾经设定过的主题,或是系统当前的主题
|
|
11
19
|
if (toggle === undefined) {
|
|
12
20
|
if (localStorage.getItem('theme') === 'dark') {
|
package/client/theme-doc/Toc.tsx
CHANGED
|
@@ -2,13 +2,8 @@ import React from 'react';
|
|
|
2
2
|
import { Helmet } from 'react-helmet';
|
|
3
3
|
import { useThemeContext } from './context';
|
|
4
4
|
import useLanguage from './useLanguage';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return title
|
|
8
|
-
.toLowerCase()
|
|
9
|
-
.replace(/\s/g, '-')
|
|
10
|
-
.replace(/[()]/g, '');
|
|
11
|
-
}
|
|
5
|
+
import getLinkFromTitle from './utils/getLinkFromTitle';
|
|
6
|
+
import getTocTitle from './utils/getTocTitle';
|
|
12
7
|
|
|
13
8
|
export default () => {
|
|
14
9
|
const { location, appData, themeConfig } = useThemeContext()!;
|
|
@@ -30,29 +25,32 @@ export default () => {
|
|
|
30
25
|
return (
|
|
31
26
|
<div
|
|
32
27
|
className="w-full lg:m-12 mb-12 border
|
|
33
|
-
border-gray-
|
|
28
|
+
border-gray-200 dark:border-neutral-700 py-4 rounded-lg z-20"
|
|
34
29
|
>
|
|
30
|
+
{/* @ts-ignore */}
|
|
35
31
|
<Helmet>
|
|
36
32
|
<title>
|
|
37
33
|
{route.titles[0].title} | {themeConfig.title}
|
|
38
34
|
</title>
|
|
39
35
|
</Helmet>
|
|
40
|
-
<p className="text-lg font-extrabold dark:text-
|
|
41
|
-
{route.titles[0].title}
|
|
36
|
+
<p className="text-lg font-extrabold text-gray-800 dark:text-neutral-50 pb-2 border-b border-gray-200 dark:border-neutral-700">
|
|
37
|
+
<span className="px-4">{route.titles[0].title}</span>
|
|
42
38
|
</p>
|
|
43
|
-
<ul className="max-h-[calc(100vh-360px)] overflow-y-auto
|
|
39
|
+
<ul className="max-h-[calc(100vh-360px)] overflow-y-auto px-4">
|
|
44
40
|
{titles.map((item: any) => {
|
|
45
41
|
return (
|
|
46
42
|
<li
|
|
47
43
|
style={{ paddingLeft: `${item.level - 2}rem` }}
|
|
48
|
-
className="mt-3 text-gray-600 cursor-pointer dark:text-
|
|
44
|
+
className="mt-3 text-gray-600 cursor-pointer dark:text-neutral-400
|
|
49
45
|
hover:text-blue-500 transition duration-300 dark:hover:text-blue-500"
|
|
50
46
|
>
|
|
51
47
|
<a
|
|
52
|
-
className={
|
|
48
|
+
className={`${
|
|
49
|
+
item.level > 2 ? 'text-sm' : 'text-base'
|
|
50
|
+
} break-all 2xl:break-words`}
|
|
53
51
|
href={'#' + getLinkFromTitle(item.title)}
|
|
54
52
|
>
|
|
55
|
-
{item.title}
|
|
53
|
+
{getTocTitle(item.title)}
|
|
56
54
|
</a>
|
|
57
55
|
</li>
|
|
58
56
|
);
|
|
@@ -8,7 +8,7 @@ function Features(
|
|
|
8
8
|
props: PropsWithChildren<{ title?: string; subtitle?: string }>,
|
|
9
9
|
) {
|
|
10
10
|
return (
|
|
11
|
-
<div className="w-
|
|
11
|
+
<div className="w-full py-36 features dark:features-dark min-h-screen">
|
|
12
12
|
{(props.title || props.subtitle) && (
|
|
13
13
|
<div className="mb-24 px-4">
|
|
14
14
|
{props.title && (
|
|
@@ -9,11 +9,14 @@ enum MessageType {
|
|
|
9
9
|
|
|
10
10
|
interface MessageProps {
|
|
11
11
|
type?: MessageType;
|
|
12
|
-
emoji?: string;
|
|
12
|
+
emoji?: string | boolean;
|
|
13
|
+
title?: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
function Message(props: PropsWithChildren<MessageProps>) {
|
|
16
|
-
const messageType = props.type ||
|
|
17
|
+
const messageType = props.type || MessageType.Info;
|
|
18
|
+
const messageTitle = props.title;
|
|
19
|
+
const propsChildren = props.children;
|
|
17
20
|
|
|
18
21
|
let messageClass: string;
|
|
19
22
|
switch (messageType) {
|
|
@@ -30,26 +33,35 @@ function Message(props: PropsWithChildren<MessageProps>) {
|
|
|
30
33
|
messageClass = 'mdx-message-info';
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
let messageEmoji = props.emoji;
|
|
37
|
+
if (!messageEmoji && messageEmoji !== false) {
|
|
38
|
+
switch (messageType) {
|
|
39
|
+
case MessageType.Success:
|
|
40
|
+
messageEmoji = '🏆︎';
|
|
41
|
+
break;
|
|
42
|
+
case MessageType.Warning:
|
|
43
|
+
messageEmoji = '🛎️';
|
|
44
|
+
break;
|
|
45
|
+
case MessageType.Error:
|
|
46
|
+
messageEmoji = '⚠️';
|
|
47
|
+
break;
|
|
48
|
+
default:
|
|
49
|
+
messageEmoji = '💡';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
37
52
|
|
|
38
53
|
return (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
)}
|
|
49
|
-
{messageText}
|
|
50
|
-
</p>
|
|
54
|
+
<div
|
|
55
|
+
className={`flex w-full py-5 px-4 rounded-lg my-4 mdx-message ${messageClass}`}
|
|
56
|
+
>
|
|
57
|
+
<span role="img" className="mr-3 dark:text-white">
|
|
58
|
+
{messageEmoji}
|
|
59
|
+
</span>
|
|
60
|
+
<div className="flex-grow">
|
|
61
|
+
{messageTitle && <h5 className="mt-0 mb-3">{messageTitle}</h5>}
|
|
62
|
+
<div className="mdx-message-text">{propsChildren}</div>
|
|
51
63
|
</div>
|
|
52
|
-
|
|
64
|
+
</div>
|
|
53
65
|
);
|
|
54
66
|
}
|
|
55
67
|
|
|
@@ -9,21 +9,27 @@ interface IContext {
|
|
|
9
9
|
github: string;
|
|
10
10
|
// 键盘搜索的快捷键,参考 https://github.com/madrobby/keymaster
|
|
11
11
|
searchHotKey?: string | { macos: string; windows: string };
|
|
12
|
-
logo: string;
|
|
12
|
+
logo: string | React.ComponentType;
|
|
13
13
|
// 在设置文件中声明该项目的国际化功能支持的语言
|
|
14
14
|
i18n?: { locale: string; text: string }[];
|
|
15
15
|
// 插件会从 docs/locales 内将所有 json 文件注入到 themeConfig 中
|
|
16
16
|
// 供 useLanguage 使用
|
|
17
17
|
locales: { [locale: string]: { [key: string]: string } };
|
|
18
|
+
// 顶部导航栏右侧自定义组件
|
|
19
|
+
extraNavRight?: React.ComponentType;
|
|
20
|
+
// 底部导航栏左侧自定义组件
|
|
21
|
+
extraNavLeft?: React.ComponentType;
|
|
18
22
|
navs: {
|
|
19
23
|
path: string;
|
|
20
24
|
title: string;
|
|
25
|
+
dropdown?: { title: string; path: string }[];
|
|
21
26
|
children: any[];
|
|
22
27
|
}[];
|
|
23
28
|
announcement?: {
|
|
24
29
|
title: string;
|
|
25
30
|
link?: string;
|
|
26
31
|
};
|
|
32
|
+
themeSwitch?: {};
|
|
27
33
|
};
|
|
28
34
|
location: {
|
|
29
35
|
pathname: string;
|