nice-ui
Version:
React design system, components, and utilities
189 lines (188 loc) • 6.46 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.TextEditor = void 0;
const React = require("react");
const loadCodeMirror_1 = require("./loadCodeMirror");
const useAsync_1 = require("react-use/lib/useAsync");
const nano_theme_1 = require("nano-theme");
const codeColor = '#f30';
const { useEffect, useRef, useState, useMemo } = React;
const blockClass = (0, nano_theme_1.rule)({
'& .CodeMirror': {
...nano_theme_1.theme.font.mono,
bgc: 'transparent',
},
'& .CodeMirror-gutters': {
bgc: 'transparent',
bdr: 0,
},
'& .CodeMirror-linenumber': {
pad: '1px 4px 0 4px',
col: '#ccc',
fz: '.7em',
},
'& .CodeMirror-placeholder': {
op: 0.8,
},
'& .CodeMirror .cm-spell-error': {
bgc: 'transparent !important',
bdb: `1px solid ${nano_theme_1.theme.color.sem.negative[0]}`,
},
// Highlighting.
'& .CodeMirror .cm-comment': {
// Single tick inline code.
col: codeColor,
},
'& .CodeMirror .cm-link': {
col: nano_theme_1.COLOR.LINK,
},
'& .CodeMirror .cm-url': {
col: nano_theme_1.COLOR.LINK,
},
});
const textareaClass = (0, nano_theme_1.rule)({
vis: 'hidden',
w: '1px',
h: '1px',
});
const defaultFontSize = 15;
const defaultLineHeight = 1.5;
const setEditorLines = (editor, lineHeight, lines) => {
const height = 10 + lines * lineHeight + 'px';
editor.setSize('100%', height);
};
const adjustLineNumber = (editor, lineHeight, minLines = 1) => {
const lines = Math.max(editor.getDoc().lineCount(), minLines);
setEditorLines(editor, lineHeight, lines);
};
const TextEditor = (props) => {
const dynamicClass = (0, nano_theme_1.useRule)((theme) => ({
'& .CodeMirror': {
col: theme.name === 'dark' ? '#ddd' : '000',
},
'& .CodeMirror-selected': {
bg: `${theme.isLight ? theme.g(0.9) : theme.g(0.8)} !important`,
},
'& .CodeMirror-cursor': {
borderLeftColor: theme.g(0, 0.5),
},
}));
const fontSize = props.fontSize || defaultFontSize;
const lineHeightInPx = Math.round(fontSize * (props.lineHeight || defaultLineHeight));
const { className, minLines = 1, onFocus, onBlur, disabled } = props;
const textareaRef = useRef(null);
const [editor, setEditor] = useState(null);
const { value: CodeMirror } = (0, useAsync_1.default)(loadCodeMirror_1.loadCodeMirror);
const controls = useMemo(() => {
if (!editor)
return;
const controls = {
getValue: () => editor.getDoc().getValue(),
getSelectionValue: () => {
return editor.getDoc().getSelection().toString();
},
setValue: (value) => editor.getDoc().setValue(value),
insert: (text, select) => {
const doc = editor.getDoc();
doc.replaceSelection(text, select);
},
clear: () => editor.getDoc().setValue(''),
focus: () => editor.focus(),
blur: () => editor.getInputField().blur(),
gotoEnd: () => editor.getDoc().setCursor(editor.getDoc().lineCount(), 0),
hasFocus: () => editor.hasFocus(),
selectAll: () => {
editor.execCommand('selectAll');
},
};
return controls;
}, [editor]);
useEffect(() => {
if (!CodeMirror)
return;
if (!textareaRef.current)
return;
if (typeof CodeMirrorSpellChecker === 'function') {
CodeMirrorSpellChecker({
codeMirrorInstance: CodeMirror,
});
}
const editor = CodeMirror.fromTextArea(textareaRef.current, {
mode: 'spell-checker',
// backdrop: 'gfm',
theme: 'default',
// This must be set to "textarea" if set to "contenteditable" or omitted
// it does not fire change event on mobile until space is pressed on mobile.
// If this setting is omitted, it defaults to "contenteditable" on mobile.
inputStyle: 'textarea',
placeholder: props.placeholder,
lineNumbers: false,
lineWrapping: true,
});
if (props.showAllLines) {
setEditorLines(editor, lineHeightInPx, minLines);
}
else {
editor.setSize('100%', '100%');
}
setEditor(editor);
}, [textareaRef.current, CodeMirror]);
useEffect(() => {
if (!editor)
return;
if (!props.showAllLines)
return;
const onChange = () => {
adjustLineNumber(editor, lineHeightInPx, minLines);
};
adjustLineNumber(editor, lineHeightInPx, minLines);
editor.on('change', onChange);
return () => editor.off('change', onChange);
}, [editor, props.showAllLines]);
useEffect(() => {
if (!editor)
return;
if (!controls)
return;
if (!props.onChange)
return;
const listener = () => props.onChange && props.onChange(controls);
editor.on('change', listener);
return () => editor.off('change', listener);
}, [editor, controls, props.onChange]);
useEffect(() => {
if (controls && props.onControls)
props.onControls(controls);
}, [controls]);
useEffect(() => {
if (!editor)
return;
if (!onFocus)
return;
editor.on('focus', onFocus);
return () => editor.off('focus', onFocus);
}, [editor, onFocus]);
useEffect(() => {
if (!editor)
return;
if (!onBlur)
return;
editor.on('blur', onBlur);
return () => editor.off('blur', onBlur);
}, [editor, onBlur]);
useEffect(() => {
if (!editor)
return;
if (disabled)
editor.setOption('readOnly', true);
else
editor.setOption('readOnly', false);
}, [editor, disabled]);
const style = {
fontSize: fontSize + 'px',
lineHeight: lineHeightInPx + 'px',
};
return (React.createElement("div", { className: (className || '') + blockClass + dynamicClass, style: style },
React.createElement("textarea", { className: textareaClass, ref: textareaRef })));
};
exports.TextEditor = TextEditor;
;