diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..efe5fdb --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "arrowParens": "avoid", + "quoteProps": "consistent" +} \ No newline at end of file diff --git a/src/App.js b/src/App.js index a32580a..c3bc33f 100644 --- a/src/App.js +++ b/src/App.js @@ -17,8 +17,10 @@ async function loadFromBytebin(id) { const resp = await fetch('https://bytebin.lucko.me/' + id); if (resp.ok) { const content = await resp.text(); - const type = parseLanguageFromContentType(resp.headers.get('content-type')); - + const type = parseLanguageFromContentType( + resp.headers.get('content-type') + ); + document.title = 'paste | ' + id; return { ok: true, content, type }; } else { @@ -52,7 +54,7 @@ export default function App() { useEffect(() => { if (pasteId && state === INITIAL) { setState(LOADING); - setContent('Loading...') + setContent('Loading...'); loadFromBytebin(pasteId).then(({ ok, content, type }) => { if (ok) { setContent(content); @@ -63,11 +65,17 @@ export default function App() { setContent(get404Message(pasteId)); } setState(LOADED); - }) + }); } - }, [pasteId, state, setContent]) + }, [pasteId, state, setContent]); - return ; + return ( + + ); } function get404Message(pasteId) { @@ -82,4 +90,4 @@ function get404Message(pasteId) { not found: '${pasteId}' maybe the paste expired? `; -} \ No newline at end of file +} diff --git a/src/components/Editor.js b/src/components/Editor.js index c255044..0ea0047 100644 --- a/src/components/Editor.js +++ b/src/components/Editor.js @@ -8,38 +8,16 @@ 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'; - } - }); - const [fontSize, setFontSize] = useState(() => { - const preference = ls.get('fontsize'); - if (preference && preference >= 10 && preference <= 22) { - return preference; - } else { - return 16; - } - }); - - useEffect(() => { - if (theme === 'dark') { - ls.remove('theme'); - } else { - ls.set('theme', theme); - } - }, [theme]); - - useEffect(() => { - if (fontSize === 16) { - ls.remove('fontsize'); - } else { - ls.set('fontsize', fontSize); - } - }, [fontSize]); + const [theme, setTheme] = usePreference( + 'theme', + 'dark', + pref => !!themes[pref] + ); + const [fontSize, setFontSize, fontSizeCheck] = usePreference( + 'fontsize', + 16, + pref => pref >= 10 && pref <= 22 + ); useEffect(() => { if (contentType) { @@ -48,21 +26,53 @@ export default function Editor({ content, setContent, contentType }) { }, [contentType]); function zoom(delta) { - const result = fontSize + delta; - if (result && result >= 10 && result <= 22) { - setFontSize(result); + const newFontSize = fontSize + delta; + if (fontSizeCheck(newFontSize)) { + setFontSize(newFontSize); } } - return <> - - - - - + return ( + <> + + + + + + ); +} + +// hook used to load "preference" settings from local storage, or fall back to a default value. +function usePreference(id, defaultValue, valid) { + const [value, setValue] = useState(() => { + const pref = ls.get(id); + if (pref && valid(pref)) { + return pref; + } else { + return defaultValue; + } + }); + + useEffect(() => { + if (value === defaultValue) { + ls.remove(id); + } else { + ls.set(id, value); + } + }, [value, id, defaultValue]); + + return [value, setValue, valid]; } diff --git a/src/components/EditorControls.js b/src/components/EditorControls.js index 7c2feb5..9c8d283 100644 --- a/src/components/EditorControls.js +++ b/src/components/EditorControls.js @@ -8,24 +8,32 @@ import { MenuButton, Button } from './Menu'; import { languageIds } from '../highlighting'; import themes from '../style/themes'; -export default function EditorControls({ code, setCode, language, setLanguage, theme, setTheme, zoom }) { +export default function EditorControls({ + code, + setCode, + language, + setLanguage, + theme, + setTheme, + zoom, +}) { const [saving, setSaving] = useState(false); const [recentlySaved, setRecentlySaved] = useState(false); useEffect(() => { setRecentlySaved(false); - }, [code, language]) + }, [code, language]); const save = useCallback(() => { if (!code || recentlySaved) { return; } setSaving(true); - saveToBytebin(code, language).then((pasteId) => { + saveToBytebin(code, language).then(pasteId => { setSaving(false); setRecentlySaved(true); history.replace({ - pathname: pasteId + pathname: pasteId, }); copy(window.location.href); document.title = 'paste | ' + pasteId; @@ -33,8 +41,8 @@ export default function EditorControls({ code, setCode, language, setLanguage, t }, [code, language, recentlySaved]); useEffect(() => { - const listener = (e) => { - if ((e.ctrlKey || e.metaKey)) { + const listener = e => { + if (e.ctrlKey || e.metaKey) { if (e.key === 's' || e.key === 'S') { e.preventDefault(); save(); @@ -45,8 +53,8 @@ export default function EditorControls({ code, setCode, language, setLanguage, t zoom(e.key === '=' ? 1 : -1); } } - } - + }; + window.addEventListener('keydown', listener); return () => window.removeEventListener('keydown', listener); }, [save, zoom]); @@ -55,7 +63,7 @@ export default function EditorControls({ code, setCode, language, setLanguage, t setCode(''); setLanguage('plain'); history.replace({ - pathname: '/' + pathname: '/', }); document.title = 'paste'; } @@ -65,21 +73,36 @@ export default function EditorControls({ code, setCode, language, setLanguage, t
- +
- - + +
- ) + ); } const Header = styled.header` @@ -123,9 +146,9 @@ async function saveToBytebin(code, language) { headers: { 'Content-Type': contentType, 'Content-Encoding': 'gzip', - 'Accept': 'application/json' + 'Accept': 'application/json', }, - body: compressed + body: compressed, }); if (resp.ok) { diff --git a/src/components/EditorPrismStyle.js b/src/components/EditorPrismStyle.js index 39a6e75..6f5e7a0 100644 --- a/src/components/EditorPrismStyle.js +++ b/src/components/EditorPrismStyle.js @@ -1,7 +1,7 @@ import styled from 'styled-components'; export default function EditorPrismStyle({ children }) { - return
{children}
+ return
{children}
; } const Main = styled.main` @@ -9,8 +9,8 @@ const Main = styled.main` color: ${props => props.theme.editor.primary}; background: ${props => props.theme.editor.background}; - code[class*="language-"], - pre[class*="language-"] { + code[class*='language-'], + pre[class*='language-'] { text-shadow: 0 1px white; font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; font-size: 1em; @@ -31,29 +31,33 @@ const Main = styled.main` hyphens: none; } - pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, - code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { + 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 { + 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-"] { + code[class*='language-'], + pre[class*='language-'] { text-shadow: none; } } /* Code blocks */ - pre[class*="language-"] { + pre[class*='language-'] { padding: 1em; - margin: .5em 0; + margin: 0.5em 0; overflow: auto; } @@ -84,7 +88,7 @@ const Main = styled.main` .token.boolean { color: ${props => props.theme.editor.keyword}; } - + .token.constant { color: ${props => props.theme.editor.constant}; } @@ -152,4 +156,4 @@ const Main = styled.main` .token.entity { cursor: help; } -`; \ No newline at end of file +`; diff --git a/src/components/EditorTextArea.js b/src/components/EditorTextArea.js index c7eacd7..2892966 100644 --- a/src/components/EditorTextArea.js +++ b/src/components/EditorTextArea.js @@ -29,12 +29,12 @@ export default function EditorTextArea({ code, setCode, language, fontSize }) { placeholder={'Paste (or type) some code...'} padding={10} size={fontSize} - textareaId='code-area' + textareaId="code-area" autoFocus={true} onKeyDown={keydown} /> - ) + ); } const StyledReactEditor = styled(ReactEditor)` @@ -101,29 +101,43 @@ function handleKeydown(e, editor) { if (pair) { e.preventDefault(); editor._applyEdits({ - value: value.substring(0, selectionStart) + pair[0] + pair[1] + value.substring(selectionEnd), + value: + value.substring(0, selectionStart) + + pair[0] + + pair[1] + + value.substring(selectionEnd), selectionStart: selectionStart + 1, - selectionEnd: selectionStart + 1 + selectionEnd: selectionStart + 1, }); } // When pressing enter immediately after an open bracket, automatically add a newline plus extra indent - if (e.keyCode === KEYCODE_ENTER && selectionEnd !== 0 && value[selectionEnd - 1] === '{') { + if ( + e.keyCode === KEYCODE_ENTER && + selectionEnd !== 0 && + value[selectionEnd - 1] === '{' + ) { const line = editor._getLines(value, selectionStart).pop(); const matches = line.match(/^\s+/); - const existingIndent = (matches ? matches[0] : ''); + const existingIndent = matches ? matches[0] : ''; const indent = ' '; - const updatedValue = value.substring(0, selectionStart) + - '\n' + existingIndent + indent + - '\n' + existingIndent + value.substring(selectionEnd); - const updatedSelection = selectionStart + 1 /* newline */ + existingIndent.length + indent.length; + const updatedValue = + value.substring(0, selectionStart) + + '\n' + + existingIndent + + indent + + '\n' + + existingIndent + + value.substring(selectionEnd); + const updatedSelection = + selectionStart + 1 /* newline */ + existingIndent.length + indent.length; e.preventDefault(); editor._applyEdits({ value: updatedValue, selectionStart: updatedSelection, - selectionEnd: updatedSelection - }) + selectionEnd: updatedSelection, + }); } } diff --git a/src/components/Menu.js b/src/components/Menu.js index 369190c..e8aa365 100644 --- a/src/components/Menu.js +++ b/src/components/Menu.js @@ -6,7 +6,7 @@ export const Button = styled.div` height: 100%; display: flex; align-items: center; - padding: 0 .25em; + padding: 0 0.25em; color: inherit; text-decoration: none; @@ -32,7 +32,7 @@ const Menu = styled.ul` overflow: auto; > li { - padding: .15em .5em; + padding: 0.15em 0.5em; } > li:hover { @@ -48,7 +48,7 @@ export const MenuButton = ({ label, ids, value, setValue }) => { return; } - const listener = (e) => setOpen(false); + const listener = e => setOpen(false); window.addEventListener('click', listener); return () => window.removeEventListener('click', listener); }, [open, setOpen]); @@ -65,12 +65,17 @@ export const MenuButton = ({ label, ids, value, setValue }) => { return ( - ) -} \ No newline at end of file + ); +}; diff --git a/src/style/themes.js b/src/style/themes.js index be6fd41..9a811d8 100644 --- a/src/style/themes.js +++ b/src/style/themes.js @@ -22,8 +22,8 @@ const themes = { keyword: '#07a', function: '#DD4A68', className: '#DD4A68', - variable: '#e90' - } + variable: '#e90', + }, }, dark: { primary: '#022550', @@ -48,9 +48,9 @@ const themes = { keyword: '#1CCBEF', function: '#BCBCBC', className: '#82CF75', - variable: '#ee9900' - } - } + variable: '#ee9900', + }, + }, }; -export default themes; \ No newline at end of file +export default themes; diff --git a/src/highlighting.js b/src/util/highlighting.js similarity index 93% rename from src/highlighting.js rename to src/util/highlighting.js index 6f96ebc..8e87074 100644 --- a/src/highlighting.js +++ b/src/util/highlighting.js @@ -18,7 +18,7 @@ import 'prismjs/components/prism-javascript'; import 'prismjs/components/prism-json'; import 'prismjs/components/prism-kotlin'; //import 'prismjs/components/prism-log'; -import './prism/prism-log'; // TODO replace with above once released +import '../prism/prism-log'; // TODO replace with above once released import 'prismjs/components/prism-markdown'; //import 'prismjs/components/prism-php'; import 'prismjs/components/prism-protobuf'; @@ -58,10 +58,10 @@ export const languageIds = [ 'rust', 'sql', 'toml', - 'yaml' -] + 'yaml', +]; export function getHighlighter(language) { const grammar = languages[language] || {}; - return (input) => highlight(input, grammar); + return input => highlight(input, grammar); }