mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-24 03:57:06 -04:00
feat(splitter): add snapping, icon and buttons to splitter
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
0984a84a68
commit
5ba1e9e565
19 changed files with 822 additions and 460 deletions
|
@ -10,7 +10,6 @@ const title = 'This is a test title'
|
||||||
describe('Document Title', () => {
|
describe('Document Title', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.visitTestNote()
|
cy.visitTestNote()
|
||||||
cy.getByCypressId('view-mode-both').should('exist')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('title should be yaml metadata title', () => {
|
describe('title should be yaml metadata title', () => {
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { PAGE_MODE } from '../support/visit'
|
|
||||||
|
|
||||||
describe('Editor mode from URL parameter is used', () => {
|
|
||||||
it('mode view', () => {
|
|
||||||
cy.visitTestNote(PAGE_MODE.EDITOR, 'view')
|
|
||||||
cy.getByCypressId('editor-pane').should('not.be.visible')
|
|
||||||
cy.getByCypressId('documentIframe').should('be.visible')
|
|
||||||
})
|
|
||||||
it('mode both', () => {
|
|
||||||
cy.visitTestNote(PAGE_MODE.EDITOR, 'both')
|
|
||||||
cy.getByCypressId('editor-pane').should('be.visible')
|
|
||||||
cy.getByCypressId('documentIframe').should('be.visible')
|
|
||||||
})
|
|
||||||
it('mode edit', () => {
|
|
||||||
cy.visitTestNote(PAGE_MODE.EDITOR, 'edit')
|
|
||||||
cy.getByCypressId('editor-pane').should('be.visible')
|
|
||||||
cy.getByCypressId('documentIframe').should('not.be.visible')
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -10,7 +10,6 @@ import { ShowIf } from '../../common/show-if/show-if'
|
||||||
import { SignInButton } from '../../landing-layout/navigation/sign-in-button'
|
import { SignInButton } from '../../landing-layout/navigation/sign-in-button'
|
||||||
import { UserDropdown } from '../../landing-layout/navigation/user-dropdown'
|
import { UserDropdown } from '../../landing-layout/navigation/user-dropdown'
|
||||||
import { DarkModeButton } from './dark-mode-button'
|
import { DarkModeButton } from './dark-mode-button'
|
||||||
import { EditorViewMode } from './editor-view-mode'
|
|
||||||
import { HelpButton } from './help-button/help-button'
|
import { HelpButton } from './help-button/help-button'
|
||||||
import { NavbarBranding } from './navbar-branding'
|
import { NavbarBranding } from './navbar-branding'
|
||||||
import { SyncScrollButtons } from './sync-scroll-buttons/sync-scroll-buttons'
|
import { SyncScrollButtons } from './sync-scroll-buttons/sync-scroll-buttons'
|
||||||
|
@ -43,7 +42,6 @@ export const AppBar: React.FC<AppBarProps> = ({ mode }) => {
|
||||||
<Nav className='me-auto d-flex align-items-center'>
|
<Nav className='me-auto d-flex align-items-center'>
|
||||||
<NavbarBranding />
|
<NavbarBranding />
|
||||||
<ShowIf condition={mode === AppBarMode.EDITOR}>
|
<ShowIf condition={mode === AppBarMode.EDITOR}>
|
||||||
<EditorViewMode />
|
|
||||||
<SyncScrollButtons />
|
<SyncScrollButtons />
|
||||||
</ShowIf>
|
</ShowIf>
|
||||||
<DarkModeButton />
|
<DarkModeButton />
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react'
|
|
||||||
import { Button, ButtonGroup } from 'react-bootstrap'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { setEditorMode } from '../../../redux/editor/methods'
|
|
||||||
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
|
|
||||||
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
|
||||||
import { cypressId } from '../../../utils/cypress-attribute'
|
|
||||||
|
|
||||||
export enum EditorMode {
|
|
||||||
PREVIEW = 'view',
|
|
||||||
BOTH = 'both',
|
|
||||||
EDITOR = 'edit'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders the button group to set the editor mode.
|
|
||||||
* @see EditorMode
|
|
||||||
*/
|
|
||||||
export const EditorViewMode: React.FC = () => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const editorMode = useApplicationState((state) => state.editorConfig.editorMode)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ButtonGroup>
|
|
||||||
<Button
|
|
||||||
{...cypressId('view-mode-editor')}
|
|
||||||
onClick={() => setEditorMode(EditorMode.EDITOR)}
|
|
||||||
variant={editorMode === EditorMode.EDITOR ? 'secondary' : 'outline-secondary'}
|
|
||||||
title={t('editor.viewMode.edit')}>
|
|
||||||
<ForkAwesomeIcon icon='pencil' />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
{...cypressId('view-mode-both')}
|
|
||||||
onClick={() => setEditorMode(EditorMode.BOTH)}
|
|
||||||
variant={editorMode === EditorMode.BOTH ? 'secondary' : 'outline-secondary'}
|
|
||||||
title={t('editor.viewMode.both')}>
|
|
||||||
<ForkAwesomeIcon icon='columns' />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
{...cypressId('view-mode-preview')}
|
|
||||||
onClick={() => setEditorMode(EditorMode.PREVIEW)}
|
|
||||||
variant={editorMode === EditorMode.PREVIEW ? 'secondary' : 'outline-secondary'}
|
|
||||||
title={t('editor.viewMode.view')}>
|
|
||||||
<ForkAwesomeIcon icon='eye' />
|
|
||||||
</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -24,7 +24,6 @@ import { useBaseUrl } from '../../../../hooks/common/use-base-url'
|
||||||
export const ShareModal: React.FC<ModalVisibilityProps> = ({ show, onHide }) => {
|
export const ShareModal: React.FC<ModalVisibilityProps> = ({ show, onHide }) => {
|
||||||
useTranslation()
|
useTranslation()
|
||||||
const noteFrontmatter = useApplicationState((state) => state.noteDetails.frontmatter)
|
const noteFrontmatter = useApplicationState((state) => state.noteDetails.frontmatter)
|
||||||
const editorMode = useApplicationState((state) => state.editorConfig.editorMode)
|
|
||||||
const baseUrl = useBaseUrl()
|
const baseUrl = useBaseUrl()
|
||||||
const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress)
|
const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress)
|
||||||
|
|
||||||
|
@ -32,10 +31,7 @@ export const ShareModal: React.FC<ModalVisibilityProps> = ({ show, onHide }) =>
|
||||||
<CommonModal show={show} onHide={onHide} showCloseButton={true} title={'editor.modal.shareLink.title'}>
|
<CommonModal show={show} onHide={onHide} showCloseButton={true} title={'editor.modal.shareLink.title'}>
|
||||||
<Modal.Body>
|
<Modal.Body>
|
||||||
<Trans i18nKey={'editor.modal.shareLink.editorDescription'} />
|
<Trans i18nKey={'editor.modal.shareLink.editorDescription'} />
|
||||||
<CopyableField
|
<CopyableField content={`${baseUrl}n/${noteIdentifier}`} shareOriginUrl={`${baseUrl}n/${noteIdentifier}`} />
|
||||||
content={`${baseUrl}n/${noteIdentifier}?${editorMode}`}
|
|
||||||
shareOriginUrl={`${baseUrl}n/${noteIdentifier}?${editorMode}`}
|
|
||||||
/>
|
|
||||||
<ShowIf condition={noteFrontmatter.type === NoteType.SLIDE}>
|
<ShowIf condition={noteFrontmatter.type === NoteType.SLIDE}>
|
||||||
<Trans i18nKey={'editor.modal.shareLink.slidesDescription'} />
|
<Trans i18nKey={'editor.modal.shareLink.slidesDescription'} />
|
||||||
<CopyableField content={`${baseUrl}p/${noteIdentifier}`} shareOriginUrl={`${baseUrl}p/${noteIdentifier}`} />
|
<CopyableField content={`${baseUrl}p/${noteIdentifier}`} shareOriginUrl={`${baseUrl}p/${noteIdentifier}`} />
|
||||||
|
|
|
@ -10,12 +10,9 @@ import { useApplyDarkMode } from '../../hooks/common/use-apply-dark-mode'
|
||||||
import { updateNoteTitleByFirstHeading } from '../../redux/note-details/methods'
|
import { updateNoteTitleByFirstHeading } from '../../redux/note-details/methods'
|
||||||
import { MotdModal } from '../common/motd-modal/motd-modal'
|
import { MotdModal } from '../common/motd-modal/motd-modal'
|
||||||
import { AppBar, AppBarMode } from './app-bar/app-bar'
|
import { AppBar, AppBarMode } from './app-bar/app-bar'
|
||||||
import { EditorMode } from './app-bar/editor-view-mode'
|
|
||||||
import { useViewModeShortcuts } from './hooks/use-view-mode-shortcuts'
|
|
||||||
import { Sidebar } from './sidebar/sidebar'
|
import { Sidebar } from './sidebar/sidebar'
|
||||||
import { Splitter } from './splitter/splitter'
|
import { Splitter } from './splitter/splitter'
|
||||||
import type { DualScrollState, ScrollState } from './synced-scroll/scroll-props'
|
import type { DualScrollState, ScrollState } from './synced-scroll/scroll-props'
|
||||||
import { useEditorModeFromUrl } from './hooks/use-editor-mode-from-url'
|
|
||||||
import { useUpdateLocalHistoryEntry } from './hooks/use-update-local-history-entry'
|
import { useUpdateLocalHistoryEntry } from './hooks/use-update-local-history-entry'
|
||||||
import { useApplicationState } from '../../hooks/common/use-application-state'
|
import { useApplicationState } from '../../hooks/common/use-application-state'
|
||||||
import { EditorDocumentRenderer } from './editor-document-renderer/editor-document-renderer'
|
import { EditorDocumentRenderer } from './editor-document-renderer/editor-document-renderer'
|
||||||
|
@ -41,7 +38,6 @@ const log = new Logger('EditorPage')
|
||||||
export const EditorPageContent: React.FC = () => {
|
export const EditorPageContent: React.FC = () => {
|
||||||
useTranslation()
|
useTranslation()
|
||||||
const scrollSource = useRef<ScrollSource>(ScrollSource.EDITOR)
|
const scrollSource = useRef<ScrollSource>(ScrollSource.EDITOR)
|
||||||
const editorMode: EditorMode = useApplicationState((state) => state.editorConfig.editorMode)
|
|
||||||
const editorSyncScroll: boolean = useApplicationState((state) => state.editorConfig.syncScroll)
|
const editorSyncScroll: boolean = useApplicationState((state) => state.editorConfig.syncScroll)
|
||||||
|
|
||||||
const [scrollState, setScrollState] = useState<DualScrollState>(() => ({
|
const [scrollState, setScrollState] = useState<DualScrollState>(() => ({
|
||||||
|
@ -83,9 +79,7 @@ export const EditorPageContent: React.FC = () => {
|
||||||
[editorSyncScroll]
|
[editorSyncScroll]
|
||||||
)
|
)
|
||||||
|
|
||||||
useViewModeShortcuts()
|
|
||||||
useApplyDarkMode()
|
useApplyDarkMode()
|
||||||
useEditorModeFromUrl()
|
|
||||||
|
|
||||||
useUpdateLocalHistoryEntry()
|
useUpdateLocalHistoryEntry()
|
||||||
|
|
||||||
|
@ -140,11 +134,9 @@ export const EditorPageContent: React.FC = () => {
|
||||||
<AppBar mode={AppBarMode.EDITOR} />
|
<AppBar mode={AppBarMode.EDITOR} />
|
||||||
<div className={'flex-fill d-flex h-100 w-100 overflow-hidden flex-row'}>
|
<div className={'flex-fill d-flex h-100 w-100 overflow-hidden flex-row'}>
|
||||||
<Splitter
|
<Splitter
|
||||||
showLeft={editorMode === EditorMode.EDITOR || editorMode === EditorMode.BOTH}
|
|
||||||
left={leftPane}
|
left={leftPane}
|
||||||
showRight={editorMode === EditorMode.PREVIEW || editorMode === EditorMode.BOTH}
|
|
||||||
right={rightPane}
|
right={rightPane}
|
||||||
additionalContainerClassName={'overflow-hidden'}
|
additionalContainerClassName={'overflow-hidden position-relative'}
|
||||||
/>
|
/>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { useEffect } from 'react'
|
|
||||||
import { EditorMode } from '../app-bar/editor-view-mode'
|
|
||||||
import { setEditorMode } from '../../../redux/editor/methods'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts the specified editor mode from the URL query and sets that into the global application state.
|
|
||||||
*/
|
|
||||||
export const useEditorModeFromUrl = (): void => {
|
|
||||||
const { query } = useRouter()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const mode = Object.values(EditorMode).find((mode) => query[mode] !== undefined)
|
|
||||||
if (mode) {
|
|
||||||
setEditorMode(mode)
|
|
||||||
}
|
|
||||||
}, [query])
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { useEffect } from 'react'
|
|
||||||
import { setEditorMode } from '../../../redux/editor/methods'
|
|
||||||
import { EditorMode } from '../app-bar/editor-view-mode'
|
|
||||||
|
|
||||||
const shortcutHandler = (event: KeyboardEvent): void => {
|
|
||||||
if (event.ctrlKey && event.altKey && event.key === 'b') {
|
|
||||||
setEditorMode(EditorMode.BOTH)
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.ctrlKey && event.altKey && event.key === 'v') {
|
|
||||||
setEditorMode(EditorMode.PREVIEW)
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.ctrlKey && event.altKey && (event.key === 'e' || event.key === '€')) {
|
|
||||||
setEditorMode(EditorMode.EDITOR)
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds global view mode keyboard shortcuts and removes them again, if the hook is dismissed.
|
|
||||||
*
|
|
||||||
* @see shortcutHandler
|
|
||||||
*/
|
|
||||||
export const useViewModeShortcuts = (): void => {
|
|
||||||
useEffect(() => {
|
|
||||||
document.addEventListener('keydown', shortcutHandler, false)
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('keydown', shortcutHandler, false)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
}
|
|
|
@ -1,100 +1,65 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`Splitter can render both panes 1`] = `
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
class="flex-fill flex-row d-flex "
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="splitter left "
|
|
||||||
style="width: calc(50% - 5px);"
|
|
||||||
>
|
|
||||||
left
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="splitter separator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="split-divider"
|
|
||||||
data-testid="splitter-divider"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="splitter"
|
|
||||||
style="width: calc(100% - 50%);"
|
|
||||||
>
|
|
||||||
right
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Splitter can render only the left pane 1`] = `
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
class="flex-fill flex-row d-flex "
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="splitter left "
|
|
||||||
style="width: calc(100% - 5px);"
|
|
||||||
>
|
|
||||||
left
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="splitter d-none"
|
|
||||||
style="width: calc(100% - 100%);"
|
|
||||||
>
|
|
||||||
right
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Splitter can render only the right pane 1`] = `
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
class="flex-fill flex-row d-flex "
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="splitter left d-none"
|
|
||||||
style="width: calc(0% - 5px);"
|
|
||||||
>
|
|
||||||
left
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="splitter"
|
|
||||||
style="width: calc(100% - 0%);"
|
|
||||||
>
|
|
||||||
right
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Splitter resize can change size with mouse 1`] = `
|
exports[`Splitter resize can change size with mouse 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="flex-fill flex-row d-flex "
|
class="flex-fill flex-row d-flex "
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="splitter left "
|
class="left"
|
||||||
style="width: calc(50% - 5px);"
|
style="width: calc(50% - 5px);"
|
||||||
>
|
>
|
||||||
left
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
left
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="splitter separator"
|
class="split-divider"
|
||||||
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="bg-light middle "
|
||||||
data-testid="splitter-divider"
|
>
|
||||||
/>
|
<div
|
||||||
|
class="buttons"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-left "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
class="grabber"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrows-h "
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-right "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="splitter"
|
class="right"
|
||||||
style="width: calc(100% - 50%);"
|
style="width: calc(100% - 50%);"
|
||||||
>
|
>
|
||||||
right
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
right
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -106,24 +71,60 @@ exports[`Splitter resize can change size with mouse 2`] = `
|
||||||
class="flex-fill flex-row d-flex "
|
class="flex-fill flex-row d-flex "
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="splitter left "
|
class="left"
|
||||||
style="width: calc(100% - 5px);"
|
style="width: calc(50% - 5px);"
|
||||||
>
|
|
||||||
left
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="splitter separator"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="inner"
|
||||||
data-testid="splitter-divider"
|
>
|
||||||
/>
|
left
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="splitter"
|
class="split-divider"
|
||||||
style="width: calc(100% - 100%);"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
right
|
<div
|
||||||
|
class="bg-light middle "
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="buttons"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-left "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
class="grabber"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrows-h "
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-right "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="right"
|
||||||
|
style="width: calc(100% - 50%);"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
right
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -135,24 +136,60 @@ exports[`Splitter resize can change size with mouse 3`] = `
|
||||||
class="flex-fill flex-row d-flex "
|
class="flex-fill flex-row d-flex "
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="splitter left "
|
class="left"
|
||||||
style="width: calc(0% - 5px);"
|
style="width: calc(50% - 5px);"
|
||||||
>
|
|
||||||
left
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="splitter separator"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="inner"
|
||||||
data-testid="splitter-divider"
|
>
|
||||||
/>
|
left
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="splitter"
|
class="split-divider"
|
||||||
style="width: calc(100% - 0%);"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
right
|
<div
|
||||||
|
class="bg-light middle "
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="buttons"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-left "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
class="grabber"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrows-h "
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-right "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="right"
|
||||||
|
style="width: calc(100% - 50%);"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
right
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -164,24 +201,60 @@ exports[`Splitter resize can change size with mouse 4`] = `
|
||||||
class="flex-fill flex-row d-flex "
|
class="flex-fill flex-row d-flex "
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="splitter left "
|
class="left"
|
||||||
style="width: calc(0% - 5px);"
|
style="width: calc(50% - 5px);"
|
||||||
>
|
|
||||||
left
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="splitter separator"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="inner"
|
||||||
data-testid="splitter-divider"
|
>
|
||||||
/>
|
left
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="splitter"
|
class="split-divider"
|
||||||
style="width: calc(100% - 0%);"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
right
|
<div
|
||||||
|
class="bg-light middle "
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="buttons"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-left "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
class="grabber"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrows-h "
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-right "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="right"
|
||||||
|
style="width: calc(100% - 50%);"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
right
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -193,24 +266,60 @@ exports[`Splitter resize can change size with touch 1`] = `
|
||||||
class="flex-fill flex-row d-flex "
|
class="flex-fill flex-row d-flex "
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="splitter left "
|
class="left"
|
||||||
style="width: calc(50% - 5px);"
|
style="width: calc(50% - 5px);"
|
||||||
>
|
>
|
||||||
left
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
left
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="splitter separator"
|
class="split-divider"
|
||||||
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="bg-light middle "
|
||||||
data-testid="splitter-divider"
|
>
|
||||||
/>
|
<div
|
||||||
|
class="buttons"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-left "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
class="grabber"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrows-h "
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-right "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="splitter"
|
class="right"
|
||||||
style="width: calc(100% - 50%);"
|
style="width: calc(100% - 50%);"
|
||||||
>
|
>
|
||||||
right
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
right
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -222,24 +331,60 @@ exports[`Splitter resize can change size with touch 2`] = `
|
||||||
class="flex-fill flex-row d-flex "
|
class="flex-fill flex-row d-flex "
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="splitter left "
|
class="left"
|
||||||
style="width: calc(100% - 5px);"
|
style="width: calc(50% - 5px);"
|
||||||
>
|
|
||||||
left
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="splitter separator"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="inner"
|
||||||
data-testid="splitter-divider"
|
>
|
||||||
/>
|
left
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="splitter"
|
class="split-divider"
|
||||||
style="width: calc(100% - 100%);"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
right
|
<div
|
||||||
|
class="bg-light middle "
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="buttons"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-left "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
class="grabber"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrows-h "
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-right "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="right"
|
||||||
|
style="width: calc(100% - 50%);"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
right
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -251,24 +396,60 @@ exports[`Splitter resize can change size with touch 3`] = `
|
||||||
class="flex-fill flex-row d-flex "
|
class="flex-fill flex-row d-flex "
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="splitter left "
|
class="left"
|
||||||
style="width: calc(0% - 5px);"
|
style="width: calc(50% - 5px);"
|
||||||
>
|
|
||||||
left
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="splitter separator"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="inner"
|
||||||
data-testid="splitter-divider"
|
>
|
||||||
/>
|
left
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="splitter"
|
class="split-divider"
|
||||||
style="width: calc(100% - 0%);"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
right
|
<div
|
||||||
|
class="bg-light middle "
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="buttons"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-left "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
class="grabber"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrows-h "
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-right "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="right"
|
||||||
|
style="width: calc(100% - 50%);"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
right
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -280,24 +461,255 @@ exports[`Splitter resize can change size with touch 4`] = `
|
||||||
class="flex-fill flex-row d-flex "
|
class="flex-fill flex-row d-flex "
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="splitter left "
|
class="left"
|
||||||
style="width: calc(0% - 5px);"
|
style="width: calc(50% - 5px);"
|
||||||
>
|
|
||||||
left
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="splitter separator"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="split-divider"
|
class="inner"
|
||||||
data-testid="splitter-divider"
|
>
|
||||||
/>
|
left
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="splitter"
|
class="split-divider"
|
||||||
style="width: calc(100% - 0%);"
|
data-testid="splitter-divider"
|
||||||
>
|
>
|
||||||
right
|
<div
|
||||||
|
class="bg-light middle "
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="buttons"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-left "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
class="grabber"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrows-h "
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-right "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="right"
|
||||||
|
style="width: calc(100% - 50%);"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
right
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Splitter resize can react to shortcuts 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="flex-fill flex-row d-flex "
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="left"
|
||||||
|
style="width: calc(0% - 5px);"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
left
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="split-divider"
|
||||||
|
data-testid="splitter-divider"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="bg-light middle shift-right"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="buttons"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn btn-secondary"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-left "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
class="grabber"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrows-h "
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-right "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="right"
|
||||||
|
style="width: calc(100% - 0%);"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
right
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Splitter resize can react to shortcuts 2`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="flex-fill flex-row d-flex "
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="left"
|
||||||
|
style="width: calc(100% - 5px);"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
left
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="split-divider"
|
||||||
|
data-testid="splitter-divider"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="bg-light middle shift-left"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="buttons"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-left "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
class="grabber"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrows-h "
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
class="btn btn-secondary"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-right "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="right"
|
||||||
|
style="width: calc(100% - 100%);"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
right
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Splitter resize can react to shortcuts 3`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="flex-fill flex-row d-flex "
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="left"
|
||||||
|
style="width: calc(50% - 5px);"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
left
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="split-divider"
|
||||||
|
data-testid="splitter-divider"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="bg-light middle "
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="buttons"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-left "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
class="grabber"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrows-h "
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
class="btn btn-light"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="fa fa-arrow-right "
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="right"
|
||||||
|
style="width: calc(100% - 50%);"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="inner"
|
||||||
|
>
|
||||||
|
right
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { useMemo } from 'react'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the adjusted relative split value.
|
|
||||||
*
|
|
||||||
* @param showLeft Defines if the left split pane should be shown
|
|
||||||
* @param showRight Defines if the right split pane should be shown
|
|
||||||
* @param relativeSplitValue The relative size ratio of the split
|
|
||||||
* @return the limited (0% to 100%) relative split value. If only the left or right pane should be shown then the return value will be always 100 or 0
|
|
||||||
*/
|
|
||||||
export const useAdjustedRelativeSplitValue = (
|
|
||||||
showLeft: boolean,
|
|
||||||
showRight: boolean,
|
|
||||||
relativeSplitValue: number
|
|
||||||
): number =>
|
|
||||||
useMemo(() => {
|
|
||||||
if (!showLeft && showRight) {
|
|
||||||
return 0
|
|
||||||
} else if (showLeft && !showRight) {
|
|
||||||
return 100
|
|
||||||
} else {
|
|
||||||
return Math.min(100, Math.max(0, relativeSplitValue))
|
|
||||||
}
|
|
||||||
}, [relativeSplitValue, showLeft, showRight])
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds global keyboard shortcuts for setting the split value.
|
||||||
|
*
|
||||||
|
* @param setRelativeSplitValue A function that is used to set the split value
|
||||||
|
*/
|
||||||
|
export const useKeyboardShortcuts = (setRelativeSplitValue: (value: number) => void) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const shortcutHandler = (event: KeyboardEvent): void => {
|
||||||
|
if (event.ctrlKey && event.altKey && event.key === 'b') {
|
||||||
|
setRelativeSplitValue(50)
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.ctrlKey && event.altKey && event.key === 'v') {
|
||||||
|
setRelativeSplitValue(0)
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.ctrlKey && event.altKey && (event.key === 'e' || event.key === '€')) {
|
||||||
|
setRelativeSplitValue(100)
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('keydown', shortcutHandler, false)
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', shortcutHandler, false)
|
||||||
|
}
|
||||||
|
}, [setRelativeSplitValue])
|
||||||
|
}
|
|
@ -5,14 +5,69 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.split-divider {
|
.split-divider {
|
||||||
width: 10px;
|
width: 15px;
|
||||||
background: white;
|
background: white;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
cursor: col-resize;
|
|
||||||
box-shadow: 3px 0 6px #e7e7e7;
|
box-shadow: 3px 0 6px #e7e7e7;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
:global(body.dark) & {
|
:global(body.dark) & {
|
||||||
box-shadow: 3px 0 6px #7b7b7b;
|
box-shadow: 3px 0 6px #7b7b7b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grabber {
|
||||||
|
cursor: col-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.middle {
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
z-index: 100000;
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 90px;
|
||||||
|
border: solid 1px #d5d5d5;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
transition: width 150ms ease-in-out, height 150ms ease-in-out, border-radius 50ms ease-in-out, transform 50ms ease-in-out;
|
||||||
|
transform: translateX(0);
|
||||||
|
|
||||||
|
&.shift-right {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
transform: translateX(8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.shift-left {
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
transform: translateX(-8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover, &.open {
|
||||||
|
width: 135px;
|
||||||
|
height: 50px;
|
||||||
|
border-radius: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.btn) {
|
||||||
|
border-radius: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
height: 40px;
|
||||||
|
width: 3*40px;
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,24 +7,63 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import styles from './split-divider.module.scss'
|
import styles from './split-divider.module.scss'
|
||||||
import { testId } from '../../../../utils/test-id'
|
import { testId } from '../../../../utils/test-id'
|
||||||
|
import { Button } from 'react-bootstrap'
|
||||||
|
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
|
||||||
|
|
||||||
|
export enum DividerButtonsShift {
|
||||||
|
SHIFT_TO_LEFT = 'shift-left',
|
||||||
|
SHIFT_TO_RIGHT = 'shift-right',
|
||||||
|
NO_SHIFT = ''
|
||||||
|
}
|
||||||
|
|
||||||
export interface SplitDividerProps {
|
export interface SplitDividerProps {
|
||||||
onGrab: () => void
|
onGrab: () => void
|
||||||
|
onLeftButtonClick: () => void
|
||||||
|
onRightButtonClick: () => void
|
||||||
|
forceOpen: boolean
|
||||||
|
focusLeft: boolean
|
||||||
|
focusRight: boolean
|
||||||
|
dividerButtonsShift: DividerButtonsShift
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the divider between the two editor panes.
|
* Renders the divider between the two editor panes.
|
||||||
* This divider supports both mouse and touch interactions.
|
* This divider supports both mouse and touch interactions.
|
||||||
*
|
*
|
||||||
* @param onGrab The callback, that should be called if the splitter is grabbed.
|
* @param onGrab callback that is triggered if the splitter is grabbed.
|
||||||
|
* @param onLeftButtonClick callback that is triggered when the left arrow button is pressed
|
||||||
|
* @param onRightButtonClick callback that is triggered when the right arrow button is pressed
|
||||||
|
* @param dividerShift defines if the buttons should be shifted to the left or right side
|
||||||
|
* @param focusLeft defines if the left button should be focused
|
||||||
|
* @param focusRight defines if the right button should be focused
|
||||||
|
* @param forceOpen defines if the arrow buttons should always be visible
|
||||||
*/
|
*/
|
||||||
export const SplitDivider: React.FC<SplitDividerProps> = ({ onGrab }) => {
|
export const SplitDivider: React.FC<SplitDividerProps> = ({
|
||||||
|
onGrab,
|
||||||
|
onLeftButtonClick,
|
||||||
|
onRightButtonClick,
|
||||||
|
dividerButtonsShift,
|
||||||
|
focusLeft,
|
||||||
|
focusRight,
|
||||||
|
forceOpen
|
||||||
|
}) => {
|
||||||
|
const shiftClass = dividerButtonsShift == '' ? '' : styles[dividerButtonsShift]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={`${styles['split-divider']}`} {...testId('splitter-divider')}>
|
||||||
onMouseDown={onGrab}
|
<div className={`bg-light ${styles['middle']} ${forceOpen ? styles['open'] : ''} ${shiftClass}`}>
|
||||||
onTouchStart={onGrab}
|
<div className={styles['buttons']}>
|
||||||
className={styles['split-divider']}
|
<Button variant={focusLeft ? 'secondary' : 'light'} onClick={onLeftButtonClick}>
|
||||||
{...testId('splitter-divider')}
|
<ForkAwesomeIcon icon={'arrow-left'} />
|
||||||
/>
|
</Button>
|
||||||
|
<span onMouseDown={onGrab} onTouchStart={onGrab} className={styles['grabber']}>
|
||||||
|
<ForkAwesomeIcon icon={'arrows-h'} />
|
||||||
|
</span>
|
||||||
|
<Button variant={focusRight ? 'secondary' : 'light'} onClick={onRightButtonClick}>
|
||||||
|
<ForkAwesomeIcon icon={'arrow-right'} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,20 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
.left, .right {
|
||||||
.splitter {
|
overflow: hidden;
|
||||||
&.left {
|
}
|
||||||
min-width: 200px;
|
|
||||||
}
|
.inner {
|
||||||
|
display: flex;
|
||||||
&.separator {
|
min-width: 20vw;
|
||||||
display: flex;
|
height: 100%;
|
||||||
}
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.move-overlay {
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 100000;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,29 +9,24 @@ import { Splitter } from './splitter'
|
||||||
import { Mock } from 'ts-mockery'
|
import { Mock } from 'ts-mockery'
|
||||||
|
|
||||||
describe('Splitter', () => {
|
describe('Splitter', () => {
|
||||||
it('can render only the left pane', () => {
|
|
||||||
const view = render(<Splitter showLeft={true} showRight={false} left={<>left</>} right={<>right</>} />)
|
|
||||||
expect(view.container).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can render only the right pane', () => {
|
|
||||||
const view = render(<Splitter showLeft={false} showRight={true} left={<>left</>} right={<>right</>} />)
|
|
||||||
expect(view.container).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can render both panes', () => {
|
|
||||||
const view = render(<Splitter showLeft={true} showRight={true} left={<>left</>} right={<>right</>} />)
|
|
||||||
expect(view.container).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('resize', () => {
|
describe('resize', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
Object.defineProperty(window.HTMLDivElement.prototype, 'clientWidth', { value: 1920 })
|
Object.defineProperty(window.HTMLDivElement.prototype, 'clientWidth', { value: 1920 })
|
||||||
Object.defineProperty(window.HTMLDivElement.prototype, 'offsetLeft', { value: 0 })
|
Object.defineProperty(window.HTMLDivElement.prototype, 'offsetLeft', { value: 0 })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('can react to shortcuts', () => {
|
||||||
|
const view = render(<Splitter left={<>left</>} right={<>right</>} />)
|
||||||
|
fireEvent.keyDown(document, Mock.of<KeyboardEvent>({ ctrlKey: true, altKey: true, key: 'v' }))
|
||||||
|
expect(view.container).toMatchSnapshot()
|
||||||
|
fireEvent.keyDown(document, Mock.of<KeyboardEvent>({ ctrlKey: true, altKey: true, key: 'e' }))
|
||||||
|
expect(view.container).toMatchSnapshot()
|
||||||
|
fireEvent.keyDown(document, Mock.of<KeyboardEvent>({ ctrlKey: true, altKey: true, key: 'b' }))
|
||||||
|
expect(view.container).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
it('can change size with mouse', async () => {
|
it('can change size with mouse', async () => {
|
||||||
const view = render(<Splitter showLeft={true} showRight={true} left={<>left</>} right={<>right</>} />)
|
const view = render(<Splitter left={<>left</>} right={<>right</>} />)
|
||||||
expect(view.container).toMatchSnapshot()
|
expect(view.container).toMatchSnapshot()
|
||||||
const divider = await screen.findByTestId('splitter-divider')
|
const divider = await screen.findByTestId('splitter-divider')
|
||||||
|
|
||||||
|
@ -50,7 +45,7 @@ describe('Splitter', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can change size with touch', async () => {
|
it('can change size with touch', async () => {
|
||||||
const view = render(<Splitter showLeft={true} showRight={true} left={<>left</>} right={<>right</>} />)
|
const view = render(<Splitter left={<>left</>} right={<>right</>} />)
|
||||||
expect(view.container).toMatchSnapshot()
|
expect(view.container).toMatchSnapshot()
|
||||||
const divider = await screen.findByTestId('splitter-divider')
|
const divider = await screen.findByTestId('splitter-divider')
|
||||||
|
|
||||||
|
|
|
@ -4,20 +4,17 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ReactElement } from 'react'
|
import type { ReactElement, TouchEvent, MouseEvent } from 'react'
|
||||||
import React, { useCallback, useRef, useState } from 'react'
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { ShowIf } from '../../common/show-if/show-if'
|
import { DividerButtonsShift, SplitDivider } from './split-divider/split-divider'
|
||||||
import { SplitDivider } from './split-divider/split-divider'
|
|
||||||
import styles from './splitter.module.scss'
|
import styles from './splitter.module.scss'
|
||||||
import { useAdjustedRelativeSplitValue } from './hooks/use-adjusted-relative-split-value'
|
import { ShowIf } from '../../common/show-if/show-if'
|
||||||
import { useBindPointerMovementEventOnWindow } from '../../../hooks/common/use-bind-pointer-movement-event-on-window'
|
import { useKeyboardShortcuts } from './hooks/use-keyboard-shortcuts'
|
||||||
|
|
||||||
export interface SplitterProps {
|
export interface SplitterProps {
|
||||||
left?: ReactElement
|
left?: ReactElement
|
||||||
right?: ReactElement
|
right?: ReactElement
|
||||||
additionalContainerClassName?: string
|
additionalContainerClassName?: string
|
||||||
showLeft: boolean
|
|
||||||
showRight: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,7 +23,7 @@ export interface SplitterProps {
|
||||||
* @param event the event to check
|
* @param event the event to check
|
||||||
* @return {@link true} if the given event is a {@link MouseEvent}
|
* @return {@link true} if the given event is a {@link MouseEvent}
|
||||||
*/
|
*/
|
||||||
const isMouseEvent = (event: Event): event is MouseEvent => {
|
const isMouseEvent = (event: MouseEvent | TouchEvent): event is MouseEvent => {
|
||||||
return (event as MouseEvent).buttons !== undefined
|
return (event as MouseEvent).buttons !== undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,55 +45,61 @@ const extractHorizontalPosition = (moveEvent: MouseEvent | TouchEvent): number |
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SNAP_PERCENTAGE = 10
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Left/Right splitter react component.
|
* Creates a Left/Right splitter react component.
|
||||||
*
|
*
|
||||||
* @param additionalContainerClassName css classes that are added to the split container.
|
* @param additionalContainerClassName css classes that are added to the split container.
|
||||||
* @param left the react component that should be shown on the left side.
|
* @param left the react component that should be shown on the left side.
|
||||||
* @param right the react component that should be shown on the right side.
|
* @param right the react component that should be shown on the right side.
|
||||||
* @param showLeft defines if the left component should be shown or hidden. Settings this prop will hide the component with css.
|
|
||||||
* @param showRight defines if the right component should be shown or hidden. Settings this prop will hide the component with css.
|
|
||||||
* @return the created component
|
* @return the created component
|
||||||
*/
|
*/
|
||||||
export const Splitter: React.FC<SplitterProps> = ({
|
export const Splitter: React.FC<SplitterProps> = ({ additionalContainerClassName, left, right }) => {
|
||||||
additionalContainerClassName,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
showLeft,
|
|
||||||
showRight
|
|
||||||
}) => {
|
|
||||||
const [relativeSplitValue, setRelativeSplitValue] = useState(50)
|
const [relativeSplitValue, setRelativeSplitValue] = useState(50)
|
||||||
const adjustedRelativeSplitValue = useAdjustedRelativeSplitValue(showLeft, showRight, relativeSplitValue)
|
const [resizingInProgress, setResizingInProgress] = useState(false)
|
||||||
const resizingInProgress = useRef(false)
|
const adjustedRelativeSplitValue = useMemo(() => Math.min(100, Math.max(0, relativeSplitValue)), [relativeSplitValue])
|
||||||
const splitContainer = useRef<HTMLDivElement>(null)
|
const splitContainer = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the splitter resizing.
|
* Starts the splitter resizing.
|
||||||
*/
|
*/
|
||||||
const onStartResizing = useCallback(() => {
|
const onStartResizing = useCallback(() => {
|
||||||
resizingInProgress.current = true
|
setResizingInProgress(true)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops the splitter resizing.
|
* Stops the splitter resizing.
|
||||||
*/
|
*/
|
||||||
const onStopResizing = useCallback(() => {
|
const onStopResizing = useCallback(() => {
|
||||||
if (resizingInProgress.current) {
|
setResizingInProgress(false)
|
||||||
resizingInProgress.current = false
|
|
||||||
}
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!resizingInProgress) {
|
||||||
|
setRelativeSplitValue((value) => {
|
||||||
|
if (value < SNAP_PERCENTAGE) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if (value > 100 - SNAP_PERCENTAGE) {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [resizingInProgress])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recalculates the panel split based on the absolute mouse/touch position.
|
* Recalculates the panel split based on the absolute mouse/touch position.
|
||||||
*
|
*
|
||||||
* @param moveEvent is a {@link MouseEvent} or {@link TouchEvent} that got triggered.
|
* @param moveEvent is a {@link MouseEvent} or {@link TouchEvent} that got triggered.
|
||||||
*/
|
*/
|
||||||
const onMove = useCallback((moveEvent: MouseEvent | TouchEvent) => {
|
const onMove = useCallback((moveEvent: MouseEvent | TouchEvent) => {
|
||||||
if (!resizingInProgress.current || !splitContainer.current) {
|
if (!splitContainer.current) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (isMouseEvent(moveEvent) && !isLeftMouseButtonClicked(moveEvent)) {
|
if (isMouseEvent(moveEvent) && !isLeftMouseButtonClicked(moveEvent)) {
|
||||||
resizingInProgress.current = false
|
setResizingInProgress(false)
|
||||||
moveEvent.preventDefault()
|
moveEvent.preventDefault()
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
@ -107,28 +110,56 @@ export const Splitter: React.FC<SplitterProps> = ({
|
||||||
}
|
}
|
||||||
const horizontalPositionInSplitContainer = horizontalPosition - splitContainer.current.offsetLeft
|
const horizontalPositionInSplitContainer = horizontalPosition - splitContainer.current.offsetLeft
|
||||||
const newRelativeSize = horizontalPositionInSplitContainer / splitContainer.current.clientWidth
|
const newRelativeSize = horizontalPositionInSplitContainer / splitContainer.current.clientWidth
|
||||||
setRelativeSplitValue(newRelativeSize * 100)
|
const number = newRelativeSize * 100
|
||||||
|
setRelativeSplitValue(number)
|
||||||
moveEvent.preventDefault()
|
moveEvent.preventDefault()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useBindPointerMovementEventOnWindow(onMove, onStopResizing)
|
const onLeftButtonClick = useCallback(() => {
|
||||||
|
setRelativeSplitValue((value) => (value === 100 ? 50 : 0))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const onRightButtonClick = useCallback(() => {
|
||||||
|
setRelativeSplitValue((value) => (value === 0 ? 50 : 100))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const dividerButtonsShift = useMemo(() => {
|
||||||
|
if (relativeSplitValue === 0) {
|
||||||
|
return DividerButtonsShift.SHIFT_TO_RIGHT
|
||||||
|
} else if (relativeSplitValue === 100) {
|
||||||
|
return DividerButtonsShift.SHIFT_TO_LEFT
|
||||||
|
} else {
|
||||||
|
return DividerButtonsShift.NO_SHIFT
|
||||||
|
}
|
||||||
|
}, [relativeSplitValue])
|
||||||
|
|
||||||
|
useKeyboardShortcuts(setRelativeSplitValue)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={splitContainer} className={`flex-fill flex-row d-flex ${additionalContainerClassName || ''}`}>
|
<div ref={splitContainer} className={`flex-fill flex-row d-flex ${additionalContainerClassName || ''}`}>
|
||||||
<div
|
<ShowIf condition={resizingInProgress}>
|
||||||
className={`${styles['splitter']} ${styles['left']} ${!showLeft ? 'd-none' : ''}`}
|
<div
|
||||||
style={{ width: `calc(${adjustedRelativeSplitValue}% - 5px)` }}>
|
className={styles['move-overlay']}
|
||||||
{left}
|
onTouchMove={onMove}
|
||||||
</div>
|
onMouseMove={onMove}
|
||||||
<ShowIf condition={showLeft && showRight}>
|
onTouchCancel={onStopResizing}
|
||||||
<div className={`${styles['splitter']} ${styles['separator']}`}>
|
onTouchEnd={onStopResizing}
|
||||||
<SplitDivider onGrab={onStartResizing} />
|
onMouseUp={onStopResizing}></div>
|
||||||
</div>
|
|
||||||
</ShowIf>
|
</ShowIf>
|
||||||
<div
|
<div className={styles['left']} style={{ width: `calc(${adjustedRelativeSplitValue}% - 5px)` }}>
|
||||||
className={`${styles['splitter']}${!showRight ? ' d-none' : ''}`}
|
<div className={styles['inner']}>{left}</div>
|
||||||
style={{ width: `calc(100% - ${adjustedRelativeSplitValue}%)` }}>
|
</div>
|
||||||
{right}
|
<SplitDivider
|
||||||
|
onGrab={onStartResizing}
|
||||||
|
onLeftButtonClick={onLeftButtonClick}
|
||||||
|
onRightButtonClick={onRightButtonClick}
|
||||||
|
forceOpen={resizingInProgress}
|
||||||
|
focusLeft={relativeSplitValue < SNAP_PERCENTAGE}
|
||||||
|
focusRight={relativeSplitValue > 100 - SNAP_PERCENTAGE}
|
||||||
|
dividerButtonsShift={dividerButtonsShift}
|
||||||
|
/>
|
||||||
|
<div className={styles['right']} style={{ width: `calc(100% - ${adjustedRelativeSplitValue}%)` }}>
|
||||||
|
<div className={styles['inner']}>{right}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,13 +5,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { store } from '..'
|
import { store } from '..'
|
||||||
import type { EditorMode } from '../../components/editor-page/app-bar/editor-view-mode'
|
|
||||||
import type {
|
import type {
|
||||||
EditorConfig,
|
EditorConfig,
|
||||||
SetEditorLigaturesAction,
|
SetEditorLigaturesAction,
|
||||||
SetEditorSmartPasteAction,
|
SetEditorSmartPasteAction,
|
||||||
SetEditorSyncScrollAction,
|
SetEditorSyncScrollAction
|
||||||
SetEditorViewModeAction
|
|
||||||
} from './types'
|
} from './types'
|
||||||
import { EditorConfigActionType } from './types'
|
import { EditorConfigActionType } from './types'
|
||||||
import { Logger } from '../../utils/logger'
|
import { Logger } from '../../utils/logger'
|
||||||
|
@ -39,14 +37,6 @@ export const saveToLocalStorage = (editorConfig: EditorConfig): void => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setEditorMode = (editorMode: EditorMode): void => {
|
|
||||||
const action: SetEditorViewModeAction = {
|
|
||||||
type: EditorConfigActionType.SET_EDITOR_VIEW_MODE,
|
|
||||||
mode: editorMode
|
|
||||||
}
|
|
||||||
store.dispatch(action)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const setEditorSyncScroll = (syncScroll: boolean): void => {
|
export const setEditorSyncScroll = (syncScroll: boolean): void => {
|
||||||
const action: SetEditorSyncScrollAction = {
|
const action: SetEditorSyncScrollAction = {
|
||||||
type: EditorConfigActionType.SET_SYNC_SCROLL,
|
type: EditorConfigActionType.SET_SYNC_SCROLL,
|
||||||
|
|
|
@ -5,13 +5,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Reducer } from 'redux'
|
import type { Reducer } from 'redux'
|
||||||
import { EditorMode } from '../../components/editor-page/app-bar/editor-view-mode'
|
|
||||||
import { loadFromLocalStorage, saveToLocalStorage } from './methods'
|
import { loadFromLocalStorage, saveToLocalStorage } from './methods'
|
||||||
import type { EditorConfig, EditorConfigActions } from './types'
|
import type { EditorConfig, EditorConfigActions } from './types'
|
||||||
import { EditorConfigActionType } from './types'
|
import { EditorConfigActionType } from './types'
|
||||||
|
|
||||||
const initialState: EditorConfig = {
|
const initialState: EditorConfig = {
|
||||||
editorMode: EditorMode.BOTH,
|
|
||||||
ligatures: true,
|
ligatures: true,
|
||||||
syncScroll: true,
|
syncScroll: true,
|
||||||
smartPaste: true,
|
smartPaste: true,
|
||||||
|
@ -28,13 +26,6 @@ export const EditorConfigReducer: Reducer<EditorConfig, EditorConfigActions> = (
|
||||||
) => {
|
) => {
|
||||||
let newState: EditorConfig
|
let newState: EditorConfig
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case EditorConfigActionType.SET_EDITOR_VIEW_MODE:
|
|
||||||
newState = {
|
|
||||||
...state,
|
|
||||||
editorMode: action.mode
|
|
||||||
}
|
|
||||||
saveToLocalStorage(newState)
|
|
||||||
return newState
|
|
||||||
case EditorConfigActionType.SET_SYNC_SCROLL:
|
case EditorConfigActionType.SET_SYNC_SCROLL:
|
||||||
newState = {
|
newState = {
|
||||||
...state,
|
...state,
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Action } from 'redux'
|
import type { Action } from 'redux'
|
||||||
import type { EditorMode } from '../../components/editor-page/app-bar/editor-view-mode'
|
|
||||||
|
|
||||||
export enum EditorConfigActionType {
|
export enum EditorConfigActionType {
|
||||||
SET_EDITOR_VIEW_MODE = 'editor/view-mode/set',
|
SET_EDITOR_VIEW_MODE = 'editor/view-mode/set',
|
||||||
|
@ -16,7 +15,6 @@ export enum EditorConfigActionType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EditorConfig {
|
export interface EditorConfig {
|
||||||
editorMode: EditorMode
|
|
||||||
syncScroll: boolean
|
syncScroll: boolean
|
||||||
ligatures: boolean
|
ligatures: boolean
|
||||||
smartPaste: boolean
|
smartPaste: boolean
|
||||||
|
@ -27,7 +25,6 @@ export type EditorConfigActions =
|
||||||
| SetEditorSyncScrollAction
|
| SetEditorSyncScrollAction
|
||||||
| SetEditorLigaturesAction
|
| SetEditorLigaturesAction
|
||||||
| SetEditorSmartPasteAction
|
| SetEditorSmartPasteAction
|
||||||
| SetEditorViewModeAction
|
|
||||||
| SetSpellCheckAction
|
| SetSpellCheckAction
|
||||||
|
|
||||||
export interface SetEditorSyncScrollAction extends Action<EditorConfigActionType> {
|
export interface SetEditorSyncScrollAction extends Action<EditorConfigActionType> {
|
||||||
|
@ -45,11 +42,6 @@ export interface SetEditorSmartPasteAction extends Action<EditorConfigActionType
|
||||||
smartPaste: boolean
|
smartPaste: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SetEditorViewModeAction extends Action<EditorConfigActionType> {
|
|
||||||
type: EditorConfigActionType.SET_EDITOR_VIEW_MODE
|
|
||||||
mode: EditorMode
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SetSpellCheckAction extends Action<EditorConfigActionType> {
|
export interface SetSpellCheckAction extends Action<EditorConfigActionType> {
|
||||||
type: EditorConfigActionType.SET_SPELL_CHECK
|
type: EditorConfigActionType.SET_SPELL_CHECK
|
||||||
spellCheck: boolean
|
spellCheck: boolean
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue