mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-14 00:26:51 +08:00
init: the first public commit for AFFiNE
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
import React, { useEffect, useRef, useImperativeHandle } from 'react';
|
||||
import { EditorState, EditorStateConfig, Extension } from '@codemirror/state';
|
||||
import { EditorView, ViewUpdate } from '@codemirror/view';
|
||||
import { useCodeMirror } from './use-code-mirror';
|
||||
|
||||
export * from './use-code-mirror';
|
||||
|
||||
export interface ReactCodeMirrorProps
|
||||
extends Omit<EditorStateConfig, 'doc' | 'extensions'>,
|
||||
Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'placeholder'> {
|
||||
/** value of the auto created model in the editor. */
|
||||
value?: string;
|
||||
height?: string;
|
||||
minHeight?: string;
|
||||
maxHeight?: string;
|
||||
width?: string;
|
||||
minWidth?: string;
|
||||
maxWidth?: string;
|
||||
/** focus on the editor. */
|
||||
autoFocus?: boolean;
|
||||
/** Enables a placeholder—a piece of example content to show when the editor is empty. */
|
||||
placeholder?: string | HTMLElement;
|
||||
/**
|
||||
* `light` / `dark` / `Extension` Defaults to `light`.
|
||||
* @default light
|
||||
*/
|
||||
theme?: 'light' | 'dark' | Extension;
|
||||
/**
|
||||
* Whether to optional basicSetup by default
|
||||
* @default true
|
||||
*/
|
||||
basicSetup?: boolean;
|
||||
/**
|
||||
* This disables editing of the editor content by the user.
|
||||
* @default true
|
||||
*/
|
||||
editable?: boolean;
|
||||
readOnly?: boolean;
|
||||
/**
|
||||
* Whether to optional basicSetup by default
|
||||
* @default true
|
||||
*/
|
||||
indentWithTab?: boolean;
|
||||
/** Fired whenever a change occurs to the document. */
|
||||
onChange?(value: string, viewUpdate: ViewUpdate): void;
|
||||
/** Fired whenever a change occurs to the document. There is a certain difference with `onChange`. */
|
||||
onUpdate?(viewUpdate: ViewUpdate): void;
|
||||
handleKeyArrowUp?: () => void;
|
||||
handleKeyArrowDown?: () => void;
|
||||
/**
|
||||
* Extension values can be [provided](https://codemirror.net/6/docs/ref/#state.EditorStateConfig.extensions) when creating a state to attach various kinds of configuration and behavior information.
|
||||
* They can either be built-in extension-providing objects,
|
||||
* such as [state fields](https://codemirror.net/6/docs/ref/#state.StateField) or [facet providers](https://codemirror.net/6/docs/ref/#state.Facet.of),
|
||||
* or objects with an extension in its `extension` property. Extensions can be nested in arrays arbitrarily deep—they will be flattened when processed.
|
||||
*/
|
||||
extensions?: Extension[];
|
||||
/**
|
||||
* If the view is going to be mounted in a shadow root or document other than the one held by the global variable document (the default), you should pass it here.
|
||||
* Originally from the [config of EditorView](https://codemirror.net/6/docs/ref/#view.EditorView.constructor%5Econfig.root)
|
||||
*/
|
||||
root?: ShadowRoot | Document;
|
||||
}
|
||||
|
||||
export interface ReactCodeMirrorRef {
|
||||
editor?: HTMLDivElement | null;
|
||||
state?: EditorState;
|
||||
view?: EditorView;
|
||||
}
|
||||
|
||||
const ReactCodeMirror = React.forwardRef<
|
||||
ReactCodeMirrorRef,
|
||||
ReactCodeMirrorProps
|
||||
>((props, ref) => {
|
||||
const {
|
||||
className,
|
||||
value = '',
|
||||
selection,
|
||||
extensions = [],
|
||||
onChange,
|
||||
onUpdate,
|
||||
handleKeyArrowUp,
|
||||
handleKeyArrowDown,
|
||||
autoFocus,
|
||||
theme = 'light',
|
||||
height,
|
||||
minHeight,
|
||||
maxHeight,
|
||||
width,
|
||||
minWidth,
|
||||
maxWidth,
|
||||
basicSetup,
|
||||
placeholder,
|
||||
indentWithTab,
|
||||
editable,
|
||||
readOnly,
|
||||
root,
|
||||
...other
|
||||
} = props;
|
||||
const editor = useRef<HTMLDivElement>(null);
|
||||
const { state, view, container, setContainer } = useCodeMirror({
|
||||
container: editor.current,
|
||||
root,
|
||||
value,
|
||||
autoFocus,
|
||||
theme,
|
||||
height,
|
||||
minHeight,
|
||||
maxHeight,
|
||||
width,
|
||||
minWidth,
|
||||
maxWidth,
|
||||
basicSetup,
|
||||
placeholder,
|
||||
indentWithTab,
|
||||
editable,
|
||||
readOnly,
|
||||
selection,
|
||||
onChange,
|
||||
onUpdate,
|
||||
extensions,
|
||||
handleKeyArrowUp,
|
||||
handleKeyArrowDown,
|
||||
});
|
||||
useImperativeHandle(ref, () => ({ editor: container, state, view }), [
|
||||
container,
|
||||
state,
|
||||
view,
|
||||
]);
|
||||
useEffect(() => {
|
||||
setContainer(editor.current);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// check type of value
|
||||
if (typeof value !== 'string') {
|
||||
throw new Error(`value must be typeof string but got ${typeof value}`);
|
||||
}
|
||||
|
||||
const defaultClassNames =
|
||||
typeof theme === 'string' ? `cm-theme-${theme}` : 'cm-theme';
|
||||
return (
|
||||
<div
|
||||
ref={editor}
|
||||
className={`${defaultClassNames}${
|
||||
className ? ` ${className}` : ''
|
||||
}`}
|
||||
{...other}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
ReactCodeMirror.displayName = 'CodeMirror';
|
||||
|
||||
export default ReactCodeMirror;
|
||||
@@ -0,0 +1,220 @@
|
||||
import { FC, useState, useMemo, useRef, useEffect } from 'react';
|
||||
import { StyleWithAtRules } from 'style9';
|
||||
|
||||
import { CreateView } from '@toeverything/framework/virgo';
|
||||
import CodeMirror from './CodeMirror';
|
||||
import { styled } from '@toeverything/components/ui';
|
||||
import DeleteSweepOutlinedIcon from '@mui/icons-material/DeleteSweepOutlined';
|
||||
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { html } from '@codemirror/lang-html';
|
||||
import { css } from '@codemirror/lang-css';
|
||||
import { json } from '@codemirror/lang-json';
|
||||
import { python } from '@codemirror/lang-python';
|
||||
import { markdown } from '@codemirror/lang-markdown';
|
||||
import { xml } from '@codemirror/lang-xml';
|
||||
import { sql, MySQL, PostgreSQL } from '@codemirror/lang-sql';
|
||||
import { java } from '@codemirror/lang-java';
|
||||
import { rust } from '@codemirror/lang-rust';
|
||||
import { cpp } from '@codemirror/lang-cpp';
|
||||
import { lezer } from '@codemirror/lang-lezer';
|
||||
import { php } from '@codemirror/lang-php';
|
||||
import { StreamLanguage } from '@codemirror/language';
|
||||
import { go } from '@codemirror/legacy-modes/mode/go';
|
||||
import { ruby } from '@codemirror/legacy-modes/mode/ruby';
|
||||
import { shell } from '@codemirror/legacy-modes/mode/shell';
|
||||
import { lua } from '@codemirror/legacy-modes/mode/lua';
|
||||
import { swift } from '@codemirror/legacy-modes/mode/swift';
|
||||
import { tcl } from '@codemirror/legacy-modes/mode/tcl';
|
||||
import { yaml } from '@codemirror/legacy-modes/mode/yaml';
|
||||
import { vb } from '@codemirror/legacy-modes/mode/vb';
|
||||
import { powerShell } from '@codemirror/legacy-modes/mode/powershell';
|
||||
import { brainfuck } from '@codemirror/legacy-modes/mode/brainfuck';
|
||||
import { stylus } from '@codemirror/legacy-modes/mode/stylus';
|
||||
import { erlang } from '@codemirror/legacy-modes/mode/erlang';
|
||||
import { nginx } from '@codemirror/legacy-modes/mode/nginx';
|
||||
import { perl } from '@codemirror/legacy-modes/mode/perl';
|
||||
import { pascal } from '@codemirror/legacy-modes/mode/pascal';
|
||||
import { liveScript } from '@codemirror/legacy-modes/mode/livescript';
|
||||
import { scheme } from '@codemirror/legacy-modes/mode/scheme';
|
||||
import { toml } from '@codemirror/legacy-modes/mode/toml';
|
||||
import { vbScript } from '@codemirror/legacy-modes/mode/vbscript';
|
||||
import { clojure } from '@codemirror/legacy-modes/mode/clojure';
|
||||
import { coffeeScript } from '@codemirror/legacy-modes/mode/coffeescript';
|
||||
import { dockerFile } from '@codemirror/legacy-modes/mode/dockerfile';
|
||||
import { julia } from '@codemirror/legacy-modes/mode/julia';
|
||||
import { r } from '@codemirror/legacy-modes/mode/r';
|
||||
import { Extension } from '@codemirror/state';
|
||||
import { Select } from '../../components/select';
|
||||
import {
|
||||
useOnSelect,
|
||||
WrapperWithPendantAndDragDrop,
|
||||
} from '@toeverything/components/editor-core';
|
||||
interface CreateCodeView extends CreateView {
|
||||
style9?: StyleWithAtRules;
|
||||
containerClassName?: string;
|
||||
}
|
||||
const langs: Record<string, any> = {
|
||||
javascript,
|
||||
jsx: () => javascript({ jsx: true }),
|
||||
typescript: () => javascript({ typescript: true }),
|
||||
tsx: () => javascript({ jsx: true, typescript: true }),
|
||||
json,
|
||||
html,
|
||||
css,
|
||||
python,
|
||||
markdown,
|
||||
xml,
|
||||
sql,
|
||||
mysql: () => sql({ dialect: MySQL }),
|
||||
pgsql: () => sql({ dialect: PostgreSQL }),
|
||||
java,
|
||||
rust,
|
||||
cpp,
|
||||
lezer,
|
||||
php,
|
||||
go: () => StreamLanguage.define(go),
|
||||
shell: () => StreamLanguage.define(shell),
|
||||
lua: () => StreamLanguage.define(lua),
|
||||
swift: () => StreamLanguage.define(swift),
|
||||
tcl: () => StreamLanguage.define(tcl),
|
||||
yaml: () => StreamLanguage.define(yaml),
|
||||
vb: () => StreamLanguage.define(vb),
|
||||
powershell: () => StreamLanguage.define(powerShell),
|
||||
brainfuck: () => StreamLanguage.define(brainfuck),
|
||||
stylus: () => StreamLanguage.define(stylus),
|
||||
erlang: () => StreamLanguage.define(erlang),
|
||||
nginx: () => StreamLanguage.define(nginx),
|
||||
perl: () => StreamLanguage.define(perl),
|
||||
ruby: () => StreamLanguage.define(ruby),
|
||||
pascal: () => StreamLanguage.define(pascal),
|
||||
livescript: () => StreamLanguage.define(liveScript),
|
||||
scheme: () => StreamLanguage.define(scheme),
|
||||
toml: () => StreamLanguage.define(toml),
|
||||
vbscript: () => StreamLanguage.define(vbScript),
|
||||
clojure: () => StreamLanguage.define(clojure),
|
||||
coffeescript: () => StreamLanguage.define(coffeeScript),
|
||||
julia: () => StreamLanguage.define(julia),
|
||||
dockerfile: () => StreamLanguage.define(dockerFile),
|
||||
r: () => StreamLanguage.define(r),
|
||||
// clike: () => StreamLanguage.define(clike),
|
||||
// clike: () => clike({ }),
|
||||
};
|
||||
|
||||
const CodeBlock = styled('div')(({ theme }) => ({
|
||||
backgroundColor: '#f8f9fa',
|
||||
border: '1px solid #e0e0e0',
|
||||
'&:hover': {
|
||||
'.operation': {
|
||||
display: 'flex',
|
||||
},
|
||||
},
|
||||
'.operation': {
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
borderBottom: '1px solid #e0e0e0',
|
||||
padding: '0 8px 0px',
|
||||
},
|
||||
'.delete-block': {
|
||||
padding: '10px 4px 10px 10px',
|
||||
},
|
||||
'.cm-focused': {
|
||||
outline: 'none !important',
|
||||
},
|
||||
'.select': {
|
||||
marginTop: '10px',
|
||||
select: {
|
||||
border: 'none',
|
||||
outline: 'none',
|
||||
height: '22px',
|
||||
lineHeight: theme.affine.typography.body1.lineHeight,
|
||||
paddingLeft: '10px',
|
||||
maxHeight: '400px',
|
||||
background: '#f1f1f1',
|
||||
borderRadius: '6px',
|
||||
},
|
||||
},
|
||||
}));
|
||||
export const CodeView: FC<CreateCodeView> = ({ block, editor }) => {
|
||||
const init_value: string = block.getProperty('text')?.value?.[0]?.text;
|
||||
const lang_type: string = block.getProperty('lang')?.value?.[0]?.text;
|
||||
const [mode, setMode] = useState('javascript');
|
||||
const [extensions, setExtensions] = useState<Extension[]>();
|
||||
const codeMirror = useRef();
|
||||
useOnSelect(block.id, (is_select: boolean) => {
|
||||
if (codeMirror.current) {
|
||||
//@ts-ignore
|
||||
codeMirror?.current?.view?.focus();
|
||||
}
|
||||
});
|
||||
const onChange = (value: any, codeEditor: any) => {
|
||||
console.log(value);
|
||||
block.setProperty('text', {
|
||||
value: [{ text: value }],
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
handleLangChange(lang_type ? lang_type : 'javascript');
|
||||
}, []);
|
||||
function handleLangChange(lang: string) {
|
||||
block.setProperty('lang', lang);
|
||||
setMode(lang);
|
||||
setExtensions([langs[lang]()]);
|
||||
}
|
||||
const handle_remove_block = () => {
|
||||
block.remove();
|
||||
};
|
||||
const handleKeyArrowDown = () => {
|
||||
editor.selectionManager.activeNextNode(block.id, 'start');
|
||||
};
|
||||
const handleKeyArrowUp = () => {
|
||||
editor.selectionManager.activePreviousNode(block.id, 'start');
|
||||
};
|
||||
return (
|
||||
<WrapperWithPendantAndDragDrop editor={editor} block={block}>
|
||||
<CodeBlock
|
||||
onKeyDown={e => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<div className="operation">
|
||||
<div className="select">
|
||||
<Select
|
||||
label="Lang"
|
||||
options={Object.keys(langs)}
|
||||
value={mode}
|
||||
onChange={evn => handleLangChange(evn.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
className="delete-block"
|
||||
onClick={handle_remove_block}
|
||||
>
|
||||
<DeleteSweepOutlinedIcon
|
||||
className="delete-icon"
|
||||
fontSize="small"
|
||||
sx={{
|
||||
color: 'rgba(0,0,0,.5)',
|
||||
cursor: 'pointer',
|
||||
'&:hover': { color: 'rgba(0,0,0,.9)' },
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CodeMirror
|
||||
ref={codeMirror}
|
||||
value={init_value}
|
||||
height={'auto'}
|
||||
extensions={extensions}
|
||||
onChange={onChange}
|
||||
handleKeyArrowDown={handleKeyArrowDown}
|
||||
handleKeyArrowUp={handleKeyArrowUp}
|
||||
/>
|
||||
</CodeBlock>
|
||||
</WrapperWithPendantAndDragDrop>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
import {
|
||||
BaseView,
|
||||
AsyncBlock,
|
||||
getTextProperties,
|
||||
CreateView,
|
||||
SelectBlock,
|
||||
getTextHtml,
|
||||
} from '@toeverything/framework/virgo';
|
||||
import {
|
||||
Protocol,
|
||||
DefaultColumnsValue,
|
||||
} from '@toeverything/datasource/db-service';
|
||||
import { CodeView } from './CodeView';
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
export class CodeBlock extends BaseView {
|
||||
type = Protocol.Block.Type.code;
|
||||
public override selectable = true;
|
||||
public override activatable = false;
|
||||
// View = CodeView;
|
||||
View = CodeView;
|
||||
override async onCreate(block: AsyncBlock): Promise<AsyncBlock> {
|
||||
if (!block.getProperty('text')) {
|
||||
await block.setProperty('text', {
|
||||
value: [{ text: '' }],
|
||||
});
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
override getSelProperties(
|
||||
block: AsyncBlock,
|
||||
selectInfo: any
|
||||
): DefaultColumnsValue {
|
||||
const properties = super.getSelProperties(block, selectInfo);
|
||||
return getTextProperties(properties, selectInfo);
|
||||
}
|
||||
|
||||
// TODO: internal format not implemented yet
|
||||
override html2block(
|
||||
el: Element,
|
||||
parseEl: (el: Element) => any[]
|
||||
): any[] | null {
|
||||
const tag_name = el.tagName;
|
||||
if (tag_name === 'CODE') {
|
||||
const childNodes = el.childNodes;
|
||||
let text_value = '';
|
||||
for (let i = 0; i < childNodes.length; i++) {
|
||||
const blocks_info = parseEl(childNodes[i] as Element);
|
||||
for (let j = 0; j < blocks_info.length; j++) {
|
||||
if (blocks_info[j].type === 'text') {
|
||||
const block_texts =
|
||||
blocks_info[j].properties.text.value;
|
||||
if (block_texts.length > 0) {
|
||||
text_value += block_texts[0].text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [
|
||||
{
|
||||
type: this.type,
|
||||
properties: {
|
||||
text: { value: [{ text: text_value }] },
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
override async block2html(
|
||||
block: AsyncBlock,
|
||||
children: SelectBlock[],
|
||||
generateHtml: (el: any[]) => Promise<string>
|
||||
): Promise<string> {
|
||||
let content = getTextHtml(block);
|
||||
content += await generateHtml(children);
|
||||
return `<code>${content}</code>`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
// import { basicSetup, minimalSetup } from 'codemirror';
|
||||
import { EditorState, StateEffect, Extension } from '@codemirror/state';
|
||||
import {
|
||||
indentWithTab,
|
||||
defaultKeymap,
|
||||
history,
|
||||
historyKeymap,
|
||||
} from '@codemirror/commands';
|
||||
import {
|
||||
EditorView,
|
||||
keymap,
|
||||
ViewUpdate,
|
||||
placeholder,
|
||||
highlightSpecialChars,
|
||||
drawSelection,
|
||||
highlightActiveLine,
|
||||
dropCursor,
|
||||
rectangularSelection,
|
||||
crosshairCursor,
|
||||
lineNumbers,
|
||||
highlightActiveLineGutter,
|
||||
} from '@codemirror/view';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
import { ReactCodeMirrorProps } from './CodeMirror';
|
||||
|
||||
import {
|
||||
defaultHighlightStyle,
|
||||
syntaxHighlighting,
|
||||
indentOnInput,
|
||||
bracketMatching,
|
||||
foldGutter,
|
||||
foldKeymap,
|
||||
} from '@codemirror/language';
|
||||
// import {searchKeymap, highlightSelectionMatches} from "@codemirror/search"
|
||||
// import {autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap} from "@codemirror/autocomplete"
|
||||
// import {lintKeymap} from "@codemirror/lint"
|
||||
|
||||
export interface UseCodeMirror extends ReactCodeMirrorProps {
|
||||
container?: HTMLDivElement | null;
|
||||
}
|
||||
|
||||
const basicSetup: Extension = [
|
||||
lineNumbers(),
|
||||
highlightActiveLineGutter(),
|
||||
highlightSpecialChars(),
|
||||
history(),
|
||||
foldGutter(),
|
||||
drawSelection(),
|
||||
dropCursor(),
|
||||
EditorState.allowMultipleSelections.of(true),
|
||||
indentOnInput(),
|
||||
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||||
bracketMatching(),
|
||||
rectangularSelection(),
|
||||
crosshairCursor(),
|
||||
highlightActiveLine(),
|
||||
keymap.of([...defaultKeymap, ...historyKeymap, ...foldKeymap]),
|
||||
];
|
||||
|
||||
export function useCodeMirror(props: UseCodeMirror) {
|
||||
const {
|
||||
value,
|
||||
selection,
|
||||
onChange,
|
||||
onUpdate,
|
||||
handleKeyArrowUp,
|
||||
handleKeyArrowDown,
|
||||
extensions = [],
|
||||
autoFocus,
|
||||
theme = 'light',
|
||||
height = '',
|
||||
minHeight = '',
|
||||
maxHeight = '',
|
||||
placeholder: placeholderStr = '',
|
||||
width = '',
|
||||
minWidth = '',
|
||||
maxWidth = '',
|
||||
editable = true,
|
||||
readOnly = false,
|
||||
indentWithTab: defaultIndentWithTab = true,
|
||||
basicSetup: defaultBasicSetup = true,
|
||||
|
||||
root,
|
||||
} = props;
|
||||
const [container, setContainer] = useState(props.container);
|
||||
const [view, setView] = useState<EditorView>();
|
||||
const [state, setState] = useState<EditorState>();
|
||||
const defaultLightThemeOption = EditorView.theme(
|
||||
{
|
||||
'&': {
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
},
|
||||
{
|
||||
dark: false,
|
||||
}
|
||||
);
|
||||
const defaultThemeOption = EditorView.theme({
|
||||
'&': {
|
||||
height,
|
||||
minHeight,
|
||||
maxHeight,
|
||||
width,
|
||||
minWidth,
|
||||
maxWidth,
|
||||
},
|
||||
});
|
||||
const updateListener = EditorView.updateListener.of((vu: ViewUpdate) => {
|
||||
if (vu.docChanged && typeof onChange === 'function') {
|
||||
const doc = vu.state.doc;
|
||||
const value = doc.toString();
|
||||
onChange(value, vu);
|
||||
}
|
||||
});
|
||||
// keymap.of([{''}])
|
||||
// keymap.of()
|
||||
const upFunction = [
|
||||
{
|
||||
key: 'ArrowUp',
|
||||
run: () => {
|
||||
handleKeyArrowUp();
|
||||
return false;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'ArrowDown',
|
||||
run: (e: any) => {
|
||||
handleKeyArrowDown();
|
||||
|
||||
return false;
|
||||
},
|
||||
},
|
||||
];
|
||||
// completionKeymap
|
||||
|
||||
let getExtensions = [updateListener, keymap.of(upFunction)];
|
||||
|
||||
if (defaultIndentWithTab) {
|
||||
getExtensions.unshift(keymap.of([indentWithTab]));
|
||||
}
|
||||
if (defaultBasicSetup) {
|
||||
getExtensions.unshift(basicSetup);
|
||||
}
|
||||
|
||||
if (placeholderStr) {
|
||||
getExtensions.unshift(placeholder(placeholderStr));
|
||||
}
|
||||
|
||||
switch (theme) {
|
||||
case 'light':
|
||||
getExtensions.push(defaultLightThemeOption);
|
||||
break;
|
||||
case 'dark':
|
||||
getExtensions.push(oneDark);
|
||||
break;
|
||||
default:
|
||||
getExtensions.push(theme);
|
||||
break;
|
||||
}
|
||||
|
||||
if (editable === false) {
|
||||
getExtensions.push(EditorView.editable.of(false));
|
||||
}
|
||||
if (readOnly) {
|
||||
getExtensions.push(EditorState.readOnly.of(true));
|
||||
}
|
||||
|
||||
if (onUpdate && typeof onUpdate === 'function') {
|
||||
getExtensions.push(EditorView.updateListener.of(onUpdate));
|
||||
}
|
||||
getExtensions.unshift(keymap.of([indentWithTab]));
|
||||
getExtensions = getExtensions.concat(extensions);
|
||||
|
||||
useEffect(() => {
|
||||
if (container && !state) {
|
||||
const stateCurrent = EditorState.create({
|
||||
doc: value,
|
||||
selection,
|
||||
extensions: getExtensions,
|
||||
});
|
||||
setState(stateCurrent);
|
||||
if (!view) {
|
||||
const viewCurrent = new EditorView({
|
||||
state: stateCurrent,
|
||||
parent: container,
|
||||
root,
|
||||
});
|
||||
setView(viewCurrent);
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
if (view) {
|
||||
setView(undefined);
|
||||
}
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [container, state]);
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
if (view) {
|
||||
view.destroy();
|
||||
setView(undefined);
|
||||
}
|
||||
},
|
||||
[view]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (autoFocus && view) {
|
||||
view.focus();
|
||||
}
|
||||
}, [autoFocus, view]);
|
||||
|
||||
useEffect(() => {
|
||||
const currentValue = view ? view.state.doc.toString() : '';
|
||||
if (view && value !== currentValue) {
|
||||
view.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: currentValue.length,
|
||||
insert: value || '',
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [value, view]);
|
||||
|
||||
useEffect(() => {
|
||||
if (view) {
|
||||
view.dispatch({
|
||||
effects: StateEffect.reconfigure.of(getExtensions),
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
theme,
|
||||
extensions,
|
||||
height,
|
||||
minHeight,
|
||||
maxHeight,
|
||||
width,
|
||||
placeholderStr,
|
||||
minWidth,
|
||||
maxWidth,
|
||||
editable,
|
||||
defaultIndentWithTab,
|
||||
defaultBasicSetup,
|
||||
]);
|
||||
|
||||
return { state, setState, view, setView, container, setContainer };
|
||||
}
|
||||
Reference in New Issue
Block a user