|
@@ -8,7 +8,7 @@ import nodePath from 'path';
|
|
|
|
|
|
|
|
import type { IPageHasId } from '@growi/core';
|
|
import type { IPageHasId } from '@growi/core';
|
|
|
import { pathUtils } from '@growi/core/dist/utils';
|
|
import { pathUtils } from '@growi/core/dist/utils';
|
|
|
-import { CodeMirrorEditorContainer, useCodeMirrorEditor } from '@growi/editor';
|
|
|
|
|
|
|
+import { CodeMirrorEditorContainer, useCodeMirrorEditorMain } from '@growi/editor';
|
|
|
import detectIndent from 'detect-indent';
|
|
import detectIndent from 'detect-indent';
|
|
|
import { useTranslation } from 'next-i18next';
|
|
import { useTranslation } from 'next-i18next';
|
|
|
import { useRouter } from 'next/router';
|
|
import { useRouter } from 'next/router';
|
|
@@ -85,7 +85,6 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
const { t } = useTranslation();
|
|
const { t } = useTranslation();
|
|
|
const router = useRouter();
|
|
const router = useRouter();
|
|
|
|
|
|
|
|
- const editorRef = useRef<IEditorMethods>(null);
|
|
|
|
|
const previewRef = useRef<HTMLDivElement>(null);
|
|
const previewRef = useRef<HTMLDivElement>(null);
|
|
|
const codeMirrorEditorContainerRef = useRef<HTMLDivElement>(null);
|
|
const codeMirrorEditorContainerRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
|
|
@@ -116,14 +115,8 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
const { mutate: mutateRemoteRevisionLastUpdatedAt } = useRemoteRevisionLastUpdatedAt();
|
|
const { mutate: mutateRemoteRevisionLastUpdatedAt } = useRemoteRevisionLastUpdatedAt();
|
|
|
const { mutate: mutateRemoteRevisionLastUpdateUser } = useRemoteRevisionLastUpdateUser();
|
|
const { mutate: mutateRemoteRevisionLastUpdateUser } = useRemoteRevisionLastUpdateUser();
|
|
|
|
|
|
|
|
- const { setContainer } = useCodeMirrorEditor({
|
|
|
|
|
- container: codeMirrorEditorContainerRef.current,
|
|
|
|
|
- });
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- if (codeMirrorEditorContainerRef.current != null) {
|
|
|
|
|
- setContainer(codeMirrorEditorContainerRef.current);
|
|
|
|
|
- }
|
|
|
|
|
- }, [setContainer]);
|
|
|
|
|
|
|
+ const { data: codemirrorEditor } = useCodeMirrorEditorMain(codeMirrorEditorContainerRef.current);
|
|
|
|
|
+ const { initDoc } = codemirrorEditor ?? {};
|
|
|
|
|
|
|
|
const { data: rendererOptions } = usePreviewOptions();
|
|
const { data: rendererOptions } = usePreviewOptions();
|
|
|
const { mutate: mutateIsEnabledUnsavedWarning } = useIsEnabledUnsavedWarning();
|
|
const { mutate: mutateIsEnabledUnsavedWarning } = useIsEnabledUnsavedWarning();
|
|
@@ -156,7 +149,6 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
|
|
|
|
|
}, [isNotFound, currentPathname, editingMarkdown, isEnabledAttachTitleHeader, templateBodyData]);
|
|
}, [isNotFound, currentPathname, editingMarkdown, isEnabledAttachTitleHeader, templateBodyData]);
|
|
|
|
|
|
|
|
- const markdownToSave = useRef<string>(initialValue);
|
|
|
|
|
const [markdownToPreview, setMarkdownToPreview] = useState<string>(initialValue);
|
|
const [markdownToPreview, setMarkdownToPreview] = useState<string>(initialValue);
|
|
|
|
|
|
|
|
const { data: socket } = useGlobalSocket();
|
|
const { data: socket } = useGlobalSocket();
|
|
@@ -179,11 +171,6 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
setCreatedPageRevisionIdWithAttachment(undefined);
|
|
setCreatedPageRevisionIdWithAttachment(undefined);
|
|
|
}, [router]);
|
|
}, [router]);
|
|
|
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- markdownToSave.current = initialValue;
|
|
|
|
|
- setMarkdownToPreview(initialValue);
|
|
|
|
|
- }, [initialValue]);
|
|
|
|
|
-
|
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
if (socket == null) { return }
|
|
if (socket == null) { return }
|
|
|
|
|
|
|
@@ -211,7 +198,6 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
}, [grantData, isSlackEnabled, pageTags]);
|
|
}, [grantData, isSlackEnabled, pageTags]);
|
|
|
|
|
|
|
|
const setMarkdownWithDebounce = useMemo(() => debounce(100, throttle(150, (value: string, isClean: boolean) => {
|
|
const setMarkdownWithDebounce = useMemo(() => debounce(100, throttle(150, (value: string, isClean: boolean) => {
|
|
|
- markdownToSave.current = value;
|
|
|
|
|
setMarkdownToPreview(value);
|
|
setMarkdownToPreview(value);
|
|
|
|
|
|
|
|
// Displays an unsaved warning alert
|
|
// Displays an unsaved warning alert
|
|
@@ -235,7 +221,10 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
mutateWaitingSaveProcessing(true);
|
|
mutateWaitingSaveProcessing(true);
|
|
|
|
|
|
|
|
const { page } = await saveOrUpdate(
|
|
const { page } = await saveOrUpdate(
|
|
|
- markdownToSave.current,
|
|
|
|
|
|
|
+ // TODO: get contents from the custom hook
|
|
|
|
|
+ // refs: https://redmine.weseek.co.jp/issues/128973
|
|
|
|
|
+ // markdownToSave.current,
|
|
|
|
|
+ '',
|
|
|
{ pageId, path: currentPagePath || currentPathname, revisionId: currentRevisionId },
|
|
{ pageId, path: currentPagePath || currentPathname, revisionId: currentRevisionId },
|
|
|
options,
|
|
options,
|
|
|
);
|
|
);
|
|
@@ -312,9 +301,11 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
* @param {any} file
|
|
* @param {any} file
|
|
|
*/
|
|
*/
|
|
|
const uploadHandler = useCallback(async(file) => {
|
|
const uploadHandler = useCallback(async(file) => {
|
|
|
- if (editorRef.current == null) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // TODO: implement
|
|
|
|
|
+ // refs: https://redmine.weseek.co.jp/issues/126528
|
|
|
|
|
+ // if (editorRef.current == null) {
|
|
|
|
|
+ // return;
|
|
|
|
|
+ // }
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -335,9 +326,11 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
if (pageId != null) {
|
|
if (pageId != null) {
|
|
|
formData.append('page_id', pageId);
|
|
formData.append('page_id', pageId);
|
|
|
}
|
|
}
|
|
|
- if (pageId == null && markdownToSave.current != null) {
|
|
|
|
|
- formData.append('page_body', markdownToSave.current);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // TODO: get contents from the custom hook
|
|
|
|
|
+ // refs: https://redmine.weseek.co.jp/issues/128973
|
|
|
|
|
+ // if (pageId == null && markdownToSave.current != null) {
|
|
|
|
|
+ // formData.append('page_body', markdownToSave.current);
|
|
|
|
|
+ // }
|
|
|
|
|
|
|
|
res = await apiPostForm('/attachments.add', formData);
|
|
res = await apiPostForm('/attachments.add', formData);
|
|
|
const attachment = res.attachment;
|
|
const attachment = res.attachment;
|
|
@@ -349,7 +342,9 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
// modify to "" syntax
|
|
// modify to "" syntax
|
|
|
insertText = `!${insertText}`;
|
|
insertText = `!${insertText}`;
|
|
|
}
|
|
}
|
|
|
- editorRef.current.insertText(insertText);
|
|
|
|
|
|
|
+ // TODO: implement
|
|
|
|
|
+ // refs: https://redmine.weseek.co.jp/issues/126528
|
|
|
|
|
+ // editorRef.current.insertText(insertText);
|
|
|
|
|
|
|
|
// when if created newly
|
|
// when if created newly
|
|
|
// Not using 'mutateGrant' to inherit the grant of the parent page
|
|
// Not using 'mutateGrant' to inherit the grant of the parent page
|
|
@@ -367,7 +362,9 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
toastError(e);
|
|
toastError(e);
|
|
|
}
|
|
}
|
|
|
finally {
|
|
finally {
|
|
|
- editorRef.current.terminateUploadingState();
|
|
|
|
|
|
|
+ // TODO: implement
|
|
|
|
|
+ // refs: https://redmine.weseek.co.jp/issues/126528
|
|
|
|
|
+ // editorRef.current.terminateUploadingState();
|
|
|
}
|
|
}
|
|
|
}, [currentPagePath, mutateCurrentPage, mutateCurrentPageId, mutateIsLatestRevision, pageId]);
|
|
}, [currentPagePath, mutateCurrentPage, mutateCurrentPageId, mutateIsLatestRevision, pageId]);
|
|
|
|
|
|
|
@@ -445,24 +442,24 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
* scroll Editor component by scroll event of Preview component
|
|
* scroll Editor component by scroll event of Preview component
|
|
|
* @param {number} offset
|
|
* @param {number} offset
|
|
|
*/
|
|
*/
|
|
|
- const scrollEditorByPreviewScroll = useCallback((offset: number) => {
|
|
|
|
|
- if (editorRef.current == null || previewRef.current == null) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // const scrollEditorByPreviewScroll = useCallback((offset: number) => {
|
|
|
|
|
+ // if (editorRef.current == null || previewRef.current == null) {
|
|
|
|
|
+ // return;
|
|
|
|
|
+ // }
|
|
|
|
|
|
|
|
- // prevent circular invocation
|
|
|
|
|
- if (isOriginOfScrollSyncEditor) {
|
|
|
|
|
- isOriginOfScrollSyncEditor = false; // turn off the flag
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // // prevent circular invocation
|
|
|
|
|
+ // if (isOriginOfScrollSyncEditor) {
|
|
|
|
|
+ // isOriginOfScrollSyncEditor = false; // turn off the flag
|
|
|
|
|
+ // return;
|
|
|
|
|
+ // }
|
|
|
|
|
|
|
|
- // turn on the flag
|
|
|
|
|
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
|
|
- isOriginOfScrollSyncPreview = true;
|
|
|
|
|
|
|
+ // // turn on the flag
|
|
|
|
|
+ // // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
|
|
+ // isOriginOfScrollSyncPreview = true;
|
|
|
|
|
|
|
|
- scrollSyncHelper.scrollEditor(editorRef.current, previewRef.current, offset);
|
|
|
|
|
- }, []);
|
|
|
|
|
- const scrollEditorByPreviewScrollWithThrottle = useMemo(() => throttle(20, scrollEditorByPreviewScroll), [scrollEditorByPreviewScroll]);
|
|
|
|
|
|
|
+ // scrollSyncHelper.scrollEditor(editorRef.current, previewRef.current, offset);
|
|
|
|
|
+ // }, []);
|
|
|
|
|
+ // const scrollEditorByPreviewScrollWithThrottle = useMemo(() => throttle(20, scrollEditorByPreviewScroll), [scrollEditorByPreviewScroll]);
|
|
|
|
|
|
|
|
const afterResolvedHandler = useCallback(async() => {
|
|
const afterResolvedHandler = useCallback(async() => {
|
|
|
// get page data from db
|
|
// get page data from db
|
|
@@ -487,24 +484,29 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
if (initialValue == null) {
|
|
if (initialValue == null) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- markdownToSave.current = initialValue;
|
|
|
|
|
|
|
+ // markdownToSave.current = initialValue;
|
|
|
|
|
+ initDoc?.(initialValue);
|
|
|
setMarkdownToPreview(initialValue);
|
|
setMarkdownToPreview(initialValue);
|
|
|
mutateIsEnabledUnsavedWarning(false);
|
|
mutateIsEnabledUnsavedWarning(false);
|
|
|
- }, [initialValue, mutateIsEnabledUnsavedWarning]);
|
|
|
|
|
|
|
+ }, [initDoc, initialValue, mutateIsEnabledUnsavedWarning]);
|
|
|
|
|
|
|
|
// initial caret line
|
|
// initial caret line
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
- if (editorRef.current != null) {
|
|
|
|
|
- editorRef.current.setCaretLine(0);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // TODO: implement
|
|
|
|
|
+ // refs: https://redmine.weseek.co.jp/issues/126516
|
|
|
|
|
+ // if (editorRef.current != null) {
|
|
|
|
|
+ // editorRef.current.setCaretLine(0);
|
|
|
|
|
+ // }
|
|
|
}, []);
|
|
}, []);
|
|
|
|
|
|
|
|
// set handler to set caret line
|
|
// set handler to set caret line
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
const handler = (line) => {
|
|
const handler = (line) => {
|
|
|
- if (editorRef.current != null) {
|
|
|
|
|
- editorRef.current.setCaretLine(line);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // TODO: implement
|
|
|
|
|
+ // refs: https://redmine.weseek.co.jp/issues/126516
|
|
|
|
|
+ // if (editorRef.current != null) {
|
|
|
|
|
+ // editorRef.current.setCaretLine(line);
|
|
|
|
|
+ // }
|
|
|
if (previewRef.current != null) {
|
|
if (previewRef.current != null) {
|
|
|
scrollSyncHelper.scrollPreview(previewRef.current, line);
|
|
scrollSyncHelper.scrollPreview(previewRef.current, line);
|
|
|
}
|
|
}
|
|
@@ -527,9 +529,11 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
|
|
|
|
|
// set handler to focus
|
|
// set handler to focus
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
- if (editorRef.current != null && editorMode === EditorMode.Editor) {
|
|
|
|
|
- editorRef.current.forceToFocus();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // TODO: implement
|
|
|
|
|
+ // refs: https://redmine.weseek.co.jp/issues/126516
|
|
|
|
|
+ // if (editorRef.current != null && editorMode === EditorMode.Editor) {
|
|
|
|
|
+ // editorRef.current.forceToFocus();
|
|
|
|
|
+ // }
|
|
|
}, [editorMode]);
|
|
}, [editorMode]);
|
|
|
|
|
|
|
|
// Detect indent size from contents (only when users are allowed to change it)
|
|
// Detect indent size from contents (only when users are allowed to change it)
|
|
@@ -551,9 +555,12 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
// when transitioning to a different page, if the initialValue is the same,
|
|
// when transitioning to a different page, if the initialValue is the same,
|
|
|
// UnControlled CodeMirror value does not reset, so explicitly set the value to initialValue
|
|
// UnControlled CodeMirror value does not reset, so explicitly set the value to initialValue
|
|
|
const onRouterChangeComplete = useCallback(() => {
|
|
const onRouterChangeComplete = useCallback(() => {
|
|
|
- editorRef.current?.setValue(initialValue);
|
|
|
|
|
- editorRef.current?.setCaretLine(0);
|
|
|
|
|
- }, [initialValue]);
|
|
|
|
|
|
|
+ initDoc?.(initialValue);
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: implement
|
|
|
|
|
+ // refs: https://redmine.weseek.co.jp/issues/126516
|
|
|
|
|
+ // editorRef.current?.setCaretLine(0);
|
|
|
|
|
+ }, [initDoc, initialValue]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
router.events.on('routeChangeComplete', onRouterChangeComplete);
|
|
router.events.on('routeChangeComplete', onRouterChangeComplete);
|
|
@@ -573,7 +580,7 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
const isUploadable = isUploadableImage || isUploadableFile;
|
|
const isUploadable = isUploadableImage || isUploadableFile;
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
- <div data-testid="page-editor" id="page-editor" className={`flex-grow-1 d-flex overflow-y-auto ${props.visibility ? '' : 'd-none'}`}>
|
|
|
|
|
|
|
+ <div data-testid="page-editor" id="page-editor" className={`flex-expand-horiz ${props.visibility ? '' : 'd-none'}`}>
|
|
|
<div className="page-editor-editor-container flex-expand-vert">
|
|
<div className="page-editor-editor-container flex-expand-vert">
|
|
|
{/* <Editor
|
|
{/* <Editor
|
|
|
ref={editorRef}
|
|
ref={editorRef}
|
|
@@ -595,7 +602,9 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
rendererOptions={rendererOptions}
|
|
rendererOptions={rendererOptions}
|
|
|
markdown={markdownToPreview}
|
|
markdown={markdownToPreview}
|
|
|
pagePath={currentPagePath}
|
|
pagePath={currentPagePath}
|
|
|
- onScroll={offset => scrollEditorByPreviewScrollWithThrottle(offset)}
|
|
|
|
|
|
|
+ // TODO: implement
|
|
|
|
|
+ // refs: https://redmine.weseek.co.jp/issues/126519
|
|
|
|
|
+ // onScroll={offset => scrollEditorByPreviewScrollWithThrottle(offset)}
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
|
{/*
|
|
{/*
|