Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DnD support for custom event components #716

Merged
merged 7 commits into from
Mar 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/demos/dnd.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class Dnd extends React.Component {
this.setState({
events: nextEvents,
})

alert(`${event.title} was resized to ${start}-${end}`)
}

render() {
Expand Down
8 changes: 7 additions & 1 deletion src/DayColumn.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,14 @@ class DayColumn extends React.Component {

let { height, top, width, xOffset } = style

let wrapperProps = {
event,
continuesPrior: _continuesPrior,
continuesAfter: _continuesAfter,
}

return (
<EventWrapper event={event} key={'evt_' + idx}>
<EventWrapper {...wrapperProps} key={'evt_' + idx}>
<div
style={{
...xStyle,
Expand Down
21 changes: 16 additions & 5 deletions src/EventCell.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class EventCell extends React.Component {
endAccessor,
titleAccessor,
tooltipAccessor,
allDayAccessor,
slotStart,
slotEnd,
onSelect,
Expand All @@ -50,9 +51,10 @@ class EventCell extends React.Component {
tooltip = get(event, tooltipAccessor),
end = get(event, endAccessor),
start = get(event, startAccessor),
isAllDayEvent =
allDay = get(event, allDayAccessor),
showAsAllDay =
isAllDay ||
get(event, props.allDayAccessor) ||
allDay ||
dates.diff(start, dates.ceil(end, 'day'), 'day') > 1,
continuesPrior = dates.lt(start, slotStart, 'day'),
continuesAfter = dates.gte(end, slotEnd, 'day')
Expand All @@ -65,13 +67,22 @@ class EventCell extends React.Component {
selected
)

let wrapperProps = {
event,
allDay,
continuesPrior,
continuesAfter,
}

return (
<EventWrapper event={event}>
// give EventWrapper some extra info to help it determine whether it
// it's in a row, etc. Useful for dnd, etc.
<EventWrapper {...wrapperProps} isRow={true}>
<div
style={{ ...props.style, ...style }}
className={cn('rbc-event', className, xClassName, {
'rbc-selected': selected,
'rbc-event-allday': isAllDayEvent,
'rbc-event-allday': showAsAllDay,
'rbc-event-continues-prior': continuesPrior,
'rbc-event-continues-after': continuesAfter,
})}
Expand All @@ -80,7 +91,7 @@ class EventCell extends React.Component {
>
<div className="rbc-event-content" title={tooltip || undefined}>
{Event ? (
<Event event={event} title={title} isAllDay={isAllDayEvent} />
<Event event={event} title={title} isAllDay={allDay} />
) : (
title
)}
Expand Down
165 changes: 140 additions & 25 deletions src/addons/dragAndDrop/DraggableEventWrapper.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,166 @@
import PropTypes from 'prop-types'
import React from 'react'
import { DragSource } from 'react-dnd'
import { getEmptyImage } from 'react-dnd-html5-backend'
import cn from 'classnames'
import compose from './compose'

import BigCalendar from '../../index'
const EventWrapper = BigCalendar.components.eventWrapper

/* drag sources */
class DraggableEventWrapper extends React.Component {
static propTypes = {
event: PropTypes.object.isRequired,

let eventSource = {
beginDrag(props) {
return props.event
},
}
connectDragSource: PropTypes.func.isRequired,
connectTopDragPreview: PropTypes.func.isRequired,
connectTopDragSource: PropTypes.func.isRequired,
connectBottomDragPreview: PropTypes.func.isRequired,
connectBottomDragSource: PropTypes.func.isRequired,
connectLeftDragPreview: PropTypes.func.isRequired,
connectLeftDragSource: PropTypes.func.isRequired,
connectRightDragPreview: PropTypes.func.isRequired,
connectRightDragSource: PropTypes.func.isRequired,

function collectSource(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
allDay: PropTypes.bool,
isRow: PropTypes.bool,
continuesPrior: PropTypes.bool,
continuesAfter: PropTypes.bool,
isDragging: PropTypes.bool,
isResizing: PropTypes.bool,
}
}

const propTypes = {
connectDragSource: PropTypes.func.isRequired,
isDragging: PropTypes.bool.isRequired,
event: PropTypes.object.isRequired,
}
componentDidMount() {
// this is needed to prevent the backend from
// screenshot'ing the event during a resize which
// would be very confusing visually
const emptyImage = getEmptyImage()
const previewOptions = { captureDraggingState: true }
this.props.connectTopDragPreview(emptyImage, previewOptions)
this.props.connectBottomDragPreview(emptyImage, previewOptions)
this.props.connectLeftDragPreview(emptyImage, previewOptions)
this.props.connectRightDragPreview(emptyImage, previewOptions)
}

class DraggableEventWrapper extends React.Component {
render() {
let { connectDragSource, isDragging, children, event } = this.props
let EventWrapper = BigCalendar.components.eventWrapper
let {
connectDragSource,
connectTopDragSource,
connectBottomDragSource,
connectLeftDragSource,
connectRightDragSource,
isDragging,
isResizing,
children,
event,
allDay,
isRow,
continuesPrior,
continuesAfter,
} = this.props

let StartAnchor = null,
EndAnchor = null

/*
* The resizability of events depends on whether they are
* allDay events and how they are displayed.
*
* 1. If the event is being shown in an event row (because
* it is an allDay event shown in the header row or because as
* in month view the view is showing all events as rows) then we
* allow east-west resizing.
*
* 2. Otherwise the event is being displayed
* normally, we can drag it north-south to resize the times.
*
* See `DropWrappers` for handling of the drop of such events.
*
* Notwithstanding the above, we never show drag anchors for
* events which continue beyond current component. This happens
* in the middle of events when showMultiDay is true, and to
* events at the edges of the calendar's min/max location.
*/
if (isRow || allDay) {
const anchor = (
<div className="rbc-addons-dnd-resize-ew-anchor">
<div className="rbc-addons-dnd-resize-ew-icon" />
</div>
)
StartAnchor = !continuesPrior && connectLeftDragSource(anchor)
EndAnchor = !continuesAfter && connectRightDragSource(anchor)
} else {
const anchor = (
<div className="rbc-addons-dnd-resize-ns-anchor">
<div className="rbc-addons-dnd-resize-ns-icon" />
</div>
)
StartAnchor = !continuesPrior && connectTopDragSource(anchor)
EndAnchor = !continuesAfter && connectBottomDragSource(anchor)
}

/*
* props.children is the singular <Event> component.
* BigCalendar positions the Event abolutely and we
* need the anchors to be part of that positioning.
* So we insert the anchors inside the Event's children
* rather than wrap the Event here as the latter approach
* would lose the positioning.
*/
const childrenWithAnchors = (
<div className="rbc-addons-dnd-resizable">
{StartAnchor}
{children.props.children}
{EndAnchor}
</div>
)

children = React.cloneElement(children, {
className: cn(
children.props.className,
isDragging && 'rbc-addons-dnd-dragging'
isDragging && 'rbc-addons-dnd-dragging',
isResizing && 'rbc-addons-dnd-resizing'
),
children: childrenWithAnchors, // replace original event child with anchor-embellished child
})

return (
<EventWrapper event={event}>{connectDragSource(children)}</EventWrapper>
<EventWrapper event={event} allDay={allDay}>
{connectDragSource(children)}
</EventWrapper>
)
}
}

DraggableEventWrapper.propTypes = propTypes
/* drag sources */
const makeEventSource = anchor => ({
beginDrag: ({ event }) => ({ event, anchor }),
// canDrag: ({ event }) => event.draggable === undefined || event.draggable - e.g. support per-event dragability/sizability
})

export default DragSource('event', eventSource, collectSource)(
DraggableEventWrapper
)
export default compose(
DragSource('event', makeEventSource('drop'), (connect, monitor) => ({
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
})),
DragSource('event', makeEventSource('resizeTop'), (connect, monitor) => ({
connectTopDragSource: connect.dragSource(),
connectTopDragPreview: connect.dragPreview(),
isResizing: monitor.isDragging(),
})),
DragSource('event', makeEventSource('resizeBottom'), (connect, monitor) => ({
connectBottomDragSource: connect.dragSource(),
connectBottomDragPreview: connect.dragPreview(),
isResizing: monitor.isDragging(),
})),
DragSource('event', makeEventSource('resizeLeft'), (connect, monitor) => ({
connectLeftDragSource: connect.dragSource(),
connectLeftDragPreview: connect.dragPreview(),
isResizing: monitor.isDragging(),
})),
DragSource('event', makeEventSource('resizeRight'), (connect, monitor) => ({
connectRightDragSource: connect.dragSource(),
connectRightDragPreview: connect.dragPreview(),
isResizing: monitor.isDragging(),
}))
)(DraggableEventWrapper)
Loading