mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-15 23:54:42 -04:00
Add editor split component (#198)
Add split and split divider component
This commit is contained in:
parent
c679f5524c
commit
f298d1469b
9 changed files with 130 additions and 13 deletions
|
@ -48,7 +48,8 @@
|
||||||
"react-router-dom": "5.2.0",
|
"react-router-dom": "5.2.0",
|
||||||
"react-scripts": "3.4.1",
|
"react-scripts": "3.4.1",
|
||||||
"redux": "4.0.5",
|
"redux": "4.0.5",
|
||||||
"typescript": "3.9.5"
|
"typescript": "3.9.5",
|
||||||
|
"use-media": "1.4.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
|
|
7
src/components/common/split-divider/split-divider.scss
Normal file
7
src/components/common/split-divider/split-divider.scss
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.split-divider {
|
||||||
|
width: 10px;
|
||||||
|
background: white;
|
||||||
|
z-index: 1;
|
||||||
|
cursor: col-resize;
|
||||||
|
box-shadow: 3px 0 6px #e7e7e7;
|
||||||
|
}
|
15
src/components/common/split-divider/split-divider.tsx
Normal file
15
src/components/common/split-divider/split-divider.tsx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import React from 'react'
|
||||||
|
import './split-divider.scss'
|
||||||
|
|
||||||
|
export interface SplitDividerProps {
|
||||||
|
onGrab: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SplitDivider: React.FC<SplitDividerProps> = ({ onGrab }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onMouseDown={() => onGrab()}
|
||||||
|
onTouchStart={() => onGrab()}
|
||||||
|
className={'split-divider'}/>
|
||||||
|
)
|
||||||
|
}
|
14
src/components/common/splitter/splitter.scss
Normal file
14
src/components/common/splitter/splitter.scss
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
.splitter {
|
||||||
|
&.left {
|
||||||
|
flex: 0 1 100%;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right {
|
||||||
|
flex: 1 0 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.separator {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
63
src/components/common/splitter/splitter.tsx
Normal file
63
src/components/common/splitter/splitter.tsx
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import React, { ReactElement, useRef, useState } from 'react'
|
||||||
|
import { ShowIf } from '../show-if/show-if'
|
||||||
|
import { SplitDivider } from '../split-divider/split-divider'
|
||||||
|
import './splitter.scss'
|
||||||
|
|
||||||
|
export interface SplitterProps {
|
||||||
|
left: ReactElement
|
||||||
|
right: ReactElement
|
||||||
|
containerClassName?: string
|
||||||
|
showLeft: boolean
|
||||||
|
showRight: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Splitter: React.FC<SplitterProps> = ({ containerClassName, left, right, showLeft, showRight }) => {
|
||||||
|
const [split, setSplit] = useState(50)
|
||||||
|
const realSplit = Math.max(0, Math.min(100, (showRight ? split : 100)))
|
||||||
|
const [doResizing, setDoResizing] = useState(false)
|
||||||
|
const splitContainer = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
const recalculateSize = (mouseXPosition: number): void => {
|
||||||
|
if (!splitContainer.current) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const x = mouseXPosition - splitContainer.current.offsetLeft
|
||||||
|
|
||||||
|
const newSize = x / splitContainer.current.clientWidth
|
||||||
|
setSplit(newSize * 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={splitContainer} className={`flex-fill flex-row d-flex ${containerClassName || ''}`}
|
||||||
|
onMouseUp={() => setDoResizing(false)}
|
||||||
|
onTouchEnd={() => setDoResizing(false)}
|
||||||
|
onMouseMove={(mouseEvent) => {
|
||||||
|
if (doResizing) {
|
||||||
|
recalculateSize(mouseEvent.pageX)
|
||||||
|
mouseEvent.preventDefault()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onTouchMove={(touchEvent) => {
|
||||||
|
if (doResizing) {
|
||||||
|
recalculateSize(touchEvent.touches[0].pageX)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ShowIf condition={showLeft}>
|
||||||
|
<div className={'splitter left'} style={{ flexBasis: `calc(${realSplit}% - 5px)` }}>
|
||||||
|
{left}
|
||||||
|
</div>
|
||||||
|
</ShowIf>
|
||||||
|
<ShowIf condition={showLeft && showRight}>
|
||||||
|
<div className='splitter separator'>
|
||||||
|
<SplitDivider onGrab={() => setDoResizing(true)}/>
|
||||||
|
</div>
|
||||||
|
</ShowIf>
|
||||||
|
<ShowIf condition={showRight}>
|
||||||
|
<div className='splitter right'>
|
||||||
|
{right}
|
||||||
|
</div>
|
||||||
|
</ShowIf>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
import React from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
import useMedia from 'use-media'
|
||||||
import { ApplicationState } from '../../redux'
|
import { ApplicationState } from '../../redux'
|
||||||
import { ShowIf } from '../common/show-if/show-if'
|
import { setEditorModeConfig } from '../../redux/editor/methods'
|
||||||
|
import { Splitter } from '../common/splitter/splitter'
|
||||||
import { EditorWindow } from './editor-window/editor-window'
|
import { EditorWindow } from './editor-window/editor-window'
|
||||||
import { MarkdownPreview } from './markdown-preview/markdown-preview'
|
import { MarkdownPreview } from './markdown-preview/markdown-preview'
|
||||||
import { EditorMode } from './task-bar/editor-view-mode'
|
import { EditorMode } from './task-bar/editor-view-mode'
|
||||||
|
@ -13,18 +15,28 @@ interface RouteParameters {
|
||||||
|
|
||||||
const Editor: React.FC = () => {
|
const Editor: React.FC = () => {
|
||||||
const editorMode: EditorMode = useSelector((state: ApplicationState) => state.editorConfig.editorMode)
|
const editorMode: EditorMode = useSelector((state: ApplicationState) => state.editorConfig.editorMode)
|
||||||
|
const isWide = useMedia({ minWidth: 576 })
|
||||||
|
const [firstDraw, setFirstDraw] = useState(true)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFirstDraw(false)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!firstDraw && !isWide && editorMode === EditorMode.BOTH) {
|
||||||
|
setEditorModeConfig(EditorMode.PREVIEW)
|
||||||
|
}
|
||||||
|
}, [editorMode, firstDraw, isWide])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'d-flex flex-column vh-100'}>
|
<div className={'d-flex flex-column vh-100'}>
|
||||||
<TaskBar/>
|
<TaskBar/>
|
||||||
<div className={'flex-fill flex-row d-flex'}>
|
<Splitter
|
||||||
<ShowIf condition={editorMode === EditorMode.EDITOR || editorMode === EditorMode.BOTH}>
|
showLeft={editorMode === EditorMode.EDITOR || editorMode === EditorMode.BOTH}
|
||||||
<EditorWindow/>
|
left={<EditorWindow/>}
|
||||||
</ShowIf>
|
showRight={editorMode === EditorMode.PREVIEW || (editorMode === EditorMode.BOTH)}
|
||||||
<ShowIf condition={editorMode === EditorMode.PREVIEW || editorMode === EditorMode.BOTH}>
|
right={<MarkdownPreview/>}
|
||||||
<MarkdownPreview/>
|
containerClassName={'overflow-hidden'}/>
|
||||||
</ShowIf>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
||||||
|
|
||||||
const MarkdownPreview: React.FC = () => {
|
const MarkdownPreview: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<div style={{ backgroundColor: 'red' }}>
|
<div className='h-100 px-2 py-1 bg-white'>
|
||||||
Hello, MarkdownPreview!
|
Hello, MarkdownPreview!
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -19,7 +19,7 @@ export const EditorViewMode: React.FC = () => {
|
||||||
<ToggleButtonGroup
|
<ToggleButtonGroup
|
||||||
type="radio"
|
type="radio"
|
||||||
name="options"
|
name="options"
|
||||||
defaultValue={editorConfig.editorMode}
|
value={editorConfig.editorMode}
|
||||||
onChange={(value: EditorMode) => {
|
onChange={(value: EditorMode) => {
|
||||||
setEditorModeConfig(value)
|
setEditorModeConfig(value)
|
||||||
}}>
|
}}>
|
||||||
|
|
|
@ -11319,6 +11319,11 @@ url@^0.11.0:
|
||||||
punycode "1.3.2"
|
punycode "1.3.2"
|
||||||
querystring "0.2.0"
|
querystring "0.2.0"
|
||||||
|
|
||||||
|
use-media@1.4.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/use-media/-/use-media-1.4.0.tgz#e777bf1f382a7aacabbd1f9ce3da2b62e58b2a98"
|
||||||
|
integrity sha512-XsgyUAf3nhzZmEfhc5MqLHwyaPjs78bgytpVJ/xDl0TF4Bptf3vEpBNBBT/EIKOmsOc8UbuECq3mrP3mt1QANA==
|
||||||
|
|
||||||
use@^3.1.0:
|
use@^3.1.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue