Skip to content

Commit

Permalink
✨ feat: add theme
Browse files Browse the repository at this point in the history
  • Loading branch information
eternallycyf committed Sep 5, 2024
1 parent e5ef6fb commit c1c2b5a
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 3 deletions.
6 changes: 5 additions & 1 deletion src/components/Playground/Playground.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { Allotment } from 'allotment';
import 'allotment/dist/style.css';
import { useContext } from 'react';
import CodeEditor from './components/CodeEditor';
import Header from './components/Header';
import Preview from './components/Preview';
import './index.less';
import { PlaygroundContext } from './PlaygroundContext';

export default function ReactPlayground() {
const { theme, setTheme } = useContext(PlaygroundContext);

Check warning on line 11 in src/components/Playground/Playground.tsx

View workflow job for this annotation

GitHub Actions / test

'setTheme' is assigned a value but never used

Check warning on line 11 in src/components/Playground/Playground.tsx

View workflow job for this annotation

GitHub Actions / Release

'setTheme' is assigned a value but never used

return (
<div style={{ height: '100vh' }}>
<div className={theme} style={{ height: '100vh' }}>
<Header />
<Allotment defaultSizes={[100, 100]}>
<Allotment.Pane minSize={0}>
Expand Down
7 changes: 7 additions & 0 deletions src/components/Playground/PlaygroundContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ export interface Files {
[key: string]: File;
}

export type Theme = 'light' | 'dark';

export interface PlaygroundContextProps {
files: Files;
selectedFileName: string;
theme: Theme;
setTheme: (theme: Theme) => void;
setSelectedFileName: (fileName: string) => void;
setFiles: (files: Files) => void;
addFile: (fileName: string) => void;
Expand All @@ -30,6 +34,7 @@ export const PlaygroundProvider = (props: PropsWithChildren) => {
const { children } = props;
const [files, setFiles] = useState<Files>(initFiles);
const [selectedFileName, setSelectedFileName] = useState('App.tsx');
const [theme, setTheme] = useState<Theme>('light');

const addFile = (name: string) => {
files[name] = {
Expand Down Expand Up @@ -64,6 +69,8 @@ export const PlaygroundProvider = (props: PropsWithChildren) => {
return (
<PlaygroundContext.Provider
value={{
theme,
setTheme,
files,
selectedFileName,
setSelectedFileName,
Expand Down
9 changes: 7 additions & 2 deletions src/components/Playground/components/CodeEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import Editor from './Editor';
import FileNameList from './FileNameList';

export default function CodeEditor() {
const { files, setFiles, selectedFileName, setSelectedFileName } = useContext(PlaygroundContext);
const { files, setFiles, selectedFileName, setSelectedFileName, theme } =

Check warning on line 9 in src/components/Playground/components/CodeEditor/index.tsx

View workflow job for this annotation

GitHub Actions / test

'setSelectedFileName' is assigned a value but never used

Check warning on line 9 in src/components/Playground/components/CodeEditor/index.tsx

View workflow job for this annotation

GitHub Actions / Release

'setSelectedFileName' is assigned a value but never used
useContext(PlaygroundContext);

const file = files[selectedFileName];

Expand All @@ -17,7 +18,11 @@ export default function CodeEditor() {
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<FileNameList />
<Editor file={file} onChange={debounce(onEditorChange, 500)} />
<Editor
file={file}
onChange={debounce(onEditorChange, 500)}
options={{ theme: `vs-${theme}` }}
/>
</div>
);
}
31 changes: 31 additions & 0 deletions src/components/Playground/components/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import '../../index.less';

import { DownloadOutlined, MoonOutlined, ShareAltOutlined, SunOutlined } from '@ant-design/icons';
import { message } from 'antd';
import copy from 'copy-to-clipboard';
import { useContext } from 'react';
import { PlaygroundContext } from '../../PlaygroundContext';
import { downloadFiles } from '../../utils';

export default function Header() {
const { files, theme, setTheme } = useContext(PlaygroundContext);

return (
<div className="header">
<div className="logo">
Expand All @@ -11,6 +20,28 @@ export default function Header() {
/>
<span>React Playground</span>
</div>
<div className="links">
{theme === 'light' && (
<MoonOutlined title="切换暗色主题" className="theme" onClick={() => setTheme('dark')} />
)}
{theme === 'dark' && (
<SunOutlined title="切换亮色主题" className="theme" onClick={() => setTheme('light')} />
)}
<ShareAltOutlined
style={{ marginLeft: '10px' }}
onClick={() => {
copy(window.location.href);
message.success('分享链接已复制。');
}}
/>
<DownloadOutlined
style={{ marginLeft: '10px' }}
onClick={async () => {
await downloadFiles(files);
message.success('下载完成');
}}
/>
</div>
</div>
);
}
58 changes: 58 additions & 0 deletions src/components/Playground/components/Message/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
.msg {
position: absolute;
right: 8px;
bottom: 0;
left: 8px;
z-index: 10;

display: flex;
max-height: calc(100% - 300px);
min-height: 40px;
margin-bottom: 8px;
color: var(--color);

background-color: var(--bg-color);
border: 2px solid #fff;
border-radius: 6px;

align-items: stretch;
border-color: var(--color);

&.error {
--color: #f56c6c;
--bg-color: #fef0f0;
}

&.warn {
--color: #e6a23c;
--bg-color: #fdf6ec;
}
}

pre {
padding: 12px 20px;
margin: 0;
overflow: auto;
white-space: break-spaces;
}

.dismiss {
position: absolute;
top: 2px;
right: 2px;

display: block;
width: 18px;
height: 18px;
padding: 0;

font-size: 9px;
line-height: 18px;
color: var(--bg-color);

text-align: center;
cursor: pointer;
background-color: var(--color);
border: none;
border-radius: 9px;
}
27 changes: 27 additions & 0 deletions src/components/Playground/components/Message/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import classnames from 'classnames';

Check warning on line 1 in src/components/Playground/components/Message/index.tsx

View workflow job for this annotation

GitHub Actions / test

'classnames' is defined but never used

Check warning on line 1 in src/components/Playground/components/Message/index.tsx

View workflow job for this annotation

GitHub Actions / Release

'classnames' is defined but never used
import React, { useEffect, useState } from 'react';

import './index.css';

export interface MessageProps {
type: 'error' | 'warn';
content: string;
}

export const Message: React.FC<MessageProps> = (props) => {
const { type, content } = props;
const [visible, setVisible] = useState(false);

useEffect(() => {
setVisible(!!content);
}, [content]);

return visible ? (
<div className={['msg', type].join(' ')}>
<pre dangerouslySetInnerHTML={{ __html: content }}></pre>
<button type="button" className="dismiss" onClick={() => setVisible(false)}>
</button>
</div>
) : null;
};
26 changes: 26 additions & 0 deletions src/components/Playground/components/Preview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@ import { useContext, useEffect, useState } from 'react';
import { IMPORT_MAP_FILE_NAME } from '../../files';
import { PlaygroundContext } from '../../PlaygroundContext';
import Editor from '../CodeEditor/Editor';

Check warning on line 4 in src/components/Playground/components/Preview/index.tsx

View workflow job for this annotation

GitHub Actions / test

'Editor' is defined but never used

Check warning on line 4 in src/components/Playground/components/Preview/index.tsx

View workflow job for this annotation

GitHub Actions / Release

'Editor' is defined but never used
import { Message } from '../Message';
import { compile } from './compiler';
import iframeRaw from './iframe.html.ejs?raw';

interface MessageData {
data: {
type: string;
message: string;
};
}

export default function Preview() {
const { files } = useContext(PlaygroundContext);
const [compiledCode, setCompiledCode] = useState('');
Expand All @@ -22,6 +30,22 @@ export default function Preview() {
};
const [iframeUrl, setIframeUrl] = useState(getIframeUrl());

const [error, setError] = useState('');

const handleMessage = (msg: MessageData) => {
const { type, message } = msg.data;
if (type === 'ERROR') {
setError(message);
}
};

useEffect(() => {
window.addEventListener('message', handleMessage);
return () => {
window.removeEventListener('message', handleMessage);
};
}, []);

useEffect(() => {
const res = compile(files);
setCompiledCode(res);
Expand All @@ -42,6 +66,8 @@ export default function Preview() {
border: 'none',
}}
/>

<Message type="error" content={error} />
{/* <Editor file={{
name: 'dist.js',
value: compiledCode,
Expand Down
20 changes: 20 additions & 0 deletions src/components/Playground/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
display: flex;
align-items: center;
justify-content: space-between;
background: var(--bg);
color: var(--text);

.logo {
display: flex;
Expand All @@ -23,4 +25,22 @@
margin-right: 10px;
}
}
}

.light {
--text: #444;
--bg: #fff;
--border: #ddd;
--box-shadow: #00000054;
--primary: #00d8fe;
--dialog: #fff;
}

.dark {
--text: #fff;
--bg: #1a1a1a;
--border: #383838;
--box-shadow: #0000;
--primary: #00d8fe;
--dialog: #2a2a2a;
}

0 comments on commit c1c2b5a

Please sign in to comment.