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;
}
`;

View File

@@ -11,6 +11,8 @@ import 'prismjs/components/prism-go';
import 'prismjs/components/prism-groovy';
import 'prismjs/components/prism-haskell';
import 'prismjs/components/prism-java';
import 'prismjs/components/prism-javadoclike';
import 'prismjs/components/prism-javadoc';
import 'prismjs/components/prism-javastacktrace';
import 'prismjs/components/prism-javascript';
import 'prismjs/components/prism-json';
@@ -61,6 +63,6 @@ export const languageIds = [
]
export function getHighlighter(language) {
const grammar = language === 'plain' ? {} : languages[language];
const grammar = languages[language] || {};
return (input) => highlight(input, grammar);
}

View File

@@ -10,82 +10,3 @@ body {
* {
box-sizing: border-box;
}
header {
position: fixed;
z-index: 2;
width: 100%;
height: 2em;
background: #025;
color: #adf;
display: flex;
justify-content: space-between;
}
header > div {
display: flex;
align-items: center;
}
header .button {
cursor: pointer;
height: 100%;
display: flex;
align-items: center;
padding: 0 .25em;
}
header a {
color: inherit;
text-decoration: none;
}
header .button:hover {
background: #36368c;
}
header .button > ul {
position: absolute;
top: 2em;
margin: 0;
padding: 0;
list-style: none;
background-color: #36368c;
}
header .button > ul > li {
padding: .15em .5em;
}
header .button > ul > li:hover {
background-color: #025;
}
main {
padding-top: 2em;
}
.editor {
counter-reset: line;
font-size: 16px;
outline: 0;
min-height: calc(100vh - 2em);
}
.editor #code-area {
outline: none;
padding-left: 60px !important;
}
.editor pre {
padding-left: 60px !important;
}
.editor .editorLineNumber {
position: absolute;
left: 0px;
color: #cccccc;
text-align: right;
width: 40px;
font-weight: 100;
}

56
src/style/themes.js Normal file
View File

@@ -0,0 +1,56 @@
const themes = {
light: {
primary: '#aaddff',
secondary: '#022550',
highlight: '#36368c',
editor: {
background: 'none',
lineNumber: '#cccccc',
primary: 'black',
selection: '#b3d4fc',
comment: 'slategray',
commentTag: '#A082BD',
punctuation: '#999',
annotation: '#999',
namespace: 'slategray',
property: '#e90',
constant: '#905',
number: '#905',
selector: '#690',
operator: '#9a6e3a',
keyword: '#07a',
function: '#DD4A68',
className: '#DD4A68',
variable: '#e90'
}
},
dark: {
primary: '#aaddff',
secondary: '#022550',
highlight: '#36368c',
editor: {
background: '#041f29',
lineNumber: '#81969A',
primary: '#E0E2E4',
selection: '#E0E2E4',
comment: '#7D8C93',
commentTag: '#A082BD',
punctuation: '#E8E2B7',
annotation: '#00FFF8',
namespace: '#7CA8CF',
property: '#ee9900',
constant: '#F77669',
number: '#FFCD22',
selector: '#E2B671',
operator: '#E8E2B7',
keyword: '#1CCBEF',
function: '#BCBCBC',
className: '#82CF75',
variable: '#ee9900'
}
}
};
export default themes;