import * as React from "react" ; import { Title } from "react-head" ; import { DragDropContext , Droppable } from "react-beautiful-dnd" ; import classnames from "classnames" ; import List from "../List/List" ; import ListAdder from "../ListAdder/ListAdder" ; import Header from "../Header/Header" ; import BoardHeader from "../BoardHeader/BoardHeader" ; import "./Board.scss" ; import { Board as _Board } from "../../types" ; import { state , dispatch , db } from "../../model" ; import Spinner from "../Spinner/Spinner" ; import NotFound from "../NotFound/NotFound" ; type Props = { boardId : string ; } ; function setCurrentBoard ( boardId ) { state . currentBoardId = boardId ; } const moveList = async ( boardId , oldListIndex , newListIndex ) => { const board = await db . boardsById . get ( boardId ) ; const newLists = board . lists ; const [ removedList ] = newLists . splice ( oldListIndex , 1 ) ; newLists . splice ( newListIndex , 0 , removedList ) ; db . boardsById . update ( boardId , { lists : newLists } ) ; } ; const moveCard = async ( oldCardIndex , newCardIndex , sourceListId , destListId ) => { if ( sourceListId === destListId ) { const list = await db . listsById . get ( sourceListId ) ; const newCards = list . cards ; const [ removedCard ] = newCards . splice ( oldCardIndex , 1 ) ; newCards . splice ( newCardIndex , 0 , removedCard ) ; db . listsById . set ( sourceListId , { ... list , cards : newCards } ) ; } else { const source = await db . listsById . get ( sourceListId ) ; const sourceCards = source . cards ; const [ removedCard ] = sourceCards . splice ( oldCardIndex , 1 ) ; const dest = await db . listsById . get ( destListId ) ; const destinationCards = dest . cards ; destinationCards . splice ( newCardIndex , 0 , removedCard ) ; db . listsById . set ( sourceListId , { ... source , cards : sourceCards } ) ; db . listsById . set ( destListId , { ... dest , cards : destinationCards } ) ; } } ; function Board ( { boardId } ) { const boardQ = db . boardsById . watch ( boardId ) ; if ( boardQ . _fetching ) return < Spinner />; if ( boardQ . _notFound ) return < NotFound missing = { `boardsById.${ boardId }` } />; const board = boardQ . data ; const boardTitle = board . title ; const boardColor = board . color ; const lists = board . lists . map ( listId => db . listsById . watch ( listId ) ) ; const [ localState , setLocalState ] = React . useState ( { startX : null , startScrollX : null , } ) ; React . useEffect ( ( ) => { dispatch ( setCurrentBoard ) ( boardId ) ; } , [ board ] ) ; const handleDragEnd = ( { source , destination , type } ) => { if ( ! destination ) { return ; } if ( type === "COLUMN" ) { if ( source . index !== destination . index ) { dispatch ( moveList ) ( source . droppableId , source . index , destination . index ) ; } return ; } if ( source . index !== destination . index || source . droppableId !== destination . droppableId ) { dispatch ( moveCard ) ( source . index , destination . index , source . droppableId , destination . droppableId ) ; } } ; const handleMouseDown = ( { target , clientX } ) => { if ( target . className !== "list-wrapper" && target . className !== "lists" ) { return ; } window . addEventListener ( "mousemove" , handleMouseMove ) ; window . addEventListener ( "mouseup" , handleMouseUp ) ; setLocalState ( { startX : clientX , startScrollX : window . scrollX , } ) ; } ; const handleMouseMove = ( { clientX } ) => { const { startX , startScrollX } = localState ; const scrollX = startScrollX - clientX + startX ; window . scrollTo ( scrollX , 0 ) ; const windowScrollX = window . scrollX ; if ( scrollX !== windowScrollX ) { setLocalState ( { ... localState , startX : clientX + windowScrollX - startScrollX , } ) ; } } ; const handleMouseUp = ( ) => { if ( localState . startX ) { window . removeEventListener ( "mousemove" , handleMouseMove ) ; window . removeEventListener ( "mouseup" , handleMouseUp ) ; setLocalState ( { startX : null , startScrollX : null } ) ; } } ; const handleWheel = ( { target , deltaY } ) => { if ( target . className !== "list-wrapper" && target . className !== "lists" && target . className !== "open-composer-button" && target . className !== "list-title-button" ) { return ; } if ( Math . sign ( deltaY ) === 1 ) { window . scrollTo ( window . scrollX + 80 , 0 ) ; } else if ( Math . sign ( deltaY ) === - 1 ) { window . scrollTo ( window . scrollX - 80 , 0 ) ; } } ; return ( < > < div className = { classnames ( "board" , boardColor ) } > < Title > { boardTitle } | React Kanban < /Title> < Header / > < BoardHeader /> < div className = "lists-wrapper" onMouseDown = { handleMouseDown } onWheel = { handleWheel } > < DragDropContext onDragEnd = { handleDragEnd as any } > < Droppable droppableId = { boardId } type = "COLUMN" direction = "horizontal" > { provided => ( < div className = "lists" ref = { provided . innerRef } > { lists . map ( ( list , index ) => { if ( list . _fetching ) return < Spinner />; if ( list . _notFound ) return < NotFound missing = { `listsById.?` } />; return ( < List list = { list . data } boardId = { boardId } index = { index } key = { list . data . id } / > ) ; } ) } { provided . placeholder } < ListAdder boardId = { boardId } /> < /div>)} < /Droppable> < /DragDropContext> < /div> < div className = "board-underlay" / > < /div> < />); } export default Board ;