mirror of
https://github.com/jaywcjlove/wxmp.git
synced 2026-01-12 00:08:50 +08:00
feat: add url parameter to load markdown content.
This commit is contained in:
@@ -15,7 +15,7 @@
|
|||||||
- [x] 支持主题选择 & 编辑预览。
|
- [x] 支持主题选择 & 编辑预览。
|
||||||
- [x] 支持明暗两种主题预览。
|
- [x] 支持明暗两种主题预览。
|
||||||
- [ ] 支持色盘取色,快速替换文章整体色调
|
- [ ] 支持色盘取色,快速替换文章整体色调
|
||||||
- [ ] 支持 URL 参数加载 Markdown 内容。
|
- [x] 支持 URL 参数加载 Markdown 内容。
|
||||||
- [x] 支持 URL 参数选择预览主题。
|
- [x] 支持 URL 参数选择预览主题。
|
||||||
|
|
||||||
### 支持代码块样式
|
### 支持代码块样式
|
||||||
@@ -116,9 +116,9 @@ https://<URL>?md=<Markdown 资源 URL>
|
|||||||
加载 Markdown 内容的示例 URL:
|
加载 Markdown 内容的示例 URL:
|
||||||
|
|
||||||
```
|
```
|
||||||
https://jaywcjlove.github.io/wxmp/#/?theme=underscore&md=https%3A%2F%2Fraw.githubusercontent.com%2Fuiwjs%2Freact-markdown-editor%2Fmaster%2FREADME.md
|
https://jaywcjlove.github.io/wxmp/#/?theme=underscore&md=https://raw.githubusercontent.com/jaywcjlove/c-tutorial/master/README.md
|
||||||
|
|
||||||
Markdown URL 地址: https://raw.githubusercontent.com/uiwjs/react-markdown-editor/master/README.md
|
Markdown URL 地址: https://raw.githubusercontent.com/jaywcjlove/c-tutorial/master/README.md
|
||||||
```
|
```
|
||||||
|
|
||||||
## 主题定制
|
## 主题定制
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.18.9",
|
"@babel/runtime": "^7.18.9",
|
||||||
|
"@tanstack/react-query": "^4.2.3",
|
||||||
"@uiw/codemirror-theme-abcdef": "^4.11.6",
|
"@uiw/codemirror-theme-abcdef": "^4.11.6",
|
||||||
"@uiw/codemirror-theme-androidstudio": "^4.11.6",
|
"@uiw/codemirror-theme-androidstudio": "^4.11.6",
|
||||||
"@uiw/codemirror-theme-atomone": "^4.11.6",
|
"@uiw/codemirror-theme-atomone": "^4.11.6",
|
||||||
@@ -55,10 +56,10 @@
|
|||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
"@types/styled-components": "^5.1.25",
|
"@types/styled-components": "^5.1.25",
|
||||||
"husky": "^8.0.1",
|
"husky": "^8.0.1",
|
||||||
"prettier": "^2.7.1",
|
|
||||||
"pretty-quick": "~3.1.3",
|
|
||||||
"kkt": "^7.2.0",
|
"kkt": "^7.2.0",
|
||||||
"markdown-react-code-preview-loader": "^2.1.2"
|
"markdown-react-code-preview-loader": "^2.1.2",
|
||||||
|
"prettier": "^2.7.1",
|
||||||
|
"pretty-quick": "~3.1.3"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import { HashRouter } from 'react-router-dom';
|
|||||||
import BackToUp from '@uiw/react-back-to-top';
|
import BackToUp from '@uiw/react-back-to-top';
|
||||||
import { Toaster } from 'react-hot-toast';
|
import { Toaster } from 'react-hot-toast';
|
||||||
import { createGlobalStyle } from 'styled-components';
|
import { createGlobalStyle } from 'styled-components';
|
||||||
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import { Provider } from './store/context';
|
import { Provider } from './store/Provider';
|
||||||
|
|
||||||
export const GlobalStyle = createGlobalStyle`
|
export const GlobalStyle = createGlobalStyle`
|
||||||
[data-color-mode*='dark'], [data-color-mode*='dark'] body {
|
[data-color-mode*='dark'], [data-color-mode*='dark'] body {
|
||||||
@@ -52,6 +53,7 @@ export const GlobalStyle = createGlobalStyle`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const queryClient = new QueryClient();
|
||||||
const style: React.CSSProperties = { zIndex: 999 };
|
const style: React.CSSProperties = { zIndex: 999 };
|
||||||
|
|
||||||
const container = document.getElementById('root');
|
const container = document.getElementById('root');
|
||||||
@@ -61,8 +63,10 @@ root.render(
|
|||||||
<Toaster />
|
<Toaster />
|
||||||
<BackToUp style={style}>Top</BackToUp>
|
<BackToUp style={style}>Top</BackToUp>
|
||||||
<GlobalStyle />
|
<GlobalStyle />
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
<Provider>
|
<Provider>
|
||||||
<App />
|
<App />
|
||||||
</Provider>
|
</Provider>
|
||||||
|
</QueryClientProvider>
|
||||||
</HashRouter>,
|
</HashRouter>,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,16 +14,18 @@ export const Warpper = styled.div`
|
|||||||
|
|
||||||
export const HomePage = () => {
|
export const HomePage = () => {
|
||||||
const commands = [...getCommands(), themeCommand];
|
const commands = [...getCommands(), themeCommand];
|
||||||
const { theme, markdown, setMarkdown } = useContext(Context);
|
const { theme, markdown, isLoading, setMarkdown } = useContext(Context);
|
||||||
const themeValue = themes[theme].value;
|
const themeValue = themes[theme].value;
|
||||||
const handleChange = (value: string) => setMarkdown(value);
|
const handleChange = (value: string) => setMarkdown(value);
|
||||||
|
console.log(isLoading);
|
||||||
return (
|
return (
|
||||||
<Warpper>
|
<Warpper>
|
||||||
<MarkdownEditor
|
<MarkdownEditor
|
||||||
value={markdown}
|
value={markdown}
|
||||||
toolbars={commands}
|
toolbars={commands}
|
||||||
theme={themeValue}
|
theme={themeValue}
|
||||||
toolbarsMode={[cssCommand, previeTheme, copy, 'preview', 'fullscreen']}
|
readOnly={isLoading}
|
||||||
|
toolbarsMode={[cssCommand, previeTheme, copy, 'fullscreen', 'preview']}
|
||||||
extensions={[EditorView.lineWrapping]}
|
extensions={[EditorView.lineWrapping]}
|
||||||
renderPreview={Preview}
|
renderPreview={Preview}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import { Warpper } from '../home';
|
|||||||
|
|
||||||
export const EditorPage = () => {
|
export const EditorPage = () => {
|
||||||
const commands = [themeTitle, themeCommand, previousCommand];
|
const commands = [themeTitle, themeCommand, previousCommand];
|
||||||
const toolbarsMode: IMarkdownEditor['toolbarsMode'] = [previeTheme, copy, 'preview', 'fullscreen'];
|
const toolbarsMode: IMarkdownEditor['toolbarsMode'] = [previeTheme, copy, 'fullscreen', 'preview'];
|
||||||
const { theme, css, setCss } = useContext(Context);
|
const { theme, css, setCss, isLoading } = useContext(Context);
|
||||||
const value = themes[theme].value;
|
const value = themes[theme].value;
|
||||||
const handleChange = (value: string) => setCss(value);
|
const handleChange = (value: string) => setCss(value);
|
||||||
return (
|
return (
|
||||||
@@ -21,6 +21,7 @@ export const EditorPage = () => {
|
|||||||
<MarkdownEditor
|
<MarkdownEditor
|
||||||
value={css}
|
value={css}
|
||||||
theme={value}
|
theme={value}
|
||||||
|
readOnly={isLoading}
|
||||||
toolbars={commands}
|
toolbars={commands}
|
||||||
toolbarsMode={toolbarsMode}
|
toolbarsMode={toolbarsMode}
|
||||||
reExtensions={[EditorView.lineWrapping, cssLang()]}
|
reExtensions={[EditorView.lineWrapping, cssLang()]}
|
||||||
|
|||||||
49
src/store/Provider.tsx
Normal file
49
src/store/Provider.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
import { PreviewThemeValue, previewThemes, ThemeValue, Context } from './context';
|
||||||
|
import { useMdSource } from './getMdSource';
|
||||||
|
|
||||||
|
import data from '../../README.md';
|
||||||
|
|
||||||
|
export const Provider: React.FC<React.PropsWithChildren> = ({ children }) => {
|
||||||
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
const paramPreviewTheme = searchParams.get('theme') as PreviewThemeValue;
|
||||||
|
const initPreviewTheme = paramPreviewTheme || 'underscore';
|
||||||
|
const mdurl = searchParams.get('md');
|
||||||
|
const [markdown, setMarkdown] = React.useState<string>(mdurl ? '' : data.source);
|
||||||
|
const [css, setCss] = React.useState<string>(previewThemes[initPreviewTheme].value);
|
||||||
|
const [previewTheme, setPreviewTheme] = React.useState<PreviewThemeValue>(initPreviewTheme);
|
||||||
|
const [theme, setTheme] = React.useState<ThemeValue>('default');
|
||||||
|
const [isLoading, setIsLoading] = React.useState<boolean>(true);
|
||||||
|
const { data: mddata, isLoading: loading } = useMdSource(mdurl);
|
||||||
|
useEffect(() => {
|
||||||
|
if (paramPreviewTheme !== previewTheme) {
|
||||||
|
searchParams.set('theme', previewTheme);
|
||||||
|
setSearchParams(searchParams);
|
||||||
|
}
|
||||||
|
}, [paramPreviewTheme, previewTheme, searchParams, setSearchParams]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (mdurl) {
|
||||||
|
setMarkdown(mddata || '');
|
||||||
|
}
|
||||||
|
}, [mddata, mdurl]);
|
||||||
|
useEffect(() => setIsLoading(loading), [loading]);
|
||||||
|
return (
|
||||||
|
<Context.Provider
|
||||||
|
value={{
|
||||||
|
isLoading,
|
||||||
|
setIsLoading,
|
||||||
|
markdown,
|
||||||
|
setMarkdown,
|
||||||
|
css,
|
||||||
|
setCss,
|
||||||
|
previewTheme,
|
||||||
|
setPreviewTheme,
|
||||||
|
theme,
|
||||||
|
setTheme,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Context.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
|
||||||
import { defaultTheme } from '@uiw/react-markdown-editor';
|
import { defaultTheme } from '@uiw/react-markdown-editor';
|
||||||
import { abcdef } from '@uiw/codemirror-theme-abcdef';
|
import { abcdef } from '@uiw/codemirror-theme-abcdef';
|
||||||
import { androidstudio } from '@uiw/codemirror-theme-androidstudio';
|
import { androidstudio } from '@uiw/codemirror-theme-androidstudio';
|
||||||
@@ -115,6 +114,8 @@ export type ThemeValue = keyof typeof themes;
|
|||||||
export type PreviewThemeValue = keyof typeof previewThemes;
|
export type PreviewThemeValue = keyof typeof previewThemes;
|
||||||
|
|
||||||
export interface CreateContext {
|
export interface CreateContext {
|
||||||
|
isLoading: boolean;
|
||||||
|
setIsLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
markdown: string;
|
markdown: string;
|
||||||
setMarkdown: React.Dispatch<React.SetStateAction<string>>;
|
setMarkdown: React.Dispatch<React.SetStateAction<string>>;
|
||||||
css: string;
|
css: string;
|
||||||
@@ -126,6 +127,8 @@ export interface CreateContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Context = React.createContext<CreateContext>({
|
export const Context = React.createContext<CreateContext>({
|
||||||
|
isLoading: true,
|
||||||
|
setIsLoading: () => {},
|
||||||
markdown: data.source,
|
markdown: data.source,
|
||||||
setMarkdown: () => {},
|
setMarkdown: () => {},
|
||||||
css: previewThemes['underscore'].value,
|
css: previewThemes['underscore'].value,
|
||||||
@@ -135,35 +138,3 @@ export const Context = React.createContext<CreateContext>({
|
|||||||
theme: 'default',
|
theme: 'default',
|
||||||
setTheme: () => {},
|
setTheme: () => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Provider: React.FC<React.PropsWithChildren> = ({ children }) => {
|
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
|
||||||
const paramPreviewTheme = searchParams.get('theme') as PreviewThemeValue;
|
|
||||||
const initPreviewTheme = paramPreviewTheme || 'underscore';
|
|
||||||
const [markdown, setMarkdown] = React.useState<string>(data.source);
|
|
||||||
const [css, setCss] = React.useState<string>(previewThemes[initPreviewTheme].value);
|
|
||||||
const [previewTheme, setPreviewTheme] = React.useState<PreviewThemeValue>(initPreviewTheme);
|
|
||||||
const [theme, setTheme] = React.useState<ThemeValue>('default');
|
|
||||||
useEffect(() => {
|
|
||||||
if (paramPreviewTheme !== previewTheme) {
|
|
||||||
searchParams.set('theme', previewTheme);
|
|
||||||
setSearchParams(searchParams);
|
|
||||||
}
|
|
||||||
}, [paramPreviewTheme, previewTheme, searchParams, setSearchParams]);
|
|
||||||
return (
|
|
||||||
<Context.Provider
|
|
||||||
value={{
|
|
||||||
markdown,
|
|
||||||
setMarkdown,
|
|
||||||
css,
|
|
||||||
setCss,
|
|
||||||
previewTheme,
|
|
||||||
setPreviewTheme,
|
|
||||||
theme,
|
|
||||||
setTheme,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Context.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
12
src/store/getMdSource.ts
Normal file
12
src/store/getMdSource.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
export const useMdSource = (url: string | null) => {
|
||||||
|
return useQuery(['database-list', url], () => {
|
||||||
|
if (!url) return Promise.resolve('');
|
||||||
|
return fetch(url)
|
||||||
|
.then((response) => response.text())
|
||||||
|
.then((data) => {
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -103,7 +103,7 @@ th {
|
|||||||
.code-spans {
|
.code-spans {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
white-space: pre;
|
white-space: initial;
|
||||||
background: rgba(27, 31, 35, 0.05);
|
background: rgba(27, 31, 35, 0.05);
|
||||||
padding: 0.2em 0.6em;
|
padding: 0.2em 0.6em;
|
||||||
border-radius: 0.6em;
|
border-radius: 0.6em;
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ th {
|
|||||||
.code-spans {
|
.code-spans {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
white-space: pre;
|
white-space: initial;
|
||||||
color: #009874;
|
color: #009874;
|
||||||
background: rgba(27, 31, 35, 0.05);
|
background: rgba(27, 31, 35, 0.05);
|
||||||
padding: 0.2em 0.6em;
|
padding: 0.2em 0.6em;
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ th {
|
|||||||
.code-spans {
|
.code-spans {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
white-space: pre;
|
white-space: initial;
|
||||||
color: #0f4c81;
|
color: #0f4c81;
|
||||||
background: rgba(27, 31, 35, 0.05);
|
background: rgba(27, 31, 35, 0.05);
|
||||||
padding: 0.2em 0.6em;
|
padding: 0.2em 0.6em;
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ th {
|
|||||||
.code-spans {
|
.code-spans {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
white-space: pre;
|
white-space: initial;
|
||||||
color: #ffb11b;
|
color: #ffb11b;
|
||||||
background: rgba(27, 31, 35, 0.05);
|
background: rgba(27, 31, 35, 0.05);
|
||||||
padding: 0.2em 0.6em;
|
padding: 0.2em 0.6em;
|
||||||
|
|||||||
Reference in New Issue
Block a user