|
@@ -1,64 +1,68 @@
|
|
|
import type { DetailedHTMLProps, JSX } from 'react';
|
|
import type { DetailedHTMLProps, JSX } from 'react';
|
|
|
-import {
|
|
|
|
|
- forwardRef, useMemo, useRef, useEffect,
|
|
|
|
|
-} from 'react';
|
|
|
|
|
-
|
|
|
|
|
|
|
+import { forwardRef, useEffect, useMemo, useRef } from 'react';
|
|
|
import { indentUnit } from '@codemirror/language';
|
|
import { indentUnit } from '@codemirror/language';
|
|
|
-import {
|
|
|
|
|
- EditorView,
|
|
|
|
|
-} from '@codemirror/view';
|
|
|
|
|
|
|
+import { EditorView } from '@codemirror/view';
|
|
|
import { AcceptedUploadFileType } from '@growi/core';
|
|
import { AcceptedUploadFileType } from '@growi/core';
|
|
|
import type { ReactCodeMirrorProps } from '@uiw/react-codemirror';
|
|
import type { ReactCodeMirrorProps } from '@uiw/react-codemirror';
|
|
|
|
|
|
|
|
-import { PasteMode, type EditorSettings, type GlobalCodeMirrorEditorKey } from '../../../consts';
|
|
|
|
|
import {
|
|
import {
|
|
|
- useFileDropzone, FileDropzoneOverlay, useShowTableIcon, getStrFromBol, adjustPasteData,
|
|
|
|
|
|
|
+ type EditorSettings,
|
|
|
|
|
+ type GlobalCodeMirrorEditorKey,
|
|
|
|
|
+ PasteMode,
|
|
|
|
|
+} from '../../../consts';
|
|
|
|
|
+import {
|
|
|
|
|
+ adjustPasteData,
|
|
|
|
|
+ FileDropzoneOverlay,
|
|
|
|
|
+ getStrFromBol,
|
|
|
|
|
+ useFileDropzone,
|
|
|
|
|
+ useShowTableIcon,
|
|
|
} from '../../services-internal';
|
|
} from '../../services-internal';
|
|
|
import { useCodeMirrorEditorIsolated } from '../../stores/codemirror-editor';
|
|
import { useCodeMirrorEditorIsolated } from '../../stores/codemirror-editor';
|
|
|
import { useDefaultExtensions } from '../../stores/use-default-extensions';
|
|
import { useDefaultExtensions } from '../../stores/use-default-extensions';
|
|
|
import { useEditorSettings } from '../../stores/use-editor-settings';
|
|
import { useEditorSettings } from '../../stores/use-editor-settings';
|
|
|
-
|
|
|
|
|
import { Toolbar } from './Toolbar';
|
|
import { Toolbar } from './Toolbar';
|
|
|
|
|
|
|
|
-
|
|
|
|
|
import style from './CodeMirrorEditor.module.scss';
|
|
import style from './CodeMirrorEditor.module.scss';
|
|
|
|
|
|
|
|
const moduleClass = style['codemirror-editor'];
|
|
const moduleClass = style['codemirror-editor'];
|
|
|
|
|
|
|
|
-
|
|
|
|
|
// Fix IME cursor position issue by EditContext
|
|
// Fix IME cursor position issue by EditContext
|
|
|
// ref: https://github.com/growilabs/growi/pull/9267
|
|
// ref: https://github.com/growilabs/growi/pull/9267
|
|
|
// ref: https://discuss.codemirror.net/t/issue-with-google-japanese-ime-cursor-position-in-v6/8810/3
|
|
// ref: https://discuss.codemirror.net/t/issue-with-google-japanese-ime-cursor-position-in-v6/8810/3
|
|
|
(EditorView as unknown as { EDIT_CONTEXT: boolean }).EDIT_CONTEXT = false;
|
|
(EditorView as unknown as { EDIT_CONTEXT: boolean }).EDIT_CONTEXT = false;
|
|
|
|
|
|
|
|
-
|
|
|
|
|
-const CodeMirrorEditorContainer = forwardRef<HTMLDivElement, DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>>(
|
|
|
|
|
- (props, ref) => {
|
|
|
|
|
- const { className = '', ...rest } = props;
|
|
|
|
|
- return (
|
|
|
|
|
- <div className={`${className} flex-expand-vert ${style['codemirror-editor-container']}`} ref={ref} {...rest} />
|
|
|
|
|
- );
|
|
|
|
|
- },
|
|
|
|
|
-);
|
|
|
|
|
|
|
+const CodeMirrorEditorContainer = forwardRef<
|
|
|
|
|
+ HTMLDivElement,
|
|
|
|
|
+ DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
|
|
|
|
|
+>((props, ref) => {
|
|
|
|
|
+ const { className = '', ...rest } = props;
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div
|
|
|
|
|
+ className={`${className} flex-expand-vert ${style['codemirror-editor-container']}`}
|
|
|
|
|
+ ref={ref}
|
|
|
|
|
+ {...rest}
|
|
|
|
|
+ />
|
|
|
|
|
+ );
|
|
|
|
|
+});
|
|
|
|
|
|
|
|
export type CodeMirrorEditorProps = {
|
|
export type CodeMirrorEditorProps = {
|
|
|
/**
|
|
/**
|
|
|
* Specity the props for the react-codemirror component. **This must be a memolized object.**
|
|
* Specity the props for the react-codemirror component. **This must be a memolized object.**
|
|
|
*/
|
|
*/
|
|
|
- cmProps?: ReactCodeMirrorProps,
|
|
|
|
|
- acceptedUploadFileType?: AcceptedUploadFileType,
|
|
|
|
|
- indentSize?: number,
|
|
|
|
|
- editorSettings?: EditorSettings,
|
|
|
|
|
- onSave?: () => void,
|
|
|
|
|
- onUpload?: (files: File[]) => void,
|
|
|
|
|
- onScroll?: () => void,
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ cmProps?: ReactCodeMirrorProps;
|
|
|
|
|
+ acceptedUploadFileType?: AcceptedUploadFileType;
|
|
|
|
|
+ indentSize?: number;
|
|
|
|
|
+ editorSettings?: EditorSettings;
|
|
|
|
|
+ onSave?: () => void;
|
|
|
|
|
+ onUpload?: (files: File[]) => void;
|
|
|
|
|
+ onScroll?: () => void;
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
type Props = CodeMirrorEditorProps & {
|
|
type Props = CodeMirrorEditorProps & {
|
|
|
- editorKey: string | GlobalCodeMirrorEditorKey,
|
|
|
|
|
- className?: string,
|
|
|
|
|
- hideToolbar?: boolean,
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ editorKey: string | GlobalCodeMirrorEditorKey;
|
|
|
|
|
+ className?: string;
|
|
|
|
|
+ hideToolbar?: boolean;
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
export const CodeMirrorEditor = (props: Props): JSX.Element => {
|
|
export const CodeMirrorEditor = (props: Props): JSX.Element => {
|
|
|
const {
|
|
const {
|
|
@@ -77,7 +81,11 @@ export const CodeMirrorEditor = (props: Props): JSX.Element => {
|
|
|
|
|
|
|
|
const containerRef = useRef(null);
|
|
const containerRef = useRef(null);
|
|
|
|
|
|
|
|
- const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(editorKey, containerRef.current, cmProps);
|
|
|
|
|
|
|
+ const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(
|
|
|
|
|
+ editorKey,
|
|
|
|
|
+ containerRef.current,
|
|
|
|
|
+ cmProps,
|
|
|
|
|
+ );
|
|
|
|
|
|
|
|
useDefaultExtensions(codeMirrorEditor);
|
|
useDefaultExtensions(codeMirrorEditor);
|
|
|
useEditorSettings(codeMirrorEditor, editorSettings, onSave);
|
|
useEditorSettings(codeMirrorEditor, editorSettings, onSave);
|
|
@@ -92,7 +100,6 @@ export const CodeMirrorEditor = (props: Props): JSX.Element => {
|
|
|
|
|
|
|
|
const cleanupFunction = codeMirrorEditor?.appendExtensions?.(extension);
|
|
const cleanupFunction = codeMirrorEditor?.appendExtensions?.(extension);
|
|
|
return cleanupFunction;
|
|
return cleanupFunction;
|
|
|
-
|
|
|
|
|
}, [codeMirrorEditor, indentSize]);
|
|
}, [codeMirrorEditor, indentSize]);
|
|
|
|
|
|
|
|
const pasteMode = editorSettings?.pasteMode;
|
|
const pasteMode = editorSettings?.pasteMode;
|
|
@@ -109,7 +116,11 @@ export const CodeMirrorEditor = (props: Props): JSX.Element => {
|
|
|
if (event.clipboardData.types.includes('text/plain')) {
|
|
if (event.clipboardData.types.includes('text/plain')) {
|
|
|
if (codeMirrorEditor == null) return;
|
|
if (codeMirrorEditor == null) return;
|
|
|
|
|
|
|
|
- if (pasteMode == null || pasteMode === PasteMode.both || pasteMode === PasteMode.text) {
|
|
|
|
|
|
|
+ if (
|
|
|
|
|
+ pasteMode == null ||
|
|
|
|
|
+ pasteMode === PasteMode.both ||
|
|
|
|
|
+ pasteMode === PasteMode.text
|
|
|
|
|
+ ) {
|
|
|
const textData = event.clipboardData.getData('text/plain');
|
|
const textData = event.clipboardData.getData('text/plain');
|
|
|
|
|
|
|
|
const strFromBol = getStrFromBol(editor);
|
|
const strFromBol = getStrFromBol(editor);
|
|
@@ -122,7 +133,11 @@ export const CodeMirrorEditor = (props: Props): JSX.Element => {
|
|
|
if (event.clipboardData.types.includes('Files')) {
|
|
if (event.clipboardData.types.includes('Files')) {
|
|
|
if (onUpload == null) return;
|
|
if (onUpload == null) return;
|
|
|
|
|
|
|
|
- if (pasteMode == null || pasteMode === PasteMode.both || pasteMode === PasteMode.file) {
|
|
|
|
|
|
|
+ if (
|
|
|
|
|
+ pasteMode == null ||
|
|
|
|
|
+ pasteMode === PasteMode.both ||
|
|
|
|
|
+ pasteMode === PasteMode.file
|
|
|
|
|
+ ) {
|
|
|
onUpload(Array.from(event.clipboardData.files));
|
|
onUpload(Array.from(event.clipboardData.files));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -134,11 +149,9 @@ export const CodeMirrorEditor = (props: Props): JSX.Element => {
|
|
|
|
|
|
|
|
const cleanupFunction = codeMirrorEditor?.appendExtensions(extension);
|
|
const cleanupFunction = codeMirrorEditor?.appendExtensions(extension);
|
|
|
return cleanupFunction;
|
|
return cleanupFunction;
|
|
|
-
|
|
|
|
|
}, [codeMirrorEditor, pasteMode, onUpload]);
|
|
}, [codeMirrorEditor, pasteMode, onUpload]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
-
|
|
|
|
|
const handleDrop = (event: DragEvent) => {
|
|
const handleDrop = (event: DragEvent) => {
|
|
|
// prevents conflicts between codemirror and react-dropzone during file drops.
|
|
// prevents conflicts between codemirror and react-dropzone during file drops.
|
|
|
event.preventDefault();
|
|
event.preventDefault();
|
|
@@ -150,11 +163,9 @@ export const CodeMirrorEditor = (props: Props): JSX.Element => {
|
|
|
|
|
|
|
|
const cleanupFunction = codeMirrorEditor?.appendExtensions(extension);
|
|
const cleanupFunction = codeMirrorEditor?.appendExtensions(extension);
|
|
|
return cleanupFunction;
|
|
return cleanupFunction;
|
|
|
-
|
|
|
|
|
}, [codeMirrorEditor]);
|
|
}, [codeMirrorEditor]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
-
|
|
|
|
|
const handleScroll = (event: Event) => {
|
|
const handleScroll = (event: Event) => {
|
|
|
event.preventDefault();
|
|
event.preventDefault();
|
|
|
if (onScroll != null) {
|
|
if (onScroll != null) {
|
|
@@ -168,7 +179,6 @@ export const CodeMirrorEditor = (props: Props): JSX.Element => {
|
|
|
|
|
|
|
|
const cleanupFunction = codeMirrorEditor?.appendExtensions(extension);
|
|
const cleanupFunction = codeMirrorEditor?.appendExtensions(extension);
|
|
|
return cleanupFunction;
|
|
return cleanupFunction;
|
|
|
-
|
|
|
|
|
}, [onScroll, codeMirrorEditor]);
|
|
}, [onScroll, codeMirrorEditor]);
|
|
|
|
|
|
|
|
const {
|
|
const {
|
|
@@ -189,7 +199,6 @@ export const CodeMirrorEditor = (props: Props): JSX.Element => {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const fileUploadState = useMemo(() => {
|
|
const fileUploadState = useMemo(() => {
|
|
|
-
|
|
|
|
|
if (isUploading) {
|
|
if (isUploading) {
|
|
|
return 'dropzone-uploading';
|
|
return 'dropzone-uploading';
|
|
|
}
|
|
}
|
|
@@ -221,8 +230,13 @@ export const CodeMirrorEditor = (props: Props): JSX.Element => {
|
|
|
}, [isUploading, isDragAccept, isDragReject, acceptedUploadFileType]);
|
|
}, [isUploading, isDragAccept, isDragReject, acceptedUploadFileType]);
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
- <div className={`${className} ${moduleClass} flex-expand-vert overflow-y-hidden`}>
|
|
|
|
|
- <div {...getRootProps()} className={`dropzone ${fileUploadState} flex-expand-vert`}>
|
|
|
|
|
|
|
+ <div
|
|
|
|
|
+ className={`${className} ${moduleClass} flex-expand-vert overflow-y-hidden`}
|
|
|
|
|
+ >
|
|
|
|
|
+ <div
|
|
|
|
|
+ {...getRootProps()}
|
|
|
|
|
+ className={`dropzone ${fileUploadState} flex-expand-vert`}
|
|
|
|
|
+ >
|
|
|
<input {...getInputProps()} />
|
|
<input {...getInputProps()} />
|
|
|
<FileDropzoneOverlay isEnabled={isDragActive} />
|
|
<FileDropzoneOverlay isEnabled={isDragActive} />
|
|
|
<CodeMirrorEditorContainer ref={containerRef} />
|
|
<CodeMirrorEditorContainer ref={containerRef} />
|