Files
lucko-paste/src/components/EditorTextArea.js

130 lines
3.4 KiB
JavaScript
Raw Normal View History

2021-03-28 17:39:16 +01:00
import { useRef } from 'react';
2021-03-27 11:49:46 +00:00
import styled from 'styled-components';
2021-03-26 22:00:12 +00:00
import ReactEditor from 'react-simple-code-editor';
2021-03-27 11:49:46 +00:00
import EditorPrismStyle from './EditorPrismStyle';
2021-03-26 22:00:12 +00:00
import { getHighlighter } from '../highlighting';
2021-03-27 14:17:28 +00:00
export default function EditorTextArea({ code, setCode, language, fontSize }) {
2021-03-26 22:00:12 +00:00
const highlight = getHighlighter(language);
function highlightWithLineNumbers(input, grammar) {
return highlight(input, grammar)
.split('\n')
.map((line, i) => `<span class='editorLineNumber'>${i + 1}</span>${line}`)
.join('\n');
}
2021-03-28 17:39:16 +01:00
const editorRef = useRef();
function keydown(e) {
handleKeydown(e, editorRef.current);
}
2021-03-26 22:00:12 +00:00
return (
2021-03-27 11:49:46 +00:00
<EditorPrismStyle>
<StyledReactEditor
2021-03-28 17:39:16 +01:00
ref={editorRef}
2021-03-26 22:00:12 +00:00
value={code}
onValueChange={setCode}
highlight={highlightWithLineNumbers}
2021-03-27 20:05:15 +00:00
placeholder={'Paste (or type) some code...'}
2021-03-26 22:00:12 +00:00
padding={10}
2021-03-27 14:17:28 +00:00
size={fontSize}
2021-03-26 22:00:12 +00:00
textareaId='code-area'
autoFocus={true}
2021-03-28 17:39:16 +01:00
onKeyDown={keydown}
2021-03-26 22:00:12 +00:00
/>
2021-03-27 11:49:46 +00:00
</EditorPrismStyle>
2021-03-26 22:00:12 +00:00
)
}
2021-03-27 11:49:46 +00:00
const StyledReactEditor = styled(ReactEditor)`
counter-reset: line;
2021-03-27 14:17:28 +00:00
font-size: ${props => props.size}px;
2021-03-27 11:49:46 +00:00
outline: 0;
2021-03-27 14:17:28 +00:00
min-height: calc(100vh - 2rem);
2021-03-27 11:49:46 +00:00
#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;
user-select: none;
2021-03-27 11:49:46 +00:00
}
`;
2021-03-28 17:39:16 +01:00
const KEYCODE_ENTER = 13;
const KEYCODE_PARENS = 57;
const KEYCODE_BRACKETS = 219;
const KEYCODE_QUOTE = 222;
const KEYCODE_BACK_QUOTE = 192;
function getPair(e) {
if (e.keyCode === KEYCODE_PARENS && e.shiftKey) {
return ['(', ')'];
} else if (e.keyCode === KEYCODE_BRACKETS) {
if (e.shiftKey) {
return ['{', '}'];
} else {
return ['[', ']'];
}
} else if (e.keyCode === KEYCODE_QUOTE) {
if (e.shiftKey) {
return ['"', '"'];
} else {
return ["'", "'"];
}
} else if (e.keyCode === KEYCODE_BACK_QUOTE && !e.shiftKey) {
return ['`', '`'];
}
return null;
}
function handleKeydown(e, editor) {
const { value, selectionStart, selectionEnd } = e.target;
if (selectionStart !== selectionEnd) {
return;
}
// When entering an open bracket/quote, add the closing one
const pair = getPair(e);
if (pair) {
e.preventDefault();
editor._applyEdits({
value: value.substring(0, selectionStart) + pair[0] + pair[1] + value.substring(selectionEnd),
selectionStart: 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] === '{') {
const line = editor._getLines(value, selectionStart).pop();
const matches = line.match(/^\s+/);
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;
e.preventDefault();
editor._applyEdits({
value: updatedValue,
selectionStart: updatedSelection,
selectionEnd: updatedSelection
})
}
}