add themes

This commit is contained in:
Luck
2021-03-27 11:49:46 +00:00
parent ca6d42343b
commit c221e8756e
9 changed files with 488 additions and 123 deletions

View File

@@ -1,10 +1,30 @@
import { useState, useEffect } from 'react';
import { ThemeProvider } from 'styled-components';
import ls from 'local-storage';
import EditorControls from './EditorControls';
import EditorTextArea from './EditorTextArea';
import themes from '../style/themes';
export default function Editor({ content, setContent, contentType }) {
const [language, setLanguage] = useState('plain');
const [theme, setTheme] = useState(() => {
const preference = ls.get('theme');
if (preference && themes[preference]) {
return preference;
} else {
return 'dark';
}
});
useEffect(() => {
if (theme === 'dark') {
ls.remove('theme');
} else {
ls.set('theme', theme);
}
}, [theme])
useEffect(() => {
if (contentType) {
setLanguage(contentType);
@@ -12,7 +32,9 @@ export default function Editor({ content, setContent, contentType }) {
}, [contentType]);
return <>
<EditorControls code={content} language={language} setLanguage={setLanguage} />
<EditorTextArea code={content} setCode={setContent} language={language} />
<ThemeProvider theme={themes[theme]}>
<EditorControls code={content} language={language} setLanguage={setLanguage} theme={theme} setTheme={setTheme} />
<EditorTextArea code={content} setCode={setContent} language={language} />
</ThemeProvider>
</>
}

View File

@@ -1,11 +1,13 @@
import { useState, useEffect } from 'react';
import { languageIds } from '../highlighting';
import styled from 'styled-components';
import { gzip } from 'pako';
import history from 'history/browser';
import copy from 'copy-to-clipboard';
export default function EditorControls({ code, language, setLanguage }) {
const [langMenuOpen, setLangMenuOpen] = useState(false);
import { languageIds } from '../highlighting';
import themes from '../style/themes';
export default function EditorControls({ code, language, setLanguage, theme, setTheme }) {
const [saving, setSaving] = useState(false);
const [recentlySaved, setRecentlySaved] = useState(false);
@@ -30,39 +32,94 @@ export default function EditorControls({ code, language, setLanguage }) {
});
}
function toggleLangMenu() {
setLangMenuOpen(!langMenuOpen);
}
function selectLanguage(e, language) {
e.stopPropagation();
setLangMenuOpen(false);
setLanguage(language);
}
return (
<header>
<div className="section">
<div className="button" onClick={save}>
<Header>
<Section>
<Button onClick={save}>
{recentlySaved
? '[link copied!]'
: saving ? '[saving...]' : '[save]'
}
</div>
<div className="button" onClick={toggleLangMenu}>
[language: {language}]
{langMenuOpen && (
<ul>
{languageIds.map(id => <li key={id} onClick={e => selectLanguage(e, id)}>{id}</li>)}
</ul>
)}
</div>
</div>
<div className="section">
<a className="button" href="https://bytebin.lucko.me/" target="_blank" rel="noreferrer">[not pasting code?]</a>
<a className="button" href="https://github.com/lucko/paste" target="_blank" rel="noreferrer">[about paste]</a>
</div>
</header>
</Button>
<MenuButton label="language" value={language} setValue={setLanguage} ids={languageIds} />
</Section>
<Section>
<MenuButton label="theme" value={theme} setValue={setTheme} ids={Object.keys(themes)} />
<Button as="a" href="https://github.com/lucko/paste" target="_blank" rel="noreferrer">[about]</Button>
</Section>
</Header>
)
}
const Header = styled.header`
position: fixed;
z-index: 2;
width: 100%;
height: 2em;
color: ${props => props.theme.primary};
background: ${props => props.theme.secondary};
display: flex;
justify-content: space-between;
`;
const Section = styled.div`
display: flex;
align-items: center;
`;
const Button = styled.div`
cursor: pointer;
height: 100%;
display: flex;
align-items: center;
padding: 0 .25em;
color: inherit;
text-decoration: none;
:hover {
background: ${props => props.theme.highlight};
}
`;
const Menu = styled.ul`
position: absolute;
top: 2em;
margin: 0;
padding: 0;
list-style: none;
background-color: ${props => props.theme.highlight};
> li {
padding: .15em .5em;
}
> li:hover {
background-color: ${props => props.theme.secondary};
}
`;
const MenuButton = ({ label, ids, value, setValue }) => {
const [open, setOpen] = useState(false);
function toggleOpen() {
setOpen(!open);
}
function select(e, id) {
e.stopPropagation();
setOpen(false);
setValue(id);
}
return (
<Button onClick={toggleOpen}>
[{label}: {value}]
{open && (
<Menu>
{ids.map(id => <li key={id} onClick={e => select(e, id)}>{id}</li>)}
</Menu>
)}
</Button>
)
}

View File

@@ -0,0 +1,155 @@
import styled from 'styled-components';
export default function EditorPrismStyle({ children }) {
return <Main>{children}</Main>
}
const Main = styled.main`
padding-top: 2em;
color: ${props => props.theme.editor.primary};
background: ${props => props.theme.editor.background};
code[class*="language-"],
pre[class*="language-"] {
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 1em;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: ${props => props.theme.editor.selection};
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: ${props => props.theme.editor.selection};
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: ${props => props.theme.editor.comment};
}
.token.punctuation {
color: ${props => props.theme.editor.punctuation};
}
.token.annotation {
color: ${props => props.theme.editor.annotation};
}
.token.namespace {
color: ${props => props.theme.editor.namespace};
}
.token.property,
.token.tag {
color: ${props => props.theme.editor.property};
}
.token.boolean {
color: ${props => props.theme.editor.keyword};
}
.token.constant {
color: ${props => props.theme.editor.constant};
}
.token.number,
.token.symbol,
.token.deleted {
color: ${props => props.theme.editor.number};
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: ${props => props.theme.editor.selector};
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: ${props => props.theme.editor.operator};
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: ${props => props.theme.editor.keyword};
}
.token.comment .token.keyword {
color: ${props => props.theme.editor.primary};
}
.token.comment .token.tag,
.token.comment .token.tag > .token.punctuation {
color: ${props => props.theme.editor.commentTag};
}
.token.function {
color: ${props => props.theme.editor.function};
}
.token.class-name {
color: ${props => props.theme.editor.className};
}
.token.regex,
.token.important,
.token.variable {
color: ${props => props.theme.editor.variable};
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
`;

View File

@@ -1,8 +1,8 @@
import styled from 'styled-components';
import ReactEditor from 'react-simple-code-editor';
import EditorPrismStyle from './EditorPrismStyle';
import { getHighlighter } from '../highlighting';
import 'prismjs/themes/prism.css';
export default function EditorTextArea({ code, setCode, language }) {
const highlight = getHighlighter(language);
@@ -14,16 +14,40 @@ export default function EditorTextArea({ code, setCode, language }) {
}
return (
<main>
<ReactEditor
<EditorPrismStyle>
<StyledReactEditor
value={code}
onValueChange={setCode}
highlight={highlightWithLineNumbers}
placeholder={'Type some code...'}
padding={10}
textareaId='code-area'
className='editor'
/>
</main>
</EditorPrismStyle>
)
}
const StyledReactEditor = styled(ReactEditor)`
counter-reset: line;
font-size: 16px;
outline: 0;
min-height: calc(100vh - 2em);
#code-area {
outline: none;
padding-left: 60px !important;
}
pre {
padding-left: 60px !important;
}
.editorLineNumber {
position: absolute;
left: 0px;
color: ${props => props.theme.editor.lineNumber};
text-align: right;
width: 40px;
font-weight: 100;
}
`;