Shun Miyazawa 2 лет назад
Родитель
Сommit
f05bc326df

+ 29 - 29
apps/app/src/components/PageEditor/PageEditor.tsx

@@ -51,7 +51,7 @@ import loggerFactory from '~/utils/logger';
 import { EditorNavbar } from './EditorNavbar';
 import EditorNavbarBottom from './EditorNavbarBottom';
 import Preview from './Preview';
-import { scrollEditor, scrollPreview } from './ScrollSyncHelper';
+import { useScrollSync } from './ScrollSyncHelper';
 import { useConflictResolver, useConflictEffect, type ConflictHandler } from './conflict';
 
 import '@growi/editor/dist/style.css';
@@ -65,10 +65,6 @@ declare global {
   var globalEmitter: EventEmitter;
 }
 
-// for scrolling
-let isOriginOfScrollSyncEditor = false;
-let isOriginOfScrollSyncPreview = false;
-
 export type SaveOptions = {
   slackChannels: string,
   overwriteScopesOfDescendants?: boolean
@@ -163,6 +159,10 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
 
   const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(GlobalCodeMirrorEditorKey.MAIN);
 
+  const { scrollEditorHandler, scrollPreviewHandler } = useScrollSync(GlobalCodeMirrorEditorKey.MAIN, previewRef);
+  const scrollEditorHandlerThrottle = useMemo(() => throttle(25, scrollEditorHandler), [scrollEditorHandler]);
+  const scrollPreviewHandlerThrottle = useMemo(() => throttle(25, scrollPreviewHandler), [scrollPreviewHandler]);
+
   const save: Save = useCallback(async(revisionId, markdown, opts, onConflict) => {
     if (pageId == null || grantData == null) {
       logger.error('Some materials to save are invalid', {
@@ -272,37 +272,37 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
 
   }, [codeMirrorEditor, pageId]);
 
-  const scrollEditorHandler = useCallback(() => {
-    if (codeMirrorEditor?.view?.scrollDOM == null || previewRef.current == null) {
-      return;
-    }
+  // const scrollEditorHandler = useCallback(() => {
+  //   if (codeMirrorEditor?.view?.scrollDOM == null || previewRef.current == null) {
+  //     return;
+  //   }
 
-    if (isOriginOfScrollSyncPreview) {
-      isOriginOfScrollSyncPreview = false;
-      return;
-    }
+  //   if (isOriginOfScrollSyncPreview) {
+  //     isOriginOfScrollSyncPreview = false;
+  //     return;
+  //   }
 
-    isOriginOfScrollSyncEditor = true;
-    scrollEditor(codeMirrorEditor.view.scrollDOM, previewRef.current);
-  }, [codeMirrorEditor]);
+  //   isOriginOfScrollSyncEditor = true;
+  //   scrollEditor(codeMirrorEditor.view.scrollDOM, previewRef.current);
+  // }, [codeMirrorEditor]);
 
-  const scrollEditorHandlerThrottle = useMemo(() => throttle(25, scrollEditorHandler), [scrollEditorHandler]);
+  // const scrollEditorHandlerThrottle = useMemo(() => throttle(25, scrollEditorHandler), [scrollEditorHandler]);
 
-  const scrollPreviewHandler = useCallback(() => {
-    if (codeMirrorEditor?.view?.scrollDOM == null || previewRef.current == null) {
-      return;
-    }
+  // const scrollPreviewHandler = useCallback(() => {
+  //   if (codeMirrorEditor?.view?.scrollDOM == null || previewRef.current == null) {
+  //     return;
+  //   }
 
-    if (isOriginOfScrollSyncEditor) {
-      isOriginOfScrollSyncEditor = false;
-      return;
-    }
+  //   if (isOriginOfScrollSyncEditor) {
+  //     isOriginOfScrollSyncEditor = false;
+  //     return;
+  //   }
 
-    isOriginOfScrollSyncPreview = true;
-    scrollPreview(codeMirrorEditor.view.scrollDOM, previewRef.current);
-  }, [codeMirrorEditor]);
+  //   isOriginOfScrollSyncPreview = true;
+  //   scrollPreview(codeMirrorEditor.view.scrollDOM, previewRef.current);
+  // }, [codeMirrorEditor]);
 
-  const scrollPreviewHandlerThrottle = useMemo(() => throttle(25, scrollPreviewHandler), [scrollPreviewHandler]);
+  // const scrollPreviewHandlerThrottle = useMemo(() => throttle(25, scrollPreviewHandler), [scrollPreviewHandler]);
 
   // initial caret line
   useEffect(() => {

+ 7 - 41
apps/app/src/components/PageEditor/PageEditorReadOnly.tsx

@@ -1,8 +1,6 @@
-import react, {
-  useCallback, useMemo, useRef, type CSSProperties,
-} from 'react';
+import react, { useMemo, useRef, type CSSProperties } from 'react';
 
-import { CodeMirrorEditorReadOnly, GlobalCodeMirrorEditorKey, useCodeMirrorEditorIsolated } from '@growi/editor';
+import { CodeMirrorEditorReadOnly, GlobalCodeMirrorEditorKey } from '@growi/editor';
 import { useRect } from '@growi/ui/dist/utils';
 import { throttle } from 'throttle-debounce';
 
@@ -14,20 +12,20 @@ import { usePreviewOptions } from '~/stores/renderer';
 
 import { EditorNavbar } from './EditorNavbar';
 import Preview from './Preview';
-import { scrollEditor, scrollPreview } from './ScrollSyncHelper';
-
-let isOriginOfScrollSyncEditor = false;
-let isOriginOfScrollSyncPreview = false;
+import { useScrollSync } from './ScrollSyncHelper';
 
 export const PageEditorReadOnly = react.memo((): JSX.Element => {
   const previewRef = useRef<HTMLDivElement>(null);
   const [previewRect] = useRect(previewRef);
 
+  const { scrollEditorHandler, scrollPreviewHandler } = useScrollSync(GlobalCodeMirrorEditorKey.MAIN, previewRef);
+  const scrollEditorHandlerThrottle = useMemo(() => throttle(25, scrollEditorHandler), [scrollEditorHandler]);
+  const scrollPreviewHandlerThrottle = useMemo(() => throttle(25, scrollPreviewHandler), [scrollPreviewHandler]);
+
   const { data: currentPage } = useSWRxCurrentPage();
   const { data: rendererOptions } = usePreviewOptions();
   const { data: editingMarkdown } = useEditingMarkdown();
   const { data: isOldRevisionPage } = useIsOldRevisionPage();
-  const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(GlobalCodeMirrorEditorKey.MAIN);
 
   const shouldExpandContent = useShouldExpandContent(currentPage);
 
@@ -42,38 +40,6 @@ export const PageEditorReadOnly = react.memo((): JSX.Element => {
     return { paddingBottom: `calc(${previewRectHeight}px - 2em)` };
   }, [previewRect]);
 
-  const scrollEditorHandler = useCallback(() => {
-    if (codeMirrorEditor?.view?.scrollDOM == null || previewRef.current == null) {
-      return;
-    }
-
-    if (isOriginOfScrollSyncPreview) {
-      isOriginOfScrollSyncPreview = false;
-      return;
-    }
-
-    isOriginOfScrollSyncEditor = true;
-    scrollEditor(codeMirrorEditor.view.scrollDOM, previewRef.current);
-  }, [codeMirrorEditor]);
-
-  const scrollEditorHandlerThrottle = useMemo(() => throttle(25, scrollEditorHandler), [scrollEditorHandler]);
-
-  const scrollPreviewHandler = useCallback(() => {
-    if (codeMirrorEditor?.view?.scrollDOM == null || previewRef.current == null) {
-      return;
-    }
-
-    if (isOriginOfScrollSyncEditor) {
-      isOriginOfScrollSyncEditor = false;
-      return;
-    }
-
-    isOriginOfScrollSyncPreview = true;
-    scrollPreview(codeMirrorEditor.view.scrollDOM, previewRef.current);
-  }, [codeMirrorEditor]);
-
-  const scrollPreviewHandlerThrottle = useMemo(() => throttle(25, scrollPreviewHandler), [scrollPreviewHandler]);
-
   if (rendererOptions == null || !isOldRevisionPage) {
     return <></>;
   }

+ 45 - 2
apps/app/src/components/PageEditor/ScrollSyncHelper.tsx

@@ -1,3 +1,7 @@
+import { useCallback, type RefObject, useRef } from 'react';
+
+import { useCodeMirrorEditorIsolated, type GlobalCodeMirrorEditorKey } from '@growi/editor';
+
 let defaultTop = 0;
 const padding = 5;
 
@@ -88,7 +92,7 @@ const calcScorllElementByRatio = (sourceElement: SourceElement, targetElement: T
 };
 
 
-export const scrollEditor = (editorRootElement: HTMLElement, previewRootElement: HTMLElement): void => {
+const scrollEditor = (editorRootElement: HTMLElement, previewRootElement: HTMLElement): void => {
 
   setDefaultTop(editorRootElement.getBoundingClientRect().top);
 
@@ -120,7 +124,7 @@ export const scrollEditor = (editorRootElement: HTMLElement, previewRootElement:
 
 };
 
-export const scrollPreview = (editorRootElement: HTMLElement, previewRootElement: HTMLElement): void => {
+const scrollPreview = (editorRootElement: HTMLElement, previewRootElement: HTMLElement): void => {
 
   setDefaultTop(previewRootElement.getBoundingClientRect().y);
 
@@ -150,3 +154,42 @@ export const scrollPreview = (editorRootElement: HTMLElement, previewRootElement
   editorRootElement.scrollTop = newScrollTop;
 
 };
+
+// eslint-disable-next-line max-len
+export const useScrollSync = (codeMirrorKey: GlobalCodeMirrorEditorKey, previewRef: RefObject<HTMLDivElement>): { scrollEditorHandler: () => void; scrollPreviewHandler: () => void } => {
+  const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(codeMirrorKey);
+
+  const isOriginOfScrollSyncEditor = useRef(false);
+  const isOriginOfScrollSyncPreview = useRef(false);
+
+  const scrollEditorHandler = useCallback(() => {
+    if (codeMirrorEditor?.view?.scrollDOM == null || previewRef.current == null) {
+      return;
+    }
+
+    if (isOriginOfScrollSyncPreview.current) {
+      isOriginOfScrollSyncPreview.current = false;
+      return;
+    }
+
+    isOriginOfScrollSyncEditor.current = true;
+    scrollEditor(codeMirrorEditor.view.scrollDOM, previewRef.current);
+  }, [codeMirrorEditor, isOriginOfScrollSyncPreview, previewRef]);
+
+  const scrollPreviewHandler = useCallback(() => {
+    if (codeMirrorEditor?.view?.scrollDOM == null || previewRef.current == null) {
+      return;
+    }
+
+    if (isOriginOfScrollSyncEditor.current) {
+      // setIsOriginOfScrollSyncEditor(false);
+      isOriginOfScrollSyncEditor.current = false;
+      return;
+    }
+
+    isOriginOfScrollSyncPreview.current = true;
+    scrollPreview(codeMirrorEditor.view.scrollDOM, previewRef.current);
+  }, [codeMirrorEditor, isOriginOfScrollSyncEditor, previewRef]);
+
+  return { scrollEditorHandler, scrollPreviewHandler };
+};