@umijs/plugin-docs 4.0.0-rc.1 → 4.0.0-rc.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.
@@ -0,0 +1 @@
1
+ export function $Layout() {}
@@ -1,10 +1,17 @@
1
- import React, { useState } from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
2
  import useLanguage from './useLanguage';
3
3
 
4
4
  export default () => {
5
- const { currentLanguage, languages, switchLanguage } = useLanguage();
5
+ const { currentLanguage, languages, switchLanguage, isFromPath } =
6
+ useLanguage();
6
7
  const [isExpanded, setExpanded] = useState(false);
7
8
 
9
+ // 首次加载时,根据 localstorage 记录的上次语言自动切换
10
+ useEffect(() => {
11
+ const locale = window.localStorage.getItem('umi_locale');
12
+ if (locale && !isFromPath) switchLanguage(locale);
13
+ }, []);
14
+
8
15
  if (!currentLanguage) {
9
16
  return null;
10
17
  }
@@ -25,7 +32,7 @@ export default () => {
25
32
  return (
26
33
  <div>
27
34
  <div
28
- className="w-24 rounded-lg overflow-hidden cursor-pointer border
35
+ className="w-24 rounded-lg overflow-hidden cursor-pointer border text-center
29
36
  border-white hover:border-gray-100 dark:border-gray-800"
30
37
  onClick={handleClick}
31
38
  >
@@ -1,5 +1,6 @@
1
1
  import cx from 'classnames';
2
- import React, { Fragment, useState } from 'react';
2
+ import React, { Fragment, useEffect, useState } from 'react';
3
+ import { Helmet } from 'react-helmet';
3
4
  import Announcement from './components/Announcement';
4
5
  import { ThemeContext } from './context';
5
6
  import Head from './Head';
@@ -9,6 +10,31 @@ import Toc from './Toc';
9
10
  export default (props: any) => {
10
11
  const [isMenuOpened, setIsMenuOpened] = useState(false);
11
12
 
13
+ /**
14
+ FireFox CSS backdrop-filter polyfill
15
+ https://www.cnblogs.com/coco1s/p/14953143.html
16
+ */
17
+ useEffect(() => {
18
+ let blur = document.getElementById('firefox-head-bg')?.style;
19
+ let offset = document.getElementById('head-container');
20
+
21
+ function updateBlur() {
22
+ if (!offset || !blur) return;
23
+ blur.backgroundPosition = `0px ` + `${-window.scrollY + 100}px`;
24
+ }
25
+
26
+ document.addEventListener('scroll', updateBlur, false), updateBlur();
27
+ return () => {
28
+ document.removeEventListener('scroll', updateBlur, false);
29
+ };
30
+ }, []);
31
+
32
+ const { title, description } = props.themeConfig;
33
+
34
+ const isHomePage =
35
+ window.location.pathname === '/' ||
36
+ window.location.pathname.replace(/[a-z]{2}-[A-Z]{2}\/?/, '') === '/';
37
+
12
38
  return (
13
39
  <ThemeContext.Provider
14
40
  value={{
@@ -20,6 +46,7 @@ export default (props: any) => {
20
46
  >
21
47
  <div className="flex flex-col dark:bg-gray-900 min-h-screen transition-all">
22
48
  <div
49
+ id="head-container"
23
50
  className="z-30 sticky top-0 dark:before:bg-gray-800 before:bg-white before:bg-opacity-[.85]
24
51
  before:backdrop-blur-md before:absolute before:block dark:before:bg-opacity-[.85]
25
52
  before:w-full before:h-full before:z-[-1]"
@@ -28,13 +55,27 @@ export default (props: any) => {
28
55
  <Head setMenuOpened={setIsMenuOpened} isMenuOpened={isMenuOpened} />
29
56
  </div>
30
57
 
31
- {window.location.pathname === '/' ? (
32
- <div>{props.children}</div>
58
+ <div className="g-glossy-firefox-cover" />
59
+ <div className="g-glossy-firefox" id="firefox-head-bg" />
60
+
61
+ {isHomePage ? (
62
+ <div id="article-body">
63
+ <Helmet>
64
+ <title>
65
+ {title}
66
+ {description ? ` - ${description}` : ''}
67
+ </title>
68
+ </Helmet>
69
+ {props.children}
70
+ </div>
33
71
  ) : (
34
72
  <Fragment>
35
- <div className="w-full flex flex-row justify-center overflow-x-hidden">
73
+ <div
74
+ id="article-body"
75
+ className="w-full flex flex-row justify-center overflow-x-hidden"
76
+ >
36
77
  <div className="container flex flex-row justify-center">
37
- <div className="w-full lg:w-1/2 px-4 lg:px-0 m-8 z-20 lg:py-12">
78
+ <div className="w-full lg:w-1/2 px-4 lg:px-2 m-8 z-20 lg:py-12">
38
79
  <article className="flex-1">{props.children}</article>
39
80
  </div>
40
81
  </div>
@@ -1,18 +1,22 @@
1
1
  import React from 'react';
2
2
  import { useThemeContext } from './context';
3
+ import useLanguage from './useLanguage';
3
4
 
4
5
  export default () => {
5
- const { themeConfig } = useThemeContext()!;
6
+ const { themeConfig, components } = useThemeContext()!;
7
+ const { isFromPath, currentLanguage } = useLanguage();
8
+
6
9
  // @ts-ignore
7
10
  const { logo } = themeConfig;
11
+
8
12
  return (
9
- <a href="/">
13
+ <components.Link to={isFromPath ? '/' + currentLanguage?.locale : '/'}>
10
14
  <div className="flex flex-row items-center">
11
15
  <img src={logo} className="w-8 h-8" alt="logo" />
12
16
  <div className="text-xl font-extrabold ml-2 dark:text-white">
13
17
  {themeConfig.title}
14
18
  </div>
15
19
  </div>
16
- </a>
20
+ </components.Link>
17
21
  );
18
22
  };
@@ -65,7 +65,7 @@ export default () => {
65
65
  <div
66
66
  className="rounded-lg w-40 lg:w-64 flex items-center pr-2 flex-row hover:bg-gray-50
67
67
  transition duration-300 bg-gray-100 border border-white focus-within:border-gray-100
68
- focus-within:bg-white dark:bg-gray-700 dark:border-gray-700
68
+ focus-within:bg-white dark:bg-gray-700 dark:border-gray-700 relative
69
69
  dark:focus-within:border-gray-700 dark:focus-within:bg-gray-800 dark:text-gray-100"
70
70
  >
71
71
  <input
@@ -74,7 +74,7 @@ export default () => {
74
74
  value={keyword}
75
75
  onChange={(e) => setKeyword(e.target.value)}
76
76
  id="search-input"
77
- className="w-full bg-transparent outline-0 text-sm px-4 py-2 "
77
+ className="w-full bg-transparent outline-none text-sm px-4 py-2 "
78
78
  placeholder={render('Search anything ...')}
79
79
  />
80
80
  <div
@@ -85,7 +85,7 @@ export default () => {
85
85
  </div>
86
86
  <div
87
87
  className={cx(
88
- 'absolute transition-all duration-500 top-16 w-96 rounded-lg',
88
+ 'absolute transition-all duration-500 top-12 w-96 rounded-lg',
89
89
  'cursor-pointer shadow overflow-hidden',
90
90
  result.length > 0 && isFocused ? 'max-h-80' : 'max-h-0',
91
91
  )}
@@ -36,9 +36,9 @@ export default () => {
36
36
  return (
37
37
  <div
38
38
  className={cx(
39
- 'md:w-12 md:h-6 w-12 h-4 flex items-center bg-gray-300 rounded-full ',
39
+ 'md:w-12 md:h-6 w-12 h-4 flex items-center rounded-full ',
40
40
  'py-1 px-1.5 cursor-pointer',
41
- toggle ? 'bg-blue-300' : 'bg-gray-700',
41
+ toggle ? 'bg-gray-100' : 'bg-gray-700',
42
42
  )}
43
43
  onClick={() => setToggle(!toggle)}
44
44
  >
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { Helmet } from 'react-helmet';
2
3
  import { useThemeContext } from './context';
3
4
  import useLanguage from './useLanguage';
4
5
 
@@ -10,7 +11,7 @@ function getLinkFromTitle(title: string) {
10
11
  }
11
12
 
12
13
  export default () => {
13
- const { location, appData } = useThemeContext()!;
14
+ const { location, appData, themeConfig } = useThemeContext()!;
14
15
  const lang = useLanguage();
15
16
  const route =
16
17
  appData.routes[
@@ -31,10 +32,15 @@ export default () => {
31
32
  className="w-full lg:m-12 mb-12 border
32
33
  border-gray-100 p-8 rounded-xl z-20"
33
34
  >
35
+ <Helmet>
36
+ <title>
37
+ {route.titles[0].title} | {themeConfig.title}
38
+ </title>
39
+ </Helmet>
34
40
  <p className="text-lg font-extrabold dark:text-white">
35
41
  {route.titles[0].title}
36
42
  </p>
37
- <ul className="max-h-[calc(100vh-360px)] overflow-y-scroll py-2">
43
+ <ul className="max-h-[calc(100vh-360px)] overflow-y-auto py-2">
38
44
  {titles.map((item: any) => {
39
45
  return (
40
46
  <li
@@ -20,6 +20,13 @@ function Announcement() {
20
20
  }
21
21
  }, []);
22
22
 
23
+ useEffect(() => {
24
+ document.documentElement.style.setProperty(
25
+ '--anchor-offset',
26
+ (closed ? 0 : 28) + 'px',
27
+ );
28
+ }, [closed]);
29
+
23
30
  function close(e: React.MouseEvent) {
24
31
  e.preventDefault();
25
32
  if (!themeConfig.announcement) return;
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+
3
+ interface Feature {
4
+ icon: string;
5
+ title: string;
6
+ description: string;
7
+ link?: string;
8
+ }
9
+
10
+ /**
11
+ * Feature Item 组件是文档首页第二个 Feature 区块中的一个 Item,
12
+ * 从 docs/README.md 中使用 MDX 语法调用,必须被包含在 <Features /> 组件内
13
+ * */
14
+ function FeatureItem(props: Feature) {
15
+ return (
16
+ <div className="w-full md:w-1/2 lg:w-1/3 flex flex-row items-center justify-center mb-8 lg:mb-16">
17
+ <div
18
+ className="flex flex-col w-5/6 lg:w-3/4 items-center
19
+ bg-white dark:bg-gray-800 py-12 px-6 justify-center
20
+ border-gray-300 dark:border-gray-500 border transition-all hover:scale-105
21
+ rounded-xl shadow-lg hover:shadow-2xl h-72 lg:h-96 dark:shadow-gray-700"
22
+ >
23
+ <img src={props.icon} className="w-8 h-8" alt="feature-icon" />
24
+ <p
25
+ className="text-3xl font-extrabold
26
+ mt-4 mb-8 text-gray-900 dark:text-gray-200"
27
+ >
28
+ {props.title}
29
+ </p>
30
+ <p className="text-center text-gray-600 text-sm lg:text-base dark:text-gray-400">
31
+ {props.description}
32
+ </p>
33
+ {props.link && (
34
+ <a
35
+ href={props.link}
36
+ target="_blank"
37
+ rel="noreferrer"
38
+ className="mt-8 link-with-underline"
39
+ >
40
+ 深入了解
41
+ </a>
42
+ )}
43
+ </div>
44
+ </div>
45
+ );
46
+ }
47
+
48
+ export default FeatureItem;
@@ -0,0 +1,44 @@
1
+ import React, { PropsWithChildren } from 'react';
2
+
3
+ /**
4
+ * Features 组件是文档首页第二个 Feature 区块的容器,
5
+ * 从 docs/README.md 中使用 MDX 语法调用,可在内部传入 <FeatureItem /> 组件
6
+ * */
7
+ function Features(
8
+ props: PropsWithChildren<{ title?: string; subtitle?: string }>,
9
+ ) {
10
+ return (
11
+ <div className="w-screen py-36 features dark:features-dark min-h-screen">
12
+ {(props.title || props.subtitle) && (
13
+ <div className="mb-24 px-4">
14
+ {props.title && (
15
+ <p
16
+ className="text-4xl lg:text-5xl font-extrabold mb-4 text-center
17
+ text-gray-700 dark:text-gray-300"
18
+ >
19
+ {props.title}
20
+ </p>
21
+ )}
22
+ {props.subtitle && (
23
+ <p
24
+ className="text-lg lg:text-xl text-center
25
+ text-gray-500 dark:text-gray-400"
26
+ >
27
+ {props.subtitle}
28
+ </p>
29
+ )}
30
+ </div>
31
+ )}
32
+ <div className="w-full flex flex-row justify-center">
33
+ <div
34
+ className="w-full flex flex-row flex-wrap
35
+ features pb-12 dark:features-dark container"
36
+ >
37
+ {props.children}
38
+ </div>
39
+ </div>
40
+ </div>
41
+ );
42
+ }
43
+
44
+ export default Features;
@@ -1,5 +1,6 @@
1
1
  import cx from 'classnames';
2
2
  import React, { useEffect, useState } from 'react';
3
+ import { useThemeContext } from '../context';
3
4
  import Github from '../icons/github.svg';
4
5
  import HeroBackground from '../icons/hero-bg.svg';
5
6
  import Star from '../icons/star.png';
@@ -15,6 +16,7 @@ interface HeroProps {
15
16
  }
16
17
 
17
18
  function Hero(props: HeroProps) {
19
+ const { components } = useThemeContext()!;
18
20
  return (
19
21
  <div
20
22
  className="w-full h-[calc(100vh-60px)] bg-[rgb(16,37,62)] flex
@@ -57,14 +59,15 @@ function Hero(props: HeroProps) {
57
59
 
58
60
  <div className="flex flex-row items-center">
59
61
  {props.buttons?.map((button, i) => (
60
- <button
61
- onClick={() => (window.location.href = button.href)}
62
- key={i}
63
- className="text-white text-lg bg-blue-600 py-2 min-w-36 mx-4 px-4 rounded-xl shadow-xl
62
+ <components.Link to={button.href}>
63
+ <button
64
+ key={i}
65
+ className="text-white text-lg bg-blue-600 py-2 min-w-36 mx-4 px-4 rounded-xl shadow-xl
64
66
  shadow-blue-900 hover:shadow-blue-700 transition-all"
65
- >
66
- {button.label}
67
- </button>
67
+ >
68
+ {button.label}
69
+ </button>
70
+ </components.Link>
68
71
  ))}
69
72
 
70
73
  {props.githubRepo && <GithubStars repo={props.githubRepo} />}
@@ -13,35 +13,41 @@ interface MessageProps {
13
13
  }
14
14
 
15
15
  function Message(props: PropsWithChildren<MessageProps>) {
16
- let bgColor = 'bg-blue-50';
17
- let textColor = 'text-blue-900';
16
+ const messageType = props.type || 'info';
18
17
 
19
- switch (props.type) {
18
+ let messageClass: string;
19
+ switch (messageType) {
20
20
  case MessageType.Success:
21
- bgColor = 'bg-green-50';
22
- textColor = 'text-green-900';
21
+ messageClass = 'mdx-message-success';
23
22
  break;
24
23
  case MessageType.Warning:
25
- bgColor = 'bg-orange-50';
26
- textColor = 'text-orange-900';
24
+ messageClass = 'mdx-message-warning';
27
25
  break;
28
26
  case MessageType.Error:
29
- bgColor = 'bg-red-50';
30
- textColor = 'text-red-900';
27
+ messageClass = 'mdx-message-error';
31
28
  break;
29
+ default:
30
+ messageClass = 'mdx-message-info';
32
31
  }
33
32
 
33
+ const messageText =
34
+ typeof props.children === 'string'
35
+ ? props.children
36
+ : (props.children as React.ReactElement).props.children;
37
+
34
38
  return (
35
39
  <>
36
40
  <div
37
- className={`w-full py-3 px-4 ${bgColor} ${textColor} rounded-lg my-2`}
41
+ className={`w-full py-3 px-4 rounded-lg my-4 mdx-message ${messageClass}`}
38
42
  >
39
- {props.emoji && (
40
- <span role="img" className="mr-3">
41
- {props.emoji}
42
- </span>
43
- )}
44
- {props.children}
43
+ <p>
44
+ {props.emoji && (
45
+ <span role="img" className="mr-3 inline">
46
+ {props.emoji}
47
+ </span>
48
+ )}
49
+ {messageText}
50
+ </p>
45
51
  </div>
46
52
  </>
47
53
  );
@@ -5,6 +5,7 @@ interface IContext {
5
5
  components: any;
6
6
  themeConfig: {
7
7
  title: string;
8
+ description?: string;
8
9
  github: string;
9
10
  // 键盘搜索的快捷键,参考 https://github.com/madrobby/keymaster
10
11
  searchHotKey?: string | { macos: string; windows: string };
@@ -0,0 +1,32 @@
1
+ /**
2
+ FireFox CSS backdrop-filter polyfill
3
+ https://www.cnblogs.com/coco1s/p/14953143.html
4
+ */
5
+
6
+ .g-glossy-firefox,
7
+ .g-glossy-firefox-cover {
8
+ display: none;
9
+ }
10
+
11
+ @supports (background: -moz-element(#article-body)) {
12
+ .g-glossy-firefox-cover {
13
+ display: block;
14
+ position: fixed;
15
+ top: var(--anchor-offset);
16
+ width: 100%;
17
+ height: 72px;
18
+ z-index: 22;
19
+ background-color: white;
20
+ }
21
+
22
+ .g-glossy-firefox {
23
+ display: block;
24
+ position: fixed;
25
+ width: 100%;
26
+ top: var(--anchor-offset);
27
+ height: 72px;
28
+ z-index: 24;
29
+ background: -moz-element(#article-body) no-repeat top;
30
+ filter: blur(10px);
31
+ }
32
+ }
@@ -1,5 +1,8 @@
1
+ import './firefox-polyfill.css';
1
2
  import './tailwind.out.css';
2
3
  // Components
4
+ export { default as FeatureItem } from './components/FeatureItem';
5
+ export { default as Features } from './components/Features';
3
6
  export { default as Hero } from './components/Hero';
4
7
  export { default as Message } from './components/Message';
5
- export { default as Layout } from './Layout';
8
+ export { default as $Layout } from './Layout';
@@ -28,7 +28,7 @@ article h6 {
28
28
  }
29
29
 
30
30
  article p {
31
- @apply text-base font-light leading-8 mt-4 text-gray-700 dark:text-white;
31
+ @apply text-base leading-8 mt-4 text-gray-700 dark:text-white;
32
32
  }
33
33
 
34
34
  article ul {
@@ -84,7 +84,18 @@ article a code {
84
84
 
85
85
  article a {
86
86
  @apply text-blue-600 mx-1 hover:text-blue-300 transition dark:text-blue-400;
87
- background-image: linear-gradient(transparent 60%, rgba(130, 199, 255, 0.28) 55%);
87
+ background-image: linear-gradient(
88
+ transparent 60%,
89
+ rgba(130, 199, 255, 0.28) 55%
90
+ );
91
+ }
92
+
93
+ .link-with-underline {
94
+ @apply text-blue-600 mx-1 hover:text-blue-300 transition dark:text-blue-400;
95
+ background-image: linear-gradient(
96
+ transparent 60%,
97
+ rgba(130, 199, 255, 0.28) 55%
98
+ );
88
99
  }
89
100
 
90
101
  /*article pre {*/
@@ -106,10 +117,83 @@ article hr {
106
117
  }
107
118
 
108
119
  html {
109
- scroll-behavior: smooth
120
+ scroll-behavior: smooth;
121
+ }
122
+
123
+ :root {
124
+ --anchor-offset: 28px;
110
125
  }
111
126
 
112
127
  /** Anchor with offset for headings */
113
- h1, h2, h3, h4, h5, h6 {
114
- scroll-margin-top: 3em;
128
+ h1,
129
+ h2,
130
+ h3,
131
+ h4,
132
+ h5,
133
+ h6 {
134
+ scroll-margin-top: calc(var(--anchor-offset) + 88px);
135
+ }
136
+
137
+ @layer components {
138
+ .features-dark {
139
+ @apply bg-gray-900;
140
+ background-image: radial-gradient(#2a2a2a 20%, transparent 20%);
141
+ background-size: 6px 6px;
142
+ width: 100%;
143
+ -ms-overflow-style: none;
144
+ scrollbar-width: none;
145
+ }
146
+
147
+ .features {
148
+ background-image: radial-gradient(#f8f8f5 20%, transparent 20%);
149
+ background-color: white;
150
+ background-size: 6px 6px;
151
+ width: 100%;
152
+ -ms-overflow-style: none;
153
+ scrollbar-width: none;
154
+ }
155
+
156
+ .features::-webkit-scrollbar {
157
+ display: none;
158
+ }
159
+
160
+ .mdx-message {
161
+ @apply border-l-8;
162
+ }
163
+
164
+ .mdx-message > p {
165
+ @apply mt-0;
166
+ }
167
+
168
+ .mdx-message-info {
169
+ @apply bg-blue-50 border-blue-300 dark:bg-blue-100 dark:border-blue-500;
170
+ }
171
+
172
+ .mdx-message-info > p {
173
+ @apply text-blue-900 dark:text-blue-900;
174
+ }
175
+
176
+ .mdx-message-success {
177
+ @apply bg-green-50 border-green-300 dark:bg-green-100 dark:border-green-500;
178
+ }
179
+
180
+ .mdx-message-success > p {
181
+ @apply text-green-900 dark:text-green-900;
182
+ }
183
+
184
+ .mdx-message-warning {
185
+ @apply bg-orange-50 border-orange-300 dark:bg-orange-100 dark:border-orange-500;
186
+ }
187
+
188
+ .mdx-message-warning > p {
189
+ @apply text-orange-900 dark:text-orange-900;
190
+ }
191
+
192
+ .mdx-message-error {
193
+ @apply bg-red-50 border-red-300 dark:bg-red-100 dark:border-red-500;
194
+ }
195
+
196
+ .mdx-message-error > p {
197
+ @apply text-red-900 dark:text-red-900;
198
+ }
115
199
  }