From 661cb737491b68228aa42f0d5b1c3eec472a306e Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Mon, 18 Dec 2023 13:23:39 +0530 Subject: [PATCH] chore: fix scroll sync in conflict resolution modal --- .../NoteConflictResolutionModal.tsx | 5 +- .../NoteView/ReadonlyNoteContent.tsx | 64 ++++++++++++++++--- .../Components/SuperEditor/BlocksEditor.tsx | 5 +- 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/packages/web/src/javascripts/Components/NoteView/NoteConflictResolutionModal/NoteConflictResolutionModal.tsx b/packages/web/src/javascripts/Components/NoteView/NoteConflictResolutionModal/NoteConflictResolutionModal.tsx index 4693e3eca..ba29188d2 100644 --- a/packages/web/src/javascripts/Components/NoteView/NoteConflictResolutionModal/NoteConflictResolutionModal.tsx +++ b/packages/web/src/javascripts/Components/NoteView/NoteConflictResolutionModal/NoteConflictResolutionModal.tsx @@ -161,6 +161,9 @@ const NoteConflictResolutionModal = ({ const [comparisonScrollPos, setComparisonScrollPos] = useState(0) const [shouldSyncComparisonScroll, setShouldSyncComparisonScroll] = useState(true) + const onScroll = useCallback(({ target }: { target: EventTarget | null }) => { + setComparisonScrollPos((target as HTMLElement).scrollTop) + }, []) return ( setComparisonScrollPos((event.target as HTMLElement).scrollTop)} + onScroll={onScroll} /> ))} diff --git a/packages/web/src/javascripts/Components/NoteView/ReadonlyNoteContent.tsx b/packages/web/src/javascripts/Components/NoteView/ReadonlyNoteContent.tsx index dd0d23bb2..0221f9e95 100644 --- a/packages/web/src/javascripts/Components/NoteView/ReadonlyNoteContent.tsx +++ b/packages/web/src/javascripts/Components/NoteView/ReadonlyNoteContent.tsx @@ -8,7 +8,7 @@ import { classNames, isUIFeatureAnIframeFeature, } from '@standardnotes/snjs' -import { CSSProperties, UIEventHandler, useEffect, useMemo, useRef } from 'react' +import { CSSProperties, useCallback, useEffect, useMemo, useRef } from 'react' import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery' import { useApplication } from '../ApplicationProvider' import IframeFeatureView from '../ComponentView/IframeFeatureView' @@ -19,6 +19,7 @@ import { useLinkingController } from '@/Controllers/LinkingControllerProvider' import LinkedItemBubblesContainer from '../LinkedItems/LinkedItemBubblesContainer' import usePreference from '@/Hooks/usePreference' import { useResponsiveEditorFontSize } from '@/Utils/getPlaintextFontSize' +import { getScrollParent } from '@/Utils' export const ReadonlyNoteContent = ({ note, @@ -33,7 +34,7 @@ export const ReadonlyNoteContent = ({ showLinkedItems?: boolean scrollPos?: number shouldSyncScroll?: boolean - onScroll?: UIEventHandler + onScroll?: ({ target }: { target: EventTarget | null }) => void }) => { const application = useApplication() const linkingController = useLinkingController() @@ -64,6 +65,27 @@ export const ReadonlyNoteContent = ({ }, [application, componentViewer]) const containerRef = useRef(null) + const scrollerRef = useRef() + + const setScroller = useCallback(() => { + if (scrollerRef.current) { + return + } + + scrollerRef.current = containerRef.current?.querySelector('textarea, .ContentEditable__root') + + if (!scrollerRef.current) { + return + } + + const isScrollerOverflowing = scrollerRef.current.scrollHeight > scrollerRef.current.clientHeight + if (!isScrollerOverflowing) { + const closestScrollParent = getScrollParent(scrollerRef.current) + if (closestScrollParent) { + scrollerRef.current = closestScrollParent + } + } + }, []) useEffect(() => { if (!shouldSyncScroll) { @@ -74,16 +96,41 @@ export const ReadonlyNoteContent = ({ return } - const scroller = containerRef.current.querySelector('textarea, .ContentEditable__root') - - if (!scroller) { - return + if (!scrollerRef.current) { + setScroller() } - scroller.scrollTo({ + scrollerRef.current?.scrollTo({ top: scrollPos, }) - }, [scrollPos, shouldSyncScroll]) + }, [scrollPos, setScroller, shouldSyncScroll]) + + useEffect( + function setupOnScrollForSuper() { + if (note.noteType !== NoteType.Super) { + return + } + + setScroller() + + const scroller = scrollerRef.current + + if (!scroller) { + return + } + + const scrollHandler = (event: Event) => { + onScroll?.(event) + } + + scroller.addEventListener('scroll', scrollHandler) + + return () => { + scroller.removeEventListener('scroll', scrollHandler) + } + }, + [note.noteType, onScroll, setScroller], + ) const lineHeight = usePreference(PrefKey.EditorLineHeight) const fontSize = usePreference(PrefKey.EditorFontSize) @@ -123,7 +170,6 @@ export const ReadonlyNoteContent = ({ readonly className="blocks-editor relative h-full resize-none p-4 text-base focus:shadow-none focus:outline-none" spellcheck={content.spellcheck} - onScroll={onScroll} > diff --git a/packages/web/src/javascripts/Components/SuperEditor/BlocksEditor.tsx b/packages/web/src/javascripts/Components/SuperEditor/BlocksEditor.tsx index bcb4f8c24..44e9d9d5a 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/BlocksEditor.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/BlocksEditor.tsx @@ -1,4 +1,4 @@ -import { FunctionComponent, UIEventHandler, useCallback, useState } from 'react' +import { FunctionComponent, useCallback, useState } from 'react' import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin' import { ContentEditable } from '@lexical/react/LexicalContentEditable' import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin' @@ -37,7 +37,6 @@ type BlocksEditorProps = { spellcheck?: boolean ignoreFirstChange?: boolean readonly?: boolean - onScroll?: UIEventHandler } export const BlocksEditor: FunctionComponent = ({ @@ -48,7 +47,6 @@ export const BlocksEditor: FunctionComponent = ({ spellcheck, ignoreFirstChange = false, readonly, - onScroll, }) => { const [didIgnoreFirstChange, setDidIgnoreFirstChange] = useState(false) const handleChange = useCallback( @@ -87,7 +85,6 @@ export const BlocksEditor: FunctionComponent = ({ id={SuperEditorContentId} className={classNames('ContentEditable__root overflow-y-auto', className)} spellCheck={spellcheck} - onScroll={onScroll} />