mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-03-25 05:35:03 +00: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-scripts": "3.4.1",
|
||||
"redux": "4.0.5",
|
||||
"typescript": "3.9.5"
|
||||
"typescript": "3.9.5",
|
||||
"use-media": "1.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"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 useMedia from 'use-media'
|
||||
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 { MarkdownPreview } from './markdown-preview/markdown-preview'
|
||||
import { EditorMode } from './task-bar/editor-view-mode'
|
||||
|
@ -13,18 +15,28 @@ interface RouteParameters {
|
|||
|
||||
const Editor: React.FC = () => {
|
||||
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 (
|
||||
<div className={'d-flex flex-column vh-100'}>
|
||||
<TaskBar/>
|
||||
<div className={'flex-fill flex-row d-flex'}>
|
||||
<ShowIf condition={editorMode === EditorMode.EDITOR || editorMode === EditorMode.BOTH}>
|
||||
<EditorWindow/>
|
||||
</ShowIf>
|
||||
<ShowIf condition={editorMode === EditorMode.PREVIEW || editorMode === EditorMode.BOTH}>
|
||||
<MarkdownPreview/>
|
||||
</ShowIf>
|
||||
</div>
|
||||
<Splitter
|
||||
showLeft={editorMode === EditorMode.EDITOR || editorMode === EditorMode.BOTH}
|
||||
left={<EditorWindow/>}
|
||||
showRight={editorMode === EditorMode.PREVIEW || (editorMode === EditorMode.BOTH)}
|
||||
right={<MarkdownPreview/>}
|
||||
containerClassName={'overflow-hidden'}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||
|
||||
const MarkdownPreview: React.FC = () => {
|
||||
return (
|
||||
<div style={{ backgroundColor: 'red' }}>
|
||||
<div className='h-100 px-2 py-1 bg-white'>
|
||||
Hello, MarkdownPreview!
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -19,7 +19,7 @@ export const EditorViewMode: React.FC = () => {
|
|||
<ToggleButtonGroup
|
||||
type="radio"
|
||||
name="options"
|
||||
defaultValue={editorConfig.editorMode}
|
||||
value={editorConfig.editorMode}
|
||||
onChange={(value: EditorMode) => {
|
||||
setEditorModeConfig(value)
|
||||
}}>
|
||||
|
|
|
@ -11319,6 +11319,11 @@ url@^0.11.0:
|
|||
punycode "1.3.2"
|
||||
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:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||
|
|
Loading…
Reference in a new issue