Skip to content

Commit

Permalink
Merge pull request #1819 from ehahn9/master
Browse files Browse the repository at this point in the history
fix: switch DnD to modern context API (was legacy)
  • Loading branch information
ehahn9 authored Mar 3, 2021
2 parents 02bbeb1 + 4574ab7 commit 611a0ce
Show file tree
Hide file tree
Showing 10 changed files with 4,459 additions and 2,057 deletions.
4 changes: 2 additions & 2 deletions examples/demos/dnd.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class Dnd extends React.Component {

const nextEvents = events.map(existingEvent => {
return existingEvent.id == event.id
? { ...existingEvent, start, end }
? { ...existingEvent, start, end, allDay }
: existingEvent
})

Expand All @@ -82,7 +82,7 @@ class Dnd extends React.Component {
//alert(`${event.title} was resized to ${start}-${end}`)
}

newEvent(event) {
newEvent(_event) {
// let idList = this.state.events.map(a => a.id)
// let newId = Math.max(...idList) + 1
// let hour = {
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@
"@4c/rollout": "^1.4.0",
"@babel/cli": "^7.1.0",
"@babel/core": "^7.1.5",
"@storybook/addon-actions": "^5.0.11",
"@storybook/react": "^5.0.11",
"@storybook/addon-actions": "^6.1.15",
"@storybook/react": "^6.1.15",
"autoprefixer": "^9.5.1",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.0.1",
Expand Down Expand Up @@ -130,7 +130,7 @@
"dependencies": {
"@babel/runtime": "^7.1.5",
"clsx": "^1.0.4",
"date-arithmetic": "^4.0.1",
"date-arithmetic": "^4.1.0",
"dom-helpers": "^5.1.0",
"invariant": "^2.2.4",
"lodash": "^4.17.11",
Expand Down
3 changes: 3 additions & 0 deletions src/addons/dragAndDrop/DnDContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import React from 'react'

export const DnDContext = React.createContext()
83 changes: 30 additions & 53 deletions src/addons/dragAndDrop/EventContainerWrapper.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
import PropTypes from 'prop-types'
import React from 'react'
import * as dates from '../../utils/dates'
import { findDOMNode } from 'react-dom'
import { DnDContext } from './DnDContext'

import Selection, {
getBoundsForNode,
getEventNodeFromPoint,
} from '../../Selection'
import TimeGridEvent from '../../TimeGridEvent'
import { dragAccessors } from './common'
import { dragAccessors, eventTimes, pointInColumn } from './common'
import NoopWrapper from '../../NoopWrapper'

const pointInColumn = (bounds, { x, y }) => {
const { left, right, top } = bounds
return x < right + 10 && x > left && y > top
}
const propTypes = {}

class EventContainerWrapper extends React.Component {
static propTypes = {
accessors: PropTypes.object.isRequired,
Expand All @@ -27,20 +21,12 @@ class EventContainerWrapper extends React.Component {
resource: PropTypes.any,
}

static contextTypes = {
draggable: PropTypes.shape({
onStart: PropTypes.func,
onEnd: PropTypes.func,
onDropFromOutside: PropTypes.func,
onBeginAction: PropTypes.func,
dragAndDropAction: PropTypes.object,
dragFromOutsideItem: PropTypes.func,
}),
}
static contextType = DnDContext

constructor(...args) {
super(...args)
this.state = {}
this.ref = React.createRef()
}

componentDidMount() {
Expand Down Expand Up @@ -73,43 +59,31 @@ class EventContainerWrapper extends React.Component {
})
}

handleMove = (point, boundaryBox) => {
handleMove = (point, bounds) => {
if (!pointInColumn(bounds, point)) return this.reset()
const { event } = this.context.draggable.dragAndDropAction
const { accessors, slotMetrics } = this.props

if (!pointInColumn(boundaryBox, point)) {
this.reset()
return
}

let currentSlot = slotMetrics.closestSlotFromPoint(
const newSlot = slotMetrics.closestSlotFromPoint(
{ y: point.y - this.eventOffsetTop, x: point.x },
boundaryBox
bounds
)

let eventStart = accessors.start(event)
let eventEnd = accessors.end(event)
let end = dates.add(
currentSlot,
dates.diff(eventStart, eventEnd, 'minutes'),
'minutes'
)

this.update(event, slotMetrics.getRange(currentSlot, end, false, true))
const { duration } = eventTimes(event, accessors)
let newEnd = dates.add(newSlot, duration, 'milliseconds')
this.update(event, slotMetrics.getRange(newSlot, newEnd, false, true))
}

handleResize(point, boundaryBox) {
let start, end
handleResize(point, bounds) {
const { accessors, slotMetrics } = this.props
const { event, direction } = this.context.draggable.dragAndDropAction
const newTime = slotMetrics.closestSlotFromPoint(point, bounds)

let currentSlot = slotMetrics.closestSlotFromPoint(point, boundaryBox)
let { start, end } = eventTimes(event, accessors)
if (direction === 'UP') {
end = accessors.end(event)
start = dates.min(currentSlot, slotMetrics.closestSlotFromDate(end, -1))
start = dates.min(newTime, slotMetrics.closestSlotFromDate(end, -1))
} else if (direction === 'DOWN') {
start = accessors.start(event)
end = dates.max(currentSlot, slotMetrics.closestSlotFromDate(start))
end = dates.max(newTime, slotMetrics.closestSlotFromDate(start))
}

this.update(event, slotMetrics.getRange(start, end))
Expand All @@ -132,10 +106,11 @@ class EventContainerWrapper extends React.Component {
}

_selectable = () => {
let node = findDOMNode(this)
let wrapper = this.ref.current
let node = wrapper.children[0]
let isBeingDragged = false
let selector = (this._selector = new Selection(() =>
node.closest('.rbc-time-view')
wrapper.closest('.rbc-time-view')
))

selector.on('beforeSelect', point => {
Expand All @@ -149,6 +124,12 @@ class EventContainerWrapper extends React.Component {
const eventNode = getEventNodeFromPoint(node, point)
if (!eventNode) return false

// eventOffsetTop is distance from the top of the event to the initial
// mouseDown position. We need this later to compute the new top of the
// event during move operations, since the final location is really a
// delta from this point. note: if we want to DRY this with WeekWrapper,
// probably better just to capture the mouseDown point here and do the
// placement computation in handleMove()...
this.eventOffsetTop = point.y - getBoundsForNode(eventNode).top
})

Expand All @@ -162,19 +143,14 @@ class EventContainerWrapper extends React.Component {

selector.on('dropFromOutside', point => {
if (!this.context.draggable.onDropFromOutside) return

const bounds = getBoundsForNode(node)

if (!pointInColumn(bounds, point)) return

this.handleDropFromOutside(point, bounds)
})

selector.on('dragOver', point => {
if (!this.context.draggable.dragFromOutsideItem) return

const bounds = getBoundsForNode(node)

this.handleDropFromOutside(point, bounds)
})

Expand Down Expand Up @@ -220,7 +196,7 @@ class EventContainerWrapper extends React.Component {
this._selector = null
}

render() {
renderContent() {
const {
children,
accessors,
Expand All @@ -231,7 +207,6 @@ class EventContainerWrapper extends React.Component {
} = this.props

let { event, top, height } = this.state

if (!event) return children

const events = children.props.children
Expand Down Expand Up @@ -271,8 +246,10 @@ class EventContainerWrapper extends React.Component {
),
})
}
}

EventContainerWrapper.propTypes = propTypes
render() {
return <div ref={this.ref}>{this.renderContent()}</div>
}
}

export default EventContainerWrapper
25 changes: 8 additions & 17 deletions src/addons/dragAndDrop/EventWrapper.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
import PropTypes from 'prop-types'
import React from 'react'
import clsx from 'clsx'
import { accessor } from '../../utils/propTypes'
import { accessor as get } from '../../utils/accessors'
import { DnDContext } from './DnDContext'

class EventWrapper extends React.Component {
static contextTypes = {
draggable: PropTypes.shape({
onStart: PropTypes.func,
onEnd: PropTypes.func,
onBeginAction: PropTypes.func,
draggableAccessor: accessor,
resizableAccessor: accessor,
dragAndDropAction: PropTypes.object,
}),
}
static contextType = DnDContext

static propTypes = {
type: PropTypes.oneOf(['date', 'time']),
Expand All @@ -32,28 +23,28 @@ class EventWrapper extends React.Component {

handleResizeUp = e => {
if (e.button !== 0) return
e.stopPropagation()
this.context.draggable.onBeginAction(this.props.event, 'resize', 'UP')
}
handleResizeDown = e => {
if (e.button !== 0) return
e.stopPropagation()
this.context.draggable.onBeginAction(this.props.event, 'resize', 'DOWN')
}
handleResizeLeft = e => {
if (e.button !== 0) return
e.stopPropagation()
this.context.draggable.onBeginAction(this.props.event, 'resize', 'LEFT')
}
handleResizeRight = e => {
if (e.button !== 0) return
e.stopPropagation()
this.context.draggable.onBeginAction(this.props.event, 'resize', 'RIGHT')
}
handleStartDragging = e => {
if (e.button === 0) {
if (e.button !== 0) return
// hack: because of the way the anchors are arranged in the DOM, resize
// anchor events will bubble up to the move anchor listener. Don't start
// move operations when we're on a resize anchor.
const isResizeHandle = e.target.className.includes('rbc-addons-dnd-resize')
if (!isResizeHandle)
this.context.draggable.onBeginAction(this.props.event, 'move')
}
}

renderAnchor(direction) {
Expand Down
52 changes: 49 additions & 3 deletions src/addons/dragAndDrop/README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,66 @@
### Drag and Drop

Creates a higher-order component (HOC) supporting drag & drop for moving and/or resizing of events:

```js
import { Calendar } from 'react-big-calendar'
import withDragAndDrop from 'react-big-calendar/lib/addons/dragAndDrop'

import 'react-big-calendar/lib/addons/dragAndDrop/styles.css'

const DraggableCalendar = withDragAndDrop(Calendar)
const DnDCalendar = withDragAndDrop(Calendar)

/* ... */

return (
<DraggableCalendar
<DnDCalendar
localizer={myLocalizer}
events={events}
draggableAccessor={event => true}
/>
)
```

Set `resizable` to false in your calendar if you don't want events to be resizable.
`resizable` is set to true by default.

The HOC adds `onEventDrop`, `onEventResize`, and `onDragStart` callback properties if the events are
moved or resized. These callbacks are called with these signatures:

```js
function onEventDrop({ event, start, end, allDay }) {...}
function onEventResize(type, { event, start, end, allDay }) {...} // type is always 'drop'
function onDragStart({ event, action, direction }) {...}
```

Moving and resizing of events has some subtlety which one should be aware of:

- In some situations, non-allDay events are displayed in "row" format where they
are rendered horizontally. This is the case for ALL events in a month view. It
is also occurs with multi-day events in a day or week view (unless `showMultiDayTimes`
is set).

- When dropping or resizing non-allDay events into a the header area or when
resizing them horizontally because they are displayed in row format, their
times are preserved, only their date is changed.

- If you care about these corner cases, you can examine the `allDay` param suppled
in the callback to determine how the user dropped or resized the event.

Additionally, this HOC adds the callback props `onDropFromOutside` and `onDragOver`:

- By default, the calendar will not respond to outside draggable items being dropped
onto it. However, if `onDropFromOutside` callback is passed, then when draggable
DOM elements are dropped on the calendar, the callback will fire, receiving an
object with start and end times, and an allDay boolean.

- If `onDropFromOutside` is passed, but `onDragOver` is not, any draggable event will be
droppable onto the calendar by default. On the other hand, if an `onDragOver` callback
_is_ passed, then it can discriminate as to whether a draggable item is droppable on the
calendar. To designate a draggable item as droppable, call `event.preventDefault`
inside `onDragOver`. If `event.preventDefault` is not called in the `onDragOver`
callback, then the draggable item will not be droppable on the calendar.

```js
function onDropFromOutside({ start, end, allDay }) {...}
function onDragOver(DragEvent: event) {...}
```
Loading

0 comments on commit 611a0ce

Please sign in to comment.