|
1 | 1 | import PropTypes from 'prop-types' |
2 | 2 | import React from 'react' |
3 | 3 | import { DragSource } from 'react-dnd' |
| 4 | +import { getEmptyImage } from 'react-dnd-html5-backend' |
4 | 5 | import cn from 'classnames' |
| 6 | +import compose from './compose' |
5 | 7 |
|
6 | 8 | import BigCalendar from '../../index' |
| 9 | +const EventWrapper = BigCalendar.components.eventWrapper |
7 | 10 |
|
8 | | -/* drag sources */ |
| 11 | +class DraggableEventWrapper extends React.Component { |
| 12 | + static propTypes = { |
| 13 | + event: PropTypes.object.isRequired, |
9 | 14 |
|
10 | | -let eventSource = { |
11 | | - beginDrag(props) { |
12 | | - return props.event |
13 | | - }, |
14 | | -} |
| 15 | + connectDragSource: PropTypes.func.isRequired, |
| 16 | + connectTopDragPreview: PropTypes.func.isRequired, |
| 17 | + connectTopDragSource: PropTypes.func.isRequired, |
| 18 | + connectBottomDragPreview: PropTypes.func.isRequired, |
| 19 | + connectBottomDragSource: PropTypes.func.isRequired, |
| 20 | + connectLeftDragPreview: PropTypes.func.isRequired, |
| 21 | + connectLeftDragSource: PropTypes.func.isRequired, |
| 22 | + connectRightDragPreview: PropTypes.func.isRequired, |
| 23 | + connectRightDragSource: PropTypes.func.isRequired, |
15 | 24 |
|
16 | | -function collectSource(connect, monitor) { |
17 | | - return { |
18 | | - connectDragSource: connect.dragSource(), |
19 | | - isDragging: monitor.isDragging(), |
| 25 | + allDay: PropTypes.bool, |
| 26 | + isRow: PropTypes.bool, |
| 27 | + continuesPrior: PropTypes.bool, |
| 28 | + continuesAfter: PropTypes.bool, |
| 29 | + isDragging: PropTypes.bool, |
| 30 | + isResizing: PropTypes.bool, |
20 | 31 | } |
21 | | -} |
22 | 32 |
|
23 | | -const propTypes = { |
24 | | - connectDragSource: PropTypes.func.isRequired, |
25 | | - isDragging: PropTypes.bool.isRequired, |
26 | | - event: PropTypes.object.isRequired, |
27 | | -} |
| 33 | + componentDidMount() { |
| 34 | + // this is needed to prevent the backend from |
| 35 | + // screenshot'ing the event during a resize which |
| 36 | + // would be very confusing visually |
| 37 | + const emptyImage = getEmptyImage() |
| 38 | + const previewOptions = { captureDraggingState: true } |
| 39 | + this.props.connectTopDragPreview(emptyImage, previewOptions) |
| 40 | + this.props.connectBottomDragPreview(emptyImage, previewOptions) |
| 41 | + this.props.connectLeftDragPreview(emptyImage, previewOptions) |
| 42 | + this.props.connectRightDragPreview(emptyImage, previewOptions) |
| 43 | + } |
28 | 44 |
|
29 | | -class DraggableEventWrapper extends React.Component { |
30 | 45 | render() { |
31 | | - let { connectDragSource, isDragging, children, event } = this.props |
32 | | - let EventWrapper = BigCalendar.components.eventWrapper |
| 46 | + let { |
| 47 | + connectDragSource, |
| 48 | + connectTopDragSource, |
| 49 | + connectBottomDragSource, |
| 50 | + connectLeftDragSource, |
| 51 | + connectRightDragSource, |
| 52 | + isDragging, |
| 53 | + isResizing, |
| 54 | + children, |
| 55 | + event, |
| 56 | + allDay, |
| 57 | + isRow, |
| 58 | + continuesPrior, |
| 59 | + continuesAfter, |
| 60 | + } = this.props |
| 61 | + |
| 62 | + let StartAnchor = null, |
| 63 | + EndAnchor = null |
| 64 | + |
| 65 | + /* |
| 66 | + * The resizability of events depends on whether they are |
| 67 | + * allDay events and how they are displayed. |
| 68 | + * |
| 69 | + * 1. If the event is being shown in an event row (because |
| 70 | + * it is an allDay event shown in the header row or because as |
| 71 | + * in month view the view is showing all events as rows) then we |
| 72 | + * allow east-west resizing. |
| 73 | + * |
| 74 | + * 2. Otherwise the event is being displayed |
| 75 | + * normally, we can drag it north-south to resize the times. |
| 76 | + * |
| 77 | + * See `DropWrappers` for handling of the drop of such events. |
| 78 | + * |
| 79 | + * Notwithstanding the above, we never show drag anchors for |
| 80 | + * events which continue beyond current component. This happens |
| 81 | + * in the middle of events when showMultiDay is true, and to |
| 82 | + * events at the edges of the calendar's min/max location. |
| 83 | + */ |
| 84 | + if (isRow || allDay) { |
| 85 | + const anchor = ( |
| 86 | + <div className="rbc-addons-dnd-resize-ew-anchor"> |
| 87 | + <div className="rbc-addons-dnd-resize-ew-icon" /> |
| 88 | + </div> |
| 89 | + ) |
| 90 | + StartAnchor = !continuesPrior && connectLeftDragSource(anchor) |
| 91 | + EndAnchor = !continuesAfter && connectRightDragSource(anchor) |
| 92 | + } else { |
| 93 | + const anchor = ( |
| 94 | + <div className="rbc-addons-dnd-resize-ns-anchor"> |
| 95 | + <div className="rbc-addons-dnd-resize-ns-icon" /> |
| 96 | + </div> |
| 97 | + ) |
| 98 | + StartAnchor = !continuesPrior && connectTopDragSource(anchor) |
| 99 | + EndAnchor = !continuesAfter && connectBottomDragSource(anchor) |
| 100 | + } |
| 101 | + |
| 102 | + /* |
| 103 | + * props.children is the singular <Event> component. |
| 104 | + * BigCalendar positions the Event abolutely and we |
| 105 | + * need the anchors to be part of that positioning. |
| 106 | + * So we insert the anchors inside the Event's children |
| 107 | + * rather than wrap the Event here as the latter approach |
| 108 | + * would lose the positioning. |
| 109 | + */ |
| 110 | + const childrenWithAnchors = ( |
| 111 | + <div className="rbc-addons-dnd-resizable"> |
| 112 | + {StartAnchor} |
| 113 | + {children.props.children} |
| 114 | + {EndAnchor} |
| 115 | + </div> |
| 116 | + ) |
33 | 117 |
|
34 | 118 | children = React.cloneElement(children, { |
35 | 119 | className: cn( |
36 | 120 | children.props.className, |
37 | | - isDragging && 'rbc-addons-dnd-dragging' |
| 121 | + isDragging && 'rbc-addons-dnd-dragging', |
| 122 | + isResizing && 'rbc-addons-dnd-resizing' |
38 | 123 | ), |
| 124 | + children: childrenWithAnchors, // replace original event child with anchor-embellished child |
39 | 125 | }) |
40 | 126 |
|
41 | 127 | return ( |
42 | | - <EventWrapper event={event}>{connectDragSource(children)}</EventWrapper> |
| 128 | + <EventWrapper event={event} allDay={allDay}> |
| 129 | + {connectDragSource(children)} |
| 130 | + </EventWrapper> |
43 | 131 | ) |
44 | 132 | } |
45 | 133 | } |
46 | 134 |
|
47 | | -DraggableEventWrapper.propTypes = propTypes |
| 135 | +/* drag sources */ |
| 136 | +const makeEventSource = anchor => ({ |
| 137 | + beginDrag: ({ event }) => ({ event, anchor }), |
| 138 | + // canDrag: ({ event }) => event.draggable === undefined || event.draggable - e.g. support per-event dragability/sizability |
| 139 | +}) |
48 | 140 |
|
49 | | -export default DragSource('event', eventSource, collectSource)( |
50 | | - DraggableEventWrapper |
51 | | -) |
| 141 | +export default compose( |
| 142 | + DragSource('event', makeEventSource('drop'), (connect, monitor) => ({ |
| 143 | + connectDragSource: connect.dragSource(), |
| 144 | + isDragging: monitor.isDragging(), |
| 145 | + })), |
| 146 | + DragSource('event', makeEventSource('resizeTop'), (connect, monitor) => ({ |
| 147 | + connectTopDragSource: connect.dragSource(), |
| 148 | + connectTopDragPreview: connect.dragPreview(), |
| 149 | + isResizing: monitor.isDragging(), |
| 150 | + })), |
| 151 | + DragSource('event', makeEventSource('resizeBottom'), (connect, monitor) => ({ |
| 152 | + connectBottomDragSource: connect.dragSource(), |
| 153 | + connectBottomDragPreview: connect.dragPreview(), |
| 154 | + isResizing: monitor.isDragging(), |
| 155 | + })), |
| 156 | + DragSource('event', makeEventSource('resizeLeft'), (connect, monitor) => ({ |
| 157 | + connectLeftDragSource: connect.dragSource(), |
| 158 | + connectLeftDragPreview: connect.dragPreview(), |
| 159 | + isResizing: monitor.isDragging(), |
| 160 | + })), |
| 161 | + DragSource('event', makeEventSource('resizeRight'), (connect, monitor) => ({ |
| 162 | + connectRightDragSource: connect.dragSource(), |
| 163 | + connectRightDragPreview: connect.dragPreview(), |
| 164 | + isResizing: monitor.isDragging(), |
| 165 | + })) |
| 166 | +)(DraggableEventWrapper) |
0 commit comments