Simplifying drag and drop for sorting with my own hook

This commit is contained in:
Jesús Espino 2021-04-07 22:48:18 +02:00
parent 13b8e40f98
commit f1673dcc85
5 changed files with 39 additions and 89 deletions

View file

@ -1,8 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useRef} from 'react'
import React from 'react'
import {FormattedMessage, injectIntl, IntlShape} from 'react-intl'
import {useDrag, useDrop} from 'react-dnd'
import {IPropertyTemplate} from '../../blocks/board'
import {Card} from '../../blocks/card'
@ -16,6 +15,7 @@ import DuplicateIcon from '../../widgets/icons/duplicate'
import OptionsIcon from '../../widgets/icons/options'
import Menu from '../../widgets/menu'
import MenuWrapper from '../../widgets/menuWrapper'
import useSortable from '../../hooks/sortable'
import ImageElement from '../content/imageElement'
import ContentElement from '../content/contentElement'
@ -35,23 +35,7 @@ type Props = {
const GalleryCard = React.memo((props: Props) => {
const {cardTree} = props
const cardRef = useRef<HTMLDivElement>(null)
const [{isDragging}, drag] = useDrag(() => ({
type: 'card',
item: cardTree.card,
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
}), [cardTree, props.onDrop])
const [{isOver}, drop] = useDrop(() => ({
accept: 'card',
collect: (monitor) => ({
isOver: monitor.isOver(),
}),
drop: (item: Card) => {
props.onDrop(item, cardTree.card)
},
}), [cardTree, props.onDrop])
const [isDragging, isOver, cardRef] = useSortable('card', cardTree.card, props.onDrop)
const visiblePropertyTemplates = props.visiblePropertyTemplates || []
@ -62,8 +46,6 @@ const GalleryCard = React.memo((props: Props) => {
className += ' dragover'
}
drop(drag(cardRef))
return (
<div
className={className}

View file

@ -1,8 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useRef} from 'react'
import React from 'react'
import {injectIntl, IntlShape} from 'react-intl'
import {useDrag, useDrop} from 'react-dnd'
import {IPropertyTemplate} from '../../blocks/board'
import {Card} from '../../blocks/card'
@ -13,6 +12,7 @@ import DuplicateIcon from '../../widgets/icons/duplicate'
import OptionsIcon from '../../widgets/icons/options'
import Menu from '../../widgets/menu'
import MenuWrapper from '../../widgets/menuWrapper'
import useSortable from '../../hooks/sortable'
import './kanbanCard.scss'
import PropertyValueElement from '../propertyValueElement'
@ -28,33 +28,14 @@ type Props = {
}
const KanbanCard = React.memo((props: Props) => {
const cardRef = useRef<HTMLDivElement>(null)
const {card, intl} = props
const [{isDragging}, drag] = useDrag(() => ({
type: 'card',
item: card,
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
}), [card])
const [{isOver}, drop] = useDrop(() => ({
accept: 'card',
collect: (monitor) => ({
isOver: monitor.isOver(),
}),
drop: (item: Card) => {
props.onDrop(item, card)
},
}), [card, props.onDrop])
const [isDragging, isOver, cardRef] = useSortable('card', card, props.onDrop)
const visiblePropertyTemplates = props.visiblePropertyTemplates || []
let className = props.isSelected ? 'KanbanCard selected' : 'KanbanCard'
if (isOver) {
className += ' dragover'
}
drop(drag(cardRef))
return (
<div
ref={props.readonly ? () => null : cardRef}

View file

@ -10,6 +10,7 @@ import SortDownIcon from '../../widgets/icons/sortDown'
import SortUpIcon from '../../widgets/icons/sortUp'
import MenuWrapper from '../../widgets/menuWrapper'
import Label from '../../widgets/label'
import useSortable from '../../hooks/sortable'
import HorizontalGrip from './horizontalGrip'
@ -23,38 +24,16 @@ type Props = {
boardTree: BoardTree
template: IPropertyTemplate
offset: number
onDrop?: (template: IPropertyTemplate, container: IPropertyTemplate) => void
onDrop: (template: IPropertyTemplate, container: IPropertyTemplate) => void
}
const TableHeader = React.memo((props: Props): JSX.Element => {
const columnRef = useRef<HTMLDivElement>(null)
const [{isDragging}, drag] = useDrag(() => ({
type: 'column',
item: props.template,
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
}))
const [{isOver}, drop] = useDrop(() => ({
accept: 'column',
collect: (monitor) => ({
isOver: monitor.isOver(),
}),
drop: (item: IPropertyTemplate) => {
props.onDrop && props.onDrop(item, props.template)
},
}))
const [isDragging, isOver, columnRef] = useSortable('column', props.template, props.onDrop)
const columnWidth = (templateId: string): number => {
return Math.max(Constants.minColumnWidth, (props.boardTree.activeView.columnWidths[templateId] || 0) + props.offset)
}
if (props.template.id === Constants.titleColumnId) {
drop(columnRef)
} else {
drop(drag(columnRef))
}
let className = 'octo-table-cell header-cell'
if (isOver) {
className += ' dragover'
@ -64,7 +43,7 @@ const TableHeader = React.memo((props: Props): JSX.Element => {
<div
className={className}
style={{overflow: 'unset', width: columnWidth(props.template.id), opacity: isDragging ? 0.5 : 1}}
ref={props.onDrop ? columnRef : () => null}
ref={props.template.id === Constants.titleColumnId ? () => null : columnRef}
>
<MenuWrapper disabled={props.readonly}>
<Label>

View file

@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import React, {useState, useRef, useEffect} from 'react'
import {FormattedMessage} from 'react-intl'
import {useDrop, useDrag} from 'react-dnd'
import {Card} from '../../blocks/card'
import {Constants} from '../../constants'
@ -10,6 +9,7 @@ import mutator from '../../mutator'
import {BoardTree} from '../../viewModel/boardTree'
import Button from '../../widgets/buttons/button'
import Editable from '../../widgets/editable'
import useSortable from '../../hooks/sortable'
import PropertyValueElement from '../propertyValueElement'
import './tableRow.scss'
@ -29,25 +29,8 @@ type Props = {
const TableRow = React.memo((props: Props) => {
const titleRef = useRef<Editable>(null)
const [title, setTitle] = useState(props.card.title)
const cardRef = useRef<HTMLDivElement>(null)
const {card} = props
const [{isDragging}, drag] = useDrag(() => ({
type: 'card',
item: card,
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
}), [card])
const [{isOver}, drop] = useDrop(() => ({
accept: 'card',
collect: (monitor) => ({
isOver: monitor.isOver(),
}),
drop: (item: Card) => {
props.onDrop(item, card)
},
}), [card, props.onDrop])
const [isDragging, isOver, cardRef] = useSortable('card', card, props.onDrop)
useEffect(() => {
if (props.focusOnMount) {
@ -67,8 +50,6 @@ const TableRow = React.memo((props: Props) => {
className += ' dragover'
}
drop(drag(cardRef))
return (
<div
className={className}

View file

@ -0,0 +1,27 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useRef} from 'react'
import {useDrag, useDrop} from 'react-dnd'
export default function useSortable(itemType: string, item: any, handler: (src: any, st: any) => void): [boolean, boolean, React.RefObject<HTMLDivElement>] {
const ref = useRef<HTMLDivElement>(null)
const [{isDragging}, drag] = useDrag(() => ({
type: itemType,
item,
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
}), [itemType, item])
const [{isOver}, drop] = useDrop(() => ({
accept: itemType,
collect: (monitor) => ({
isOver: monitor.isOver(),
}),
drop: (dragItem: any) => {
handler(dragItem, item)
},
}), [item, handler])
drop(drag(ref))
return [isDragging, isOver, ref]
}