chore: fix scroll sync in conflict resolution modal

This commit is contained in:
Aman Harwara
2023-12-18 13:23:39 +05:30
parent 3b0e0ac013
commit 661cb73749
3 changed files with 60 additions and 14 deletions

View File

@@ -161,6 +161,9 @@ const NoteConflictResolutionModal = ({
const [comparisonScrollPos, setComparisonScrollPos] = useState(0) const [comparisonScrollPos, setComparisonScrollPos] = useState(0)
const [shouldSyncComparisonScroll, setShouldSyncComparisonScroll] = useState(true) const [shouldSyncComparisonScroll, setShouldSyncComparisonScroll] = useState(true)
const onScroll = useCallback(({ target }: { target: EventTarget | null }) => {
setComparisonScrollPos((target as HTMLElement).scrollTop)
}, [])
return ( return (
<Modal <Modal
@@ -319,7 +322,7 @@ const NoteConflictResolutionModal = ({
key={note.uuid} key={note.uuid}
scrollPos={comparisonScrollPos} scrollPos={comparisonScrollPos}
shouldSyncScroll={shouldSyncComparisonScroll} shouldSyncScroll={shouldSyncComparisonScroll}
onScroll={(event) => setComparisonScrollPos((event.target as HTMLElement).scrollTop)} onScroll={onScroll}
/> />
))} ))}
</div> </div>

View File

@@ -8,7 +8,7 @@ import {
classNames, classNames,
isUIFeatureAnIframeFeature, isUIFeatureAnIframeFeature,
} from '@standardnotes/snjs' } 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 { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
import { useApplication } from '../ApplicationProvider' import { useApplication } from '../ApplicationProvider'
import IframeFeatureView from '../ComponentView/IframeFeatureView' import IframeFeatureView from '../ComponentView/IframeFeatureView'
@@ -19,6 +19,7 @@ import { useLinkingController } from '@/Controllers/LinkingControllerProvider'
import LinkedItemBubblesContainer from '../LinkedItems/LinkedItemBubblesContainer' import LinkedItemBubblesContainer from '../LinkedItems/LinkedItemBubblesContainer'
import usePreference from '@/Hooks/usePreference' import usePreference from '@/Hooks/usePreference'
import { useResponsiveEditorFontSize } from '@/Utils/getPlaintextFontSize' import { useResponsiveEditorFontSize } from '@/Utils/getPlaintextFontSize'
import { getScrollParent } from '@/Utils'
export const ReadonlyNoteContent = ({ export const ReadonlyNoteContent = ({
note, note,
@@ -33,7 +34,7 @@ export const ReadonlyNoteContent = ({
showLinkedItems?: boolean showLinkedItems?: boolean
scrollPos?: number scrollPos?: number
shouldSyncScroll?: boolean shouldSyncScroll?: boolean
onScroll?: UIEventHandler onScroll?: ({ target }: { target: EventTarget | null }) => void
}) => { }) => {
const application = useApplication() const application = useApplication()
const linkingController = useLinkingController() const linkingController = useLinkingController()
@@ -64,6 +65,27 @@ export const ReadonlyNoteContent = ({
}, [application, componentViewer]) }, [application, componentViewer])
const containerRef = useRef<HTMLDivElement>(null) const containerRef = useRef<HTMLDivElement>(null)
const scrollerRef = useRef<HTMLElement | null>()
const setScroller = useCallback(() => {
if (scrollerRef.current) {
return
}
scrollerRef.current = containerRef.current?.querySelector<HTMLElement>('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(() => { useEffect(() => {
if (!shouldSyncScroll) { if (!shouldSyncScroll) {
@@ -74,16 +96,41 @@ export const ReadonlyNoteContent = ({
return return
} }
const scroller = containerRef.current.querySelector('textarea, .ContentEditable__root') if (!scrollerRef.current) {
setScroller()
if (!scroller) {
return
} }
scroller.scrollTo({ scrollerRef.current?.scrollTo({
top: scrollPos, 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 lineHeight = usePreference(PrefKey.EditorLineHeight)
const fontSize = usePreference(PrefKey.EditorFontSize) const fontSize = usePreference(PrefKey.EditorFontSize)
@@ -123,7 +170,6 @@ export const ReadonlyNoteContent = ({
readonly readonly
className="blocks-editor relative h-full resize-none p-4 text-base focus:shadow-none focus:outline-none" className="blocks-editor relative h-full resize-none p-4 text-base focus:shadow-none focus:outline-none"
spellcheck={content.spellcheck} spellcheck={content.spellcheck}
onScroll={onScroll}
></BlocksEditor> ></BlocksEditor>
</BlocksEditorComposer> </BlocksEditorComposer>
</div> </div>

View File

@@ -1,4 +1,4 @@
import { FunctionComponent, UIEventHandler, useCallback, useState } from 'react' import { FunctionComponent, useCallback, useState } from 'react'
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin' import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'
import { ContentEditable } from '@lexical/react/LexicalContentEditable' import { ContentEditable } from '@lexical/react/LexicalContentEditable'
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin' import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin'
@@ -37,7 +37,6 @@ type BlocksEditorProps = {
spellcheck?: boolean spellcheck?: boolean
ignoreFirstChange?: boolean ignoreFirstChange?: boolean
readonly?: boolean readonly?: boolean
onScroll?: UIEventHandler
} }
export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
@@ -48,7 +47,6 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
spellcheck, spellcheck,
ignoreFirstChange = false, ignoreFirstChange = false,
readonly, readonly,
onScroll,
}) => { }) => {
const [didIgnoreFirstChange, setDidIgnoreFirstChange] = useState(false) const [didIgnoreFirstChange, setDidIgnoreFirstChange] = useState(false)
const handleChange = useCallback( const handleChange = useCallback(
@@ -87,7 +85,6 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
id={SuperEditorContentId} id={SuperEditorContentId}
className={classNames('ContentEditable__root overflow-y-auto', className)} className={classNames('ContentEditable__root overflow-y-auto', className)}
spellCheck={spellcheck} spellCheck={spellcheck}
onScroll={onScroll}
/> />
<div className="search-highlight-container pointer-events-none absolute left-0 top-0 h-full w-full" /> <div className="search-highlight-container pointer-events-none absolute left-0 top-0 h-full w-full" />
</div> </div>