feat: add theme editor.

This commit is contained in:
jaywcjlove
2022-09-03 12:19:13 +08:00
parent 0580011f7b
commit 1c7167bcf9
16 changed files with 190 additions and 31 deletions

View File

@@ -27,7 +27,7 @@
"@uiw/codemirror-theme-xcode": "^4.11.6",
"@uiw/react-back-to-top": "^1.2.0",
"@uiw/react-github-corners": "^1.5.15",
"@uiw/react-markdown-editor": "^5.5.1",
"@uiw/react-markdown-editor": "^5.6.0",
"@wcj/dark-mode": "^1.0.15",
"css-tree": "^2.2.1",
"react": "^18.2.0",

View File

@@ -1,12 +1,14 @@
import { Routes, Route } from 'react-router-dom';
import { Layout } from './components/Layout';
import { HomePage } from './pages/home';
import { EditorPage } from './pages/theme/editor';
export default function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<HomePage />} />
<Route path="/editor/theme" element={<EditorPage />} />
</Route>
</Routes>
);

7
src/assets/color.svg Normal file
View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g id="Layer_2">
<g id="color-palette">
<path d="M19.54 5.08A10.61 10.61 0 0 0 11.91 2a10 10 0 0 0-.05 20 2.58 2.58 0 0 0 2.53-1.89 2.52 2.52 0 0 0-.57-2.28.5.5 0 0 1 .37-.83h1.65A6.15 6.15 0 0 0 22 11.33a8.48 8.48 0 0 0-2.46-6.25Zm-12.7 9.66a1.5 1.5 0 1 1 .4-2.08 1.49 1.49 0 0 1-.4 2.08ZM8.3 9.25a1.5 1.5 0 1 1-.55-2 1.5 1.5 0 0 1 .55 2ZM11 7a1.5 1.5 0 1 1 1.5-1.5A1.5 1.5 0 0 1 11 7Zm5.75.8a1.5 1.5 0 1 1 .55-2 1.5 1.5 0 0 1-.55 2Z" style="fill:#231f20" id="color-palette-2"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 576 B

27
src/commands/css.tsx Normal file
View File

@@ -0,0 +1,27 @@
import { NavLink } from 'react-router-dom';
import { ICommand } from '@uiw/react-markdown-editor';
import styled from 'styled-components';
const Link = styled(NavLink)`
font-size: 0.8rem;
line-height: 0.8rem;
text-decoration: none;
padding: 0.18rem 0.3rem;
&:hover {
color: var(--color-accent-fg);
background-color: var(--color-neutral-muted);
border-radius: 0.2rem;
}
`;
export const cssCommand: ICommand = {
name: 'previewTtheme',
keyCommand: 'previewTtheme',
button: () => <Link to="/editor/theme"></Link>,
};
export const previousCommand: ICommand = {
name: 'previous',
keyCommand: 'previous',
button: () => <Link to="/"></Link>,
};

View File

@@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import { ICommand, IMarkdownEditor, ToolBarProps } from '@uiw/react-markdown-editor';
import styled from 'styled-components';
import { Context, previewThemes, PreviewThemeValue, themes as editorThemes, ThemeValue } from '../../store/context';
import { Context, previewThemes, PreviewThemeValue, themes as editorThemes, ThemeValue } from '../store/context';
const Select = styled.select`
max-width: 4rem;
@@ -46,10 +46,15 @@ export const theme: ICommand = {
};
const ThemePreviewView: React.FC<{}> = () => {
const { css, setCss } = useContext(Context);
const handleChange = (ev: React.ChangeEvent<HTMLSelectElement>) => setCss(ev.target.value as any);
const { setCss, previewTheme, setPreviewTheme } = useContext(Context);
const handleChange = (ev: React.ChangeEvent<HTMLSelectElement>) => {
const value = ev.target.value as PreviewThemeValue;
console.log('vvvv');
setPreviewTheme(value);
setCss(previewThemes[value].value);
};
return (
<Select value={css} onChange={handleChange}>
<Select value={previewTheme} onChange={handleChange}>
{(Object.keys(previewThemes) as Array<PreviewThemeValue>).map((name, key) => {
return (
<option value={name} key={key}>

25
src/commands/title.tsx Normal file
View File

@@ -0,0 +1,25 @@
import React from 'react';
import { ICommand } from '@uiw/react-markdown-editor';
import styled from 'styled-components';
import { ReactComponent as ColorIcon } from '../assets/color.svg';
const Title = styled.div`
font-size: 0.9rem;
font-weight: bold;
display: flex;
align-items: center;
line-height: 1;
padding-right: 0.5rem;
padding-left: 0.2rem;
`;
export const themeTitle: ICommand = {
name: 'themeTitle',
keyCommand: 'themeTitle',
button: () => (
<Title>
<ColorIcon width={16} height={16} />
</Title>
),
};

View File

@@ -1,5 +1,5 @@
import styled from 'styled-components';
import { Outlet } from 'react-router-dom';
import { Outlet, NavLink } from 'react-router-dom';
import '@wcj/dark-mode';
import { ReactComponent as LogoIcon } from '../assets/logo.svg';
import { ReactComponent as GithubIcon } from '../assets/github.svg';
@@ -11,7 +11,7 @@ const Header = styled.header`
flex-direction: row;
justify-content: space-between;
border-bottom: 1px solid var(--color-border-muted);
padding: 0.5rem 1rem 0.5rem 1rem;
padding: 0.5rem 0.6rem 0.5rem 1rem;
`;
const Article = styled.article`
@@ -46,7 +46,7 @@ const Title = styled.h1`
const Section = styled.section`
display: flex;
align-items: center;
gap: 0.8rem;
gap: 0.5rem;
dark-mode {
font-size: 1.05rem;
display: block;
@@ -55,6 +55,22 @@ const Section = styled.section`
a svg {
display: block;
}
a {
text-decoration: none;
color: var(--color-theme-text);
padding: 0.1rem 0.3rem;
box-shadow: inset 0 0 0 var(--color-accent-fg);
transition: all 0.3s;
font-size: 0.9rem;
&.active {
box-shadow: inset 0 -0.3rem 0 var(--color-accent-fg);
}
&:hover:not(.active):not(:last-child) {
box-shadow: inset 0 -1.5rem 0 var(--color-accent-fg);
color: #fff;
border-radius: 0.2rem;
}
}
`;
export function Layout() {
@@ -69,6 +85,8 @@ export function Layout() {
</Title>
</Article>
<Section>
<NavLink to="/"></NavLink>
<NavLink to="/editor/theme"></NavLink>
<dark-mode permanent dark="Dark" light="Light" />
<a href="https://github.com/jaywcjlove/wxmp" target="__blank">
<GithubIcon width={23} height={23} />

View File

@@ -1,7 +1,7 @@
import { MarkdownPreviewProps } from '@uiw/react-markdown-preview';
import styled from 'styled-components';
import { useContext } from 'react';
import { Context, previewThemes } from '../../store/context';
import { Context } from '../../store/context';
import { markdownToHTML } from '../../utils/markdownToHTML';
@@ -14,6 +14,6 @@ const Warpper = styled.div`
export const Preview = (props: MarkdownPreviewProps) => {
const { css } = useContext(Context);
const html = markdownToHTML(props.source || '', previewThemes[css].value);
const html = markdownToHTML(props.source || '', css);
return <Warpper dangerouslySetInnerHTML={{ __html: html }} />;
};

View File

@@ -3,10 +3,10 @@ import { useContext } from 'react';
import { EditorView } from '@codemirror/view';
import styled from 'styled-components';
import { Preview } from './Preview';
import { copy } from './copy';
import { theme as themeCommand, previeTheme } from './theme';
import { copy } from '../../commands/copy';
import { theme as themeCommand, previeTheme } from '../../commands/theme';
import { cssCommand } from '../../commands/css';
import { Context, themes } from '../../store/context';
import data from '../../../README.md';
const Warpper = styled.div`
height: calc(100vh - 2.9rem);
@@ -14,17 +14,19 @@ const Warpper = styled.div`
export const HomePage = () => {
const commands = [...getCommands(), themeCommand];
const { theme } = useContext(Context);
const value = themes[theme].value;
const { theme, markdown, setMarkdown } = useContext(Context);
const themeValue = themes[theme].value;
const handleChange = (value: string) => setMarkdown(value);
return (
<Warpper>
<MarkdownEditor
value={data.source}
value={markdown}
toolbars={commands}
theme={value}
toolbarsMode={[previeTheme, copy, 'preview', 'fullscreen']}
theme={themeValue}
toolbarsMode={[cssCommand, previeTheme, copy, 'preview', 'fullscreen']}
extensions={[EditorView.lineWrapping]}
renderPreview={Preview}
onChange={handleChange}
visible={true}
height="calc(100vh - 4.92rem)"
/>

View File

@@ -0,0 +1,19 @@
import { MarkdownPreviewProps } from '@uiw/react-markdown-preview';
import styled from 'styled-components';
import { useContext } from 'react';
import { Context } from '../../store/context';
import { markdownToHTML } from '../../utils/markdownToHTML';
const Warpper = styled.div`
width: 375px;
padding: 20px;
box-shadow: 0 0 60px rgb(0 0 0 / 10%);
min-height: 100%;
`;
export const Preview = (props: MarkdownPreviewProps) => {
const { css, markdown } = useContext(Context);
const html = markdownToHTML(markdown, css);
return <Warpper dangerouslySetInnerHTML={{ __html: html }} />;
};

View File

@@ -0,0 +1,38 @@
import MarkdownEditor, { IMarkdownEditor } from '@uiw/react-markdown-editor';
import { useContext } from 'react';
import { EditorView } from '@codemirror/view';
import styled from 'styled-components';
import { css as cssLang } from '@codemirror/lang-css';
import { Preview } from './Preview';
import { copy } from '../../commands/copy';
import { previousCommand } from '../../commands/css';
import { themeTitle } from '../../commands/title';
import { theme as themeCommand, previeTheme } from '../../commands/theme';
import { Context, themes } from '../../store/context';
const Warpper = styled.div`
height: calc(100vh - 2.9rem);
`;
export const EditorPage = () => {
const commands = [themeTitle, themeCommand, previousCommand];
const toolbarsMode: IMarkdownEditor['toolbarsMode'] = [previeTheme, copy, 'preview', 'fullscreen'];
const { theme, css, setCss } = useContext(Context);
const value = themes[theme].value;
const handleChange = (value: string) => setCss(value);
return (
<Warpper>
<MarkdownEditor
value={css}
theme={value}
toolbars={commands}
toolbarsMode={toolbarsMode}
reExtensions={[EditorView.lineWrapping, cssLang()]}
renderPreview={Preview}
onChange={handleChange}
visible={true}
height="calc(100vh - 4.92rem)"
/>
</Warpper>
);
};

View File

@@ -17,6 +17,8 @@ import defStyle from '../themes/default.md.css';
import simpleStyle from '../themes/simple.md.css';
import underscoreStyle from '../themes/underscore.md.css';
import data from '../../README.md';
export const themes = {
default: {
label: '默认主题',
@@ -107,28 +109,42 @@ export type ThemeValue = keyof typeof themes;
export type PreviewThemeValue = keyof typeof previewThemes;
export interface CreateContext {
css: PreviewThemeValue;
setCss: React.Dispatch<React.SetStateAction<PreviewThemeValue>>;
markdown: string;
setMarkdown: React.Dispatch<React.SetStateAction<string>>;
css: string;
setCss: React.Dispatch<React.SetStateAction<string>>;
previewTheme: PreviewThemeValue;
setPreviewTheme: React.Dispatch<React.SetStateAction<PreviewThemeValue>>;
theme: ThemeValue;
setTheme: React.Dispatch<React.SetStateAction<ThemeValue>>;
}
export const Context = React.createContext<CreateContext>({
css: 'default',
markdown: data.source,
setMarkdown: () => {},
css: previewThemes['underscore'].value,
setCss: () => {},
previewTheme: 'underscore',
setPreviewTheme: () => {},
theme: 'default',
setTheme: () => {},
});
export const Provider: React.FC<React.PropsWithChildren> = ({ children }) => {
const [css, setCss] = React.useState<PreviewThemeValue>('underscore');
const [markdown, setMarkdown] = React.useState<string>(data.source);
const [css, setCss] = React.useState<string>(previewThemes['underscore'].value);
const [previewTheme, setPreviewTheme] = React.useState<PreviewThemeValue>('underscore');
const [theme, setTheme] = React.useState<ThemeValue>('default');
return (
<Context.Provider
value={{
markdown,
setMarkdown,
css,
setCss,
previewTheme,
setPreviewTheme,
theme,
setTheme,
}}

View File

@@ -5,6 +5,7 @@ a {
}
h1 {
display: table;
text-align: center;
color: #3f3f3f;
line-height: 1.75;
@@ -12,7 +13,6 @@ h1 {
'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;
font-size: 1.2em;
font-weight: bold;
display: table;
margin: 2em auto 1em;
padding: 0 1em;
border-bottom: 2px solid #009874;
@@ -20,6 +20,7 @@ h1 {
}
h2 {
display: table;
text-align: center;
color: #fff;
line-height: 1.75;
@@ -27,7 +28,6 @@ h2 {
'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;
font-size: 1.2em;
font-weight: bold;
display: table;
margin: 4em auto 2em;
padding: 0 0.3em;
border-radius: 0.3rem;
@@ -156,11 +156,11 @@ th {
}
.footnotes-title {
display: table;
font-family: -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB',
'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;
font-size: 1rem;
font-weight: bold;
display: table;
margin: 3rem 0 0.6rem 0;
padding-left: 0.2rem;
}

View File

@@ -5,6 +5,7 @@ a {
}
h1 {
display: table;
text-align: center;
color: #3f3f3f;
line-height: 1.75;
@@ -12,7 +13,6 @@ h1 {
'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;
font-size: 1.2em;
font-weight: bold;
display: table;
margin: 2em auto 1em;
padding: 0 1em;
border-bottom: 2px solid #0f4c81;
@@ -20,6 +20,7 @@ h1 {
}
h2 {
display: table;
text-align: center;
color: #fff;
line-height: 1.75;
@@ -27,7 +28,6 @@ h2 {
'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;
font-size: 1.2em;
font-weight: bold;
display: table;
margin: 4em auto 2em;
padding: 0 0.3em;
border-radius: 0.3rem;
@@ -156,11 +156,11 @@ th {
}
.footnotes-title {
display: table;
font-family: -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB',
'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;
font-size: 1rem;
font-weight: bold;
display: table;
margin: 3rem 0 0.6rem 0;
padding-left: 0.2rem;
}

View File

@@ -5,6 +5,7 @@ a {
}
h1 {
display: table;
text-align: center;
color: #3f3f3f;
line-height: 1.15;
@@ -12,7 +13,6 @@ h1 {
'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;
font-size: 1.3em;
font-weight: bold;
display: table;
margin: 2em auto 1em;
padding: 0 1em 0.3em 1em;
margin-top: 0;
@@ -20,12 +20,12 @@ h1 {
}
h2 {
display: table;
line-height: 1.35;
font-family: -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB',
'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;
font-size: 1.2em;
font-weight: bold;
display: table;
padding: 0 0.3em;
margin: 2em 0 1em 0;
box-shadow: inset 0 -0.7rem 0 0 #ffb11b;
@@ -153,11 +153,11 @@ th {
}
.footnotes-title {
display: table;
font-family: -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB',
'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;
font-size: 1rem;
font-weight: bold;
display: table;
margin: 3rem 0 0.6rem 0;
padding-left: 0.2rem;
}