mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-04-11 07:36:35 +00:00
Add sync scroll button (#481)
* Add disable sync scroll button Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
This commit is contained in:
parent
d488c8e2ad
commit
c95a7e0fba
12 changed files with 194 additions and 34 deletions
public/locales
src
components/editor
redux/editor
|
@ -201,6 +201,12 @@
|
|||
"switchToDark": "Switch to Dark Mode",
|
||||
"switchToLight": "Switch to Light Mode"
|
||||
},
|
||||
"appBar": {
|
||||
"syncScroll": {
|
||||
"disable": "Disable sync scroll",
|
||||
"enable": "Enable sync scroll"
|
||||
}
|
||||
},
|
||||
"editorToolbar": {
|
||||
"bold": "Bold",
|
||||
"italic": "Italic",
|
||||
|
|
|
@ -9,6 +9,7 @@ import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
|
|||
import { ShowIf } from '../../common/show-if/show-if'
|
||||
import { SignInButton } from '../../landing-layout/navigation/sign-in-button'
|
||||
import { UserDropdown } from '../../landing-layout/navigation/user-dropdown'
|
||||
import { SyncScrollButton } from './sync-scroll-button/sync-scroll-button'
|
||||
import { EditorPathParams } from '../editor'
|
||||
import { DarkModeButton } from './dark-mode-button'
|
||||
import { EditorViewMode } from './editor-view-mode'
|
||||
|
@ -25,6 +26,7 @@ export const AppBar: React.FC = () => {
|
|||
<Nav className="mr-auto d-flex align-items-center">
|
||||
<NavbarBranding/>
|
||||
<EditorViewMode/>
|
||||
<SyncScrollButton/>
|
||||
<DarkModeButton/>
|
||||
<Link to={`/p/${id}`} target='_blank'>
|
||||
<Button title={t('editor.documentBar.slideMode')} className="ml-2 text-secondary" size="sm" variant="outline-light">
|
||||
|
|
|
@ -3,7 +3,7 @@ import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap'
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { ApplicationState } from '../../../redux'
|
||||
import { setEditorModeConfig } from '../../../redux/editor/methods'
|
||||
import { setEditorMode } from '../../../redux/editor/methods'
|
||||
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
|
||||
|
||||
export enum EditorMode {
|
||||
|
@ -21,7 +21,7 @@ export const EditorViewMode: React.FC = () => {
|
|||
name="options"
|
||||
value={editorConfig.editorMode}
|
||||
onChange={(value: EditorMode) => {
|
||||
setEditorModeConfig(value)
|
||||
setEditorMode(value)
|
||||
}}>
|
||||
<ToggleButton value={EditorMode.PREVIEW} variant="outline-secondary" title={t('editor.viewMode.view')}>
|
||||
<ForkAwesomeIcon icon="eye"/>
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 135.46666 135.46666"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
|
||||
sodipodi:docname="buttonIcon.svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
fit-margin-bottom="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-top="0"
|
||||
id="base"
|
||||
pagecolor="#545b62"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.4"
|
||||
inkscape:cx="151.94971"
|
||||
inkscape:cy="220.06486"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:window-width="3434"
|
||||
inkscape:window-height="1321"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="84"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:pagecheckerboard="false" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
transform="translate(253.17277,890.86874)"
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
id="path864"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
|
||||
d="m -185.5862,-882.45881 c -1.05348,0.0354 -2.05943,0.44876 -2.83393,1.16582 l -17.37672,16.08016 c -1.77986,1.64837 -1.89553,4.45148 -0.25787,6.24199 1.63744,1.79079 4.43758,1.90901 6.21875,0.262 l 14.39653,-13.32218 14.39654,13.32218 c 1.78118,1.64702 4.5813,1.5288 6.21874,-0.262 1.63691,-1.79129 1.52077,-4.59444 -0.25993,-6.24199 l -17.37465,-16.08016 c -0.84861,-0.78564 -1.97318,-1.20418 -3.12746,-1.16582 z" />
|
||||
<path
|
||||
id="path851"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000"
|
||||
d="m -202.99806,-788.72004 c -1.12724,0.0475 -2.23794,0.52467 -3.05666,1.42007 -1.63766,1.79051 -1.52199,4.59363 0.25787,6.24199 l 17.37672,16.08016 c 0.7745,0.71706 1.78045,1.13042 2.83393,1.16582 1.15428,0.0384 2.27885,-0.38018 3.12746,-1.16582 l 17.37465,-16.08016 c 1.7807,-1.64754 1.89684,-4.4507 0.25993,-6.24199 -1.63744,-1.79081 -4.43756,-1.90902 -6.21874,-0.262 l -14.39654,13.32218 -14.39653,-13.32218 c -0.89058,-0.8235 -2.03485,-1.20561 -3.16209,-1.15807 z" />
|
||||
<circle
|
||||
r="16"
|
||||
cy="-823.13544"
|
||||
cx="-185.43944"
|
||||
id="path845"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:5.565;stroke-linecap:round;stroke-linejoin:round" />
|
||||
<g
|
||||
style="display:inline;opacity:1;stroke:none;stroke-opacity:1"
|
||||
id="g855">
|
||||
<path
|
||||
id="path858"
|
||||
d="m -128.54012,-883.40784 -121.89532,111.71083 8.0967,8.83405 121.89531,-111.71083 z"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.565;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1" />
|
||||
<path
|
||||
id="path860"
|
||||
d="m -128.61914,-886.18945 a 2.7827783,2.7827783 0 0 0 -1.80078,0.73047 l -121.89453,111.71093 a 2.7827783,2.7827783 0 0 0 -0.17188,3.93164 l 8.09571,8.83399 a 2.7827783,2.7827783 0 0 0 3.93164,0.16992 l 121.89648,-111.70898 a 2.7827783,2.7827783 0 0 0 0.16992,-3.93164 l -8.0957,-8.83399 a 2.7827783,2.7827783 0 0 0 -2.13086,-0.90234 z m -0.0937,6.71289 4.33789,4.73047 -117.79297,107.95117 -4.33594,-4.73047 z"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#545b62;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.565;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After (image error) Size: 8.9 KiB |
|
@ -0,0 +1 @@
|
|||
<svg height="512" viewBox="0 0 135.46666 135.46666" width="512" xmlns="http://www.w3.org/2000/svg"><g fill="#fff" transform="translate(253.17277 890.86874)"><path d="m-185.60701-890.86598c-1.20278.0405-2.35129.51236-3.23554 1.33104l-19.83931 18.35901c-2.0321 1.88196-2.16416 5.08233-.29441 7.12658 1.86949 2.04457 5.06645 2.17956 7.10004.29914l16.43678-15.21017 16.43678 15.21017c2.03361 1.88043 5.23055 1.74545 7.10004-.29914 1.86889-2.04514 1.73629-5.24555-.29677-7.12658l-19.83694-18.35901c-.96887-.89698-2.25281-1.37483-3.57067-1.33104z"/><path d="m-205.48644-783.84278c-1.28698.0542-2.55509.59901-3.48983 1.62131-1.86975 2.04426-1.73769 5.24463.29441 7.12659l19.83931 18.359c.88425.81868 2.03276 1.29062 3.23554 1.33104 1.31786.0439 2.6018-.43406 3.57067-1.33104l19.83694-18.359c2.03306-1.88102 2.16566-5.08144.29677-7.12659-1.86949-2.04459-5.06643-2.17956-7.10004-.29913l-16.43678 15.21017-16.43678-15.21017c-1.01679-.94021-2.32321-1.37647-3.61021-1.32218z"/><path d="m-185.49136-841.40292a18.267478 18.267478 0 0 0 -18.21562 18.26754 18.267478 18.267478 0 0 0 .23777 2.93583l22.52379-20.64169a18.267478 18.267478 0 0 0 -4.49402-.56168 18.267478 18.267478 0 0 0 -.0519 0zm18.08169 15.33288-22.5232 20.64052a18.267478 18.267478 0 0 0 4.49343.56168 18.267478 18.267478 0 0 0 18.26754-18.26754 18.267478 18.267478 0 0 0 -.23777-2.93466z" fill-rule="evenodd"/><path d="m-124.79368-883.92129-126.14511 116.28534 4.95067 5.40084 126.14688-116.28534z"/></g></svg>
|
After (image error) Size: 1.4 KiB |
|
@ -0,0 +1 @@
|
|||
<svg height="512" viewBox="0 0 135.46666 135.46666" width="512" xmlns="http://www.w3.org/2000/svg"><g fill="#545b62"><path d="m67.566309.00276674c-1.20278.040417-2.35129.512357-3.23555 1.33103706l-19.83929 18.3589962c-2.0321 1.881973-2.16417 5.082332-.29442 7.126588 1.8695 2.044576 5.06646 2.17955 7.10005.29913l16.43677-15.210163 16.43678 15.210163c2.03361 1.880432 5.23055 1.745458 7.10004-.29913 1.86889-2.045147 1.73629-5.24555-.29677-7.126588l-19.83693-18.3589962c-.96887-.89697906-2.25281-1.37483336-3.57068-1.33103706z"/><path d="m47.686889 107.02594c-1.28698.0542-2.55509.59903-3.48984 1.62132-1.86975 2.04426-1.73768 5.24462.29442 7.12659l19.83929 18.359c.88426.81868 2.03277 1.29062 3.23555 1.33104 1.31787.0438 2.60181-.43406 3.57068-1.33104l19.83693-18.359c2.03306-1.88103 2.16566-5.08144.29677-7.12659-1.86949-2.0446-5.06643-2.17956-7.10004-.29913l-16.43678 15.21016-16.43677-15.21016c-1.01679-.94021-2.32322-1.37647-3.61021-1.32219z"/><circle cx="67.733864" cy="67.733284" fill-rule="evenodd" r="18.267477"/></g></svg>
|
After (image error) Size: 1 KiB |
|
@ -0,0 +1,30 @@
|
|||
import React, { useCallback } from 'react'
|
||||
import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { ApplicationState } from '../../../../redux'
|
||||
import { setEditorSyncScroll } from '../../../../redux/editor/methods'
|
||||
import disabledScroll from './disabledScroll.svg'
|
||||
import enabledScroll from './enabledScroll.svg'
|
||||
|
||||
export const SyncScrollButton: React.FC = () => {
|
||||
const syncScroll: boolean = useSelector((state: ApplicationState) => state.editorConfig.syncScroll)
|
||||
const translation = syncScroll ? 'editor.appBar.syncScroll.enable' : 'editor.appBar.syncScroll.disable'
|
||||
const onClick = useCallback(() => {
|
||||
setEditorSyncScroll(!syncScroll)
|
||||
}, [syncScroll])
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<ToggleButtonGroup type="checkbox" defaultValue={[]} name="sync-scroll" className="ml-2" value={[syncScroll]}>
|
||||
<ToggleButton
|
||||
title={ t(translation) }
|
||||
variant={syncScroll ? 'secondary' : 'light'}
|
||||
onChange={onClick} value={true}
|
||||
>
|
||||
<img src={syncScroll ? disabledScroll : enabledScroll} width={'20px'} alt={t(translation)}/>
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
)
|
||||
}
|
|
@ -61,12 +61,14 @@ export const EditorPane: React.FC<EditorPaneProps & ScrollProps> = ({ onContentC
|
|||
})
|
||||
|
||||
const lastScrollPosition = useRef<number>()
|
||||
const [editorScroll, setEditorScroll] = useState<ScrollInfo>()
|
||||
const onEditorScroll = useCallback((editor: Editor, data: ScrollInfo) => setEditorScroll(data), [])
|
||||
|
||||
const onEditorScroll = useCallback((editor: Editor, data: ScrollInfo) => {
|
||||
if (!editor || !onScroll) {
|
||||
useEffect(() => {
|
||||
if (!editor || !onScroll || !editorScroll) {
|
||||
return
|
||||
}
|
||||
const scrollEventValue = data.top as number
|
||||
const scrollEventValue = editorScroll.top as number
|
||||
const line = editor.lineAtHeight(scrollEventValue, 'local')
|
||||
const startYOfLine = editor.heightAtLine(line, 'local')
|
||||
const lineInfo = editor.lineInfo(line)
|
||||
|
@ -79,7 +81,7 @@ export const EditorPane: React.FC<EditorPaneProps & ScrollProps> = ({ onContentC
|
|||
|
||||
const newScrollState: ScrollState = { firstLineInView: line + 1, scrolledPercentage: percentage }
|
||||
onScroll(newScrollState)
|
||||
}, [onScroll])
|
||||
}, [editor, editorScroll, onScroll])
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor || !scrollState) {
|
||||
|
|
|
@ -3,18 +3,18 @@ import { useTranslation } from 'react-i18next'
|
|||
import { useSelector } from 'react-redux'
|
||||
import useMedia from 'use-media'
|
||||
import { ApplicationState } from '../../redux'
|
||||
import { setEditorModeConfig } from '../../redux/editor/methods'
|
||||
import { setEditorMode } from '../../redux/editor/methods'
|
||||
import { DocumentTitle } from '../common/document-title/document-title'
|
||||
import { DocumentRenderPane } from './document-renderer-pane/document-render-pane'
|
||||
import { EditorPane } from './editor-pane/editor-pane'
|
||||
import { Splitter } from './splitter/splitter'
|
||||
import { MotdBanner } from '../common/motd-banner/motd-banner'
|
||||
import { DocumentBar } from './document-bar/document-bar'
|
||||
import { editorTestContent } from './editorTestContent'
|
||||
import { DualScrollState, ScrollState } from './scroll/scroll-props'
|
||||
import { YAMLMetaData } from './yaml-metadata/yaml-metadata'
|
||||
import { AppBar } from './app-bar/app-bar'
|
||||
import { EditorMode } from './app-bar/editor-view-mode'
|
||||
import { DocumentBar } from './document-bar/document-bar'
|
||||
import { DocumentRenderPane } from './document-renderer-pane/document-render-pane'
|
||||
import { EditorPane } from './editor-pane/editor-pane'
|
||||
import { editorTestContent } from './editorTestContent'
|
||||
import { DualScrollState, ScrollState } from './scroll/scroll-props'
|
||||
import { Splitter } from './splitter/splitter'
|
||||
import { YAMLMetaData } from './yaml-metadata/yaml-metadata'
|
||||
|
||||
export interface EditorPathParams {
|
||||
id: string
|
||||
|
@ -28,7 +28,6 @@ export enum ScrollSource {
|
|||
export const Editor: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const untitledNote = t('editor.untitledNote')
|
||||
const editorMode: EditorMode = useSelector((state: ApplicationState) => state.editorConfig.editorMode)
|
||||
const [markdownContent, setMarkdownContent] = useState(editorTestContent)
|
||||
const isWide = useMedia({ minWidth: 576 })
|
||||
const [firstDraw, setFirstDraw] = useState(true)
|
||||
|
@ -37,6 +36,9 @@ export const Editor: React.FC = () => {
|
|||
const firstHeading = useRef<string>()
|
||||
const scrollSource = useRef<ScrollSource>(ScrollSource.EDITOR)
|
||||
|
||||
const editorMode: EditorMode = useSelector((state: ApplicationState) => state.editorConfig.editorMode)
|
||||
const editorSyncScroll: boolean = useSelector((state: ApplicationState) => state.editorConfig.syncScroll)
|
||||
|
||||
const [scrollState, setScrollState] = useState<DualScrollState>(() => ({
|
||||
editorScrollState: { firstLineInView: 1, scrolledPercentage: 0 },
|
||||
rendererScrollState: { firstLineInView: 1, scrolledPercentage: 0 }
|
||||
|
@ -68,21 +70,21 @@ export const Editor: React.FC = () => {
|
|||
|
||||
useEffect(() => {
|
||||
if (!firstDraw && !isWide && editorMode === EditorMode.BOTH) {
|
||||
setEditorModeConfig(EditorMode.PREVIEW)
|
||||
setEditorMode(EditorMode.PREVIEW)
|
||||
}
|
||||
}, [editorMode, firstDraw, isWide])
|
||||
|
||||
const onEditorScroll = useCallback((newScrollState: ScrollState) => {
|
||||
if (scrollSource.current === ScrollSource.EDITOR) {
|
||||
setScrollState((old) => ({ rendererScrollState: newScrollState, editorScrollState: old.editorScrollState }))
|
||||
}
|
||||
}, [])
|
||||
|
||||
const onMarkdownRendererScroll = useCallback((newScrollState: ScrollState) => {
|
||||
if (scrollSource.current === ScrollSource.RENDERER) {
|
||||
if (scrollSource.current === ScrollSource.RENDERER && editorSyncScroll) {
|
||||
setScrollState((old) => ({ editorScrollState: newScrollState, rendererScrollState: old.rendererScrollState }))
|
||||
}
|
||||
}, [])
|
||||
}, [editorSyncScroll])
|
||||
|
||||
const onEditorScroll = useCallback((newScrollState: ScrollState) => {
|
||||
if (scrollSource.current === ScrollSource.EDITOR && editorSyncScroll) {
|
||||
setScrollState((old) => ({ rendererScrollState: newScrollState, editorScrollState: old.editorScrollState }))
|
||||
}
|
||||
}, [editorSyncScroll])
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
@ -99,7 +101,7 @@ export const Editor: React.FC = () => {
|
|||
content={markdownContent}
|
||||
scrollState={scrollState.editorScrollState}
|
||||
onScroll={onEditorScroll}
|
||||
onMakeScrollSource={() => { scrollSource.current = ScrollSource.EDITOR }}
|
||||
onMakeScrollSource={() => scrollSource.current = ScrollSource.EDITOR}
|
||||
/>
|
||||
}
|
||||
showRight={editorMode === EditorMode.PREVIEW || (editorMode === EditorMode.BOTH)}
|
||||
|
@ -111,7 +113,9 @@ export const Editor: React.FC = () => {
|
|||
onScroll={onMarkdownRendererScroll}
|
||||
onMetadataChange={onMetadataChange}
|
||||
onFirstHeadingChange={onFirstHeadingChange}
|
||||
onMakeScrollSource={() => { scrollSource.current = ScrollSource.RENDERER }}/>}
|
||||
onMakeScrollSource={() => {
|
||||
scrollSource.current = ScrollSource.RENDERER
|
||||
}}/>}
|
||||
containerClassName={'overflow-hidden'}/>
|
||||
</div>
|
||||
</Fragment>
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
import { store } from '..'
|
||||
import { EditorMode } from '../../components/editor/app-bar/editor-view-mode'
|
||||
import { EditorConfigActionType, SetEditorConfigAction } from './types'
|
||||
import { EditorConfigActionType, SetEditorConfigAction, SetEditorSyncScrollAction } from './types'
|
||||
|
||||
export const setEditorModeConfig = (editorMode: EditorMode): void => {
|
||||
export const setEditorMode = (editorMode: EditorMode): void => {
|
||||
const action: SetEditorConfigAction = {
|
||||
type: EditorConfigActionType.SET_EDITOR_VIEW_MODE,
|
||||
mode: editorMode
|
||||
}
|
||||
store.dispatch(action)
|
||||
}
|
||||
|
||||
export const setEditorSyncScroll = (syncScroll: boolean): void => {
|
||||
const action: SetEditorSyncScrollAction = {
|
||||
type: EditorConfigActionType.SET_SYNC_SCROLL,
|
||||
syncScroll: syncScroll
|
||||
}
|
||||
store.dispatch(action)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
import { Reducer } from 'redux'
|
||||
import { EditorConfig, EditorConfigActions, EditorConfigActionType, SetEditorConfigAction } from './types'
|
||||
import {
|
||||
EditorConfig,
|
||||
EditorConfigActions,
|
||||
EditorConfigActionType,
|
||||
SetEditorConfigAction,
|
||||
SetEditorSyncScrollAction
|
||||
} from './types'
|
||||
import { EditorMode } from '../../components/editor/app-bar/editor-view-mode'
|
||||
|
||||
export const initialState: EditorConfig = {
|
||||
editorMode: EditorMode.BOTH
|
||||
editorMode: EditorMode.BOTH,
|
||||
syncScroll: true
|
||||
}
|
||||
|
||||
export const EditorConfigReducer: Reducer<EditorConfig, EditorConfigActions> = (state: EditorConfig = initialState, action: EditorConfigActions) => {
|
||||
|
@ -13,6 +20,11 @@ export const EditorConfigReducer: Reducer<EditorConfig, EditorConfigActions> = (
|
|||
...state,
|
||||
editorMode: (action as SetEditorConfigAction).mode
|
||||
}
|
||||
case EditorConfigActionType.SET_SYNC_SCROLL:
|
||||
return {
|
||||
...state,
|
||||
syncScroll: (action as SetEditorSyncScrollAction).syncScroll
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
import { Action } from 'redux'
|
||||
import { EditorMode } from '../../components/editor/app-bar/editor-view-mode';
|
||||
import { EditorMode } from '../../components/editor/app-bar/editor-view-mode'
|
||||
|
||||
export enum EditorConfigActionType {
|
||||
SET_EDITOR_VIEW_MODE = 'editor/mode/set'
|
||||
SET_EDITOR_VIEW_MODE = 'editor/mode/set',
|
||||
SET_SYNC_SCROLL = 'editor/syncScroll/set'
|
||||
}
|
||||
|
||||
export interface EditorConfig {
|
||||
editorMode: EditorMode;
|
||||
syncScroll: boolean;
|
||||
}
|
||||
|
||||
export interface EditorConfigActions extends Action<EditorConfigActionType> {
|
||||
type: EditorConfigActionType;
|
||||
}
|
||||
|
||||
export interface SetEditorConfigAction extends EditorConfigActions {
|
||||
mode: EditorMode;
|
||||
export interface SetEditorSyncScrollAction extends EditorConfigActions {
|
||||
syncScroll: boolean
|
||||
}
|
||||
|
||||
export interface SetEditorConfigAction extends EditorConfigActions {
|
||||
mode: EditorMode
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue