add themes
This commit is contained in:
@@ -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>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
155
src/components/EditorPrismStyle.js
Normal file
155
src/components/EditorPrismStyle.js
Normal 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;
|
||||
}
|
||||
`;
|
||||
@@ -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;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
56
src/style/themes.js
Normal 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;
|
||||
Reference in New Issue
Block a user