import React from "react" ; import { BlockEditor } from "./BlockEditor" ; import { BlockItems } from "./BlockItems" ; import { BlockBulletButton } from "./BlockBulletButton" ; import { createContextMenu } from "../../../apps/repository/js/doc_repo/MUIContextMenu2" ; import { IDocViewerContextMenuOrigin } from "../../../apps/doc/src/DocViewerMenu" ; import { BlockContextMenuItems } from "./BlockContextMenuItems" ; import useTheme from "@material-ui/core/styles/useTheme" ; import { BlockExpandToggleButton } from "./BlockExpandToggleButton" ; import { BlockIDStr , useBlocksStore } from "./store/BlocksStore" ; import { observer , } from "mobx-react-lite" ; import makeStyles from "@material-ui/core/styles/makeStyles" ; import createStyles from "@material-ui/core/styles/createStyles" ; import clsx from "clsx" ; import { BlockDragIndicator } from "./BlockDragIndicator" ; import { BlockImageContent } from "./blocks/BlockImageContent" ; import { BlockPredicates } from "./store/BlockPredicates" ; import { useUndoQueue } from "../undo/UndoQueueProvider2" ; const useStyles = makeStyles ( ( theme ) => createStyles ( { selected : { background : theme . palette . primary . main } , } ) ) ; interface IProps { readonly parent : BlockIDStr | undefined ; readonly id : BlockIDStr ; } export interface IBlockContextMenuOrigin { } export const [ BlockContextMenu , useBlockContextMenu ] = createContextMenu < IDocViewerContextMenuOrigin > ( BlockContextMenuItems , { name : 'notes' } ) ; export const BlockInner = observer ( ( props ) => { const { id } = props ; const blocksStore = useBlocksStore ( ) ; const classes = useStyles ( ) ; const undoQueue = useUndoQueue ( ) ; const theme = useTheme ( ) ; const contextMenuHandlers = useBlockContextMenu ( ) ; const expanded = blocksStore . isExpanded ( id ) ; const selected = blocksStore . isSelected ( id ) ; const block = blocksStore . getBlock ( id ) ; const divRef = React . useRef < HTMLDivElement | null > ( null ) ; const dragActive = React . useRef < boolean > ( false ) ; const root = blocksStore . root ; const handleMouseDown = React . useCallback ( ( event ) => { if ( event . shiftKey ) { if ( blocksStore . active !== undefined ) { if ( blocksStore . active ?. id !== id ) { blocksStore . setSelectionRange ( blocksStore . active . id , id ) ; window . getSelection ( ) ! . removeAllRanges ( ) ; } } event . stopPropagation ( ) ; } else { blocksStore . clearSelected ( 'Note: handleMouseDown' ) ; } } , [ id , blocksStore ] ) ; if ( ! block ) { return null ; } const handleKeyDown = React . useCallback ( ( event ) => { function abortEvent ( ) { event . preventDefault ( ) ; event . stopPropagation ( ) ; } function computeUndoOperation ( ) { if ( event . metaKey && event . key === 'z' ) { return 'undo' ; } if ( event . metaKey && event . shiftKey && event . key === 'z' ) { abortEvent ( ) ; return 'redo' ; } if ( event . metaKey && event . key === 'Backspace' ) { abortEvent ( ) ; return 'undo' ; } if ( event . ctrlKey && event . key === 'z' ) { abortEvent ( ) ; return 'undo' ; } if ( event . ctrlKey && event . key === 'y' ) { return 'redo' ; } if ( event . ctrlKey && event . shiftKey && event . key === 'z' ) { return 'redo' ; } return undefined ; } const op = computeUndoOperation ( ) ; if ( op !== undefined ) { switch ( op ) { case "undo" : undoQueue . undo ( ) ; break ; case "redo" : undoQueue . redo ( ) ; break ; } abortEvent ( ) ; } } , [ undoQueue ] ) ; const handleDragStart = React . useCallback ( ( event ) => { blocksStore . setDropSource ( props . id ) ; } , [ props . id , blocksStore ] ) ; const computeDragPosition = React . useCallback ( ( event ) => { if ( divRef . current ) { const bcr = divRef . current . getBoundingClientRect ( ) ; const deltaTop = Math . abs ( event . clientY - bcr . top ) ; const deltaBottom = Math . abs ( event . clientY - bcr . bottom ) ; if ( deltaTop < deltaBottom ) { return 'top' ; } } return 'bottom' ; } , [ ] ) ; const updateDropTarget = React . useCallback ( ( event ) => { const pos = computeDragPosition ( event ) ; blocksStore . setDropTarget ( { id : props . id , pos } ) ; } , [ computeDragPosition , props . id , blocksStore ] ) ; const handleDragEnter = React . useCallback ( ( event ) => { updateDropTarget ( event ) ; dragActive . current = true ; event . preventDefault ( ) ; event . stopPropagation ( ) ; } , [ updateDropTarget ] ) ; const handleDragExit = React . useCallback ( ( event ) => { dragActive . current = false ; event . preventDefault ( ) ; event . stopPropagation ( ) ; } , [ ] ) ; const handleMouseMove = React . useCallback ( ( event ) => { if ( dragActive . current ) { updateDropTarget ( event ) ; } } , [ updateDropTarget ] ) ; const handleDrop = React . useCallback ( ( event ) => { event . preventDefault ( ) ; console . log ( "FIXME: DROP" ) ; } , [ ] ) ; const items = blocksStore . lookup ( block . itemsAsArray || [ ] ) ; const hasItems = items . length > 0 ; const dropActive = blocksStore . dropTarget ?. id === props . id && blocksStore . dropSource !== props . id ; return ( < div ref = { divRef } onMouseDown = { handleMouseDown } onMouseMove = { handleMouseMove } onKeyDown = { handleKeyDown } onDragStart = { event => handleDragStart ( event ) } onDragEnter = { event => handleDragEnter ( event ) } onDragLeave = { event => handleDragExit ( event ) } onDragEnd = { ( ) => blocksStore . clearDrop ( ) } onDrop = { event => handleDrop ( event ) } className = { clsx ( [ 'Block' , selected ? classes . selected : undefined ] ) } > < BlockDragIndicator id = { props . id } > < > < div { ... contextMenuHandlers } style = { { display : 'flex' , alignItems : 'flex-start' , boxSizing : 'border-box' , paddingTop : theme . spacing ( 0.5 ) , paddingBottom : theme . spacing ( 0.5 ) , } } > < div style = { { display : 'flex' , alignItems : 'center' , minWidth : '3em' , justifyContent : 'flex-end' , marginRight : theme . spacing ( 0.5 ) } } > { } { hasItems && id !== root && ( < BlockExpandToggleButton id = { props . id } />)} < BlockBulletButton target = { props . id } / > < /div> { BlockPredicates . isTextBlock ( block ) && ( < BlockEditor key = { props . id } parent = { props . parent } id = { props . id } / > ) } { block . content . type === 'image' && ( < BlockImageContent src = { block . content . src } width = { block . content . width } height = { block . content . height } / > ) } < /div> { ( expanded || id === root ) && ( < BlockItems parent = { props . id } notes = { items } / > ) } < /> < /BlockDragIndicator> < /div>); } ) ; export const Block = observer ( function Note ( props ) { return ( < BlockContextMenu > < BlockInner { ... props } / > < /BlockContextMenu>); } ) ;