|
|
@@ -3,14 +3,19 @@ import HitDragListener, { Hit } from '../dnd/HitDragListener'
|
|
|
import globalContext from '../common/GlobalContext'
|
|
|
import { PointerDragEvent } from '../dnd/PointerDragListener'
|
|
|
import { EventStore, parseDef, createInstance } from '../reducers/event-store'
|
|
|
-import UnzonedRange from '../models/UnzonedRange';
|
|
|
+import UnzonedRange from '../models/UnzonedRange'
|
|
|
+import * as externalHooks from '../exports'
|
|
|
+import { createDuration } from '../datelib/duration'
|
|
|
+import { assignTo } from '../util/object'
|
|
|
|
|
|
export default class ExternalDragging {
|
|
|
|
|
|
hitListener: HitDragListener
|
|
|
addableEventStore: EventStore
|
|
|
+ explicitEventCreationData: any
|
|
|
+ eventCreationData: any
|
|
|
|
|
|
- constructor(dragListener: IntentfulDragListener) {
|
|
|
+ constructor(dragListener: IntentfulDragListener, rawEventCreationData?) {
|
|
|
let hitListener = this.hitListener = new HitDragListener(dragListener, globalContext.componentHash)
|
|
|
hitListener.dieIfNoInitial = false
|
|
|
hitListener.on('dragstart', this.onDragStart)
|
|
|
@@ -19,6 +24,8 @@ export default class ExternalDragging {
|
|
|
hitListener.on('dragend', this.onDragEnd)
|
|
|
|
|
|
dragListener.enableMirror()
|
|
|
+
|
|
|
+ this.explicitEventCreationData = rawEventCreationData ? processExplicitData(rawEventCreationData) : null
|
|
|
}
|
|
|
|
|
|
destroy() {
|
|
|
@@ -38,16 +45,21 @@ export default class ExternalDragging {
|
|
|
globalContext.eventSelectedComponent = null
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ this.eventCreationData = this.explicitEventCreationData || getDraggedElMeta(ev.el)
|
|
|
}
|
|
|
|
|
|
- onHitOver = (hit, ev) => {
|
|
|
+ onHitOver = (hit) => {
|
|
|
let calendar = hit.component.getCalendar()
|
|
|
|
|
|
+ this.addableEventStore = computeEventStoreForHit(hit, this.eventCreationData)
|
|
|
+
|
|
|
calendar.dispatch({
|
|
|
type: 'SET_DRAG',
|
|
|
- displacement: (this.addableEventStore = computeEventStoreForHit(hit)),
|
|
|
- origSeg: null,
|
|
|
- isTouch: ev.isTouch
|
|
|
+ dragState: {
|
|
|
+ eventStore: this.addableEventStore,
|
|
|
+ willCreateEvent: Boolean(this.eventCreationData.standardProps)
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
let { dragListener } = this.hitListener
|
|
|
@@ -62,15 +74,15 @@ export default class ExternalDragging {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- onHitOut = (hit, ev) => { // TODO: onHitChange?
|
|
|
+ onHitOut = (hit) => { // TODO: onHitChange?
|
|
|
|
|
|
// we still want to notify calendar about invalid drag
|
|
|
// because we want related events to stay hidden
|
|
|
hit.component.getCalendar().dispatch({
|
|
|
type: 'SET_DRAG',
|
|
|
- displacement: { defs: {}, instances: {} }, // TODO: better way to make empty event-store
|
|
|
- origSeg: null,
|
|
|
- isTouch: ev.isTouch
|
|
|
+ dragState: {
|
|
|
+ eventStore: { defs: {}, instances: {} } // TODO: better way to make empty event-store
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
this.addableEventStore = null
|
|
|
@@ -81,31 +93,178 @@ export default class ExternalDragging {
|
|
|
dragListener.setMirrorNeedsRevert(true)
|
|
|
}
|
|
|
|
|
|
- onDragEnd = () => {
|
|
|
+ onDragEnd = (pev: PointerDragEvent) => {
|
|
|
+ this.hitListener.dragListener.enableMirror() // always restore!
|
|
|
+
|
|
|
if (this.addableEventStore) {
|
|
|
- let finalComponent = this.hitListener.finalHit.component
|
|
|
+ let finalHit = this.hitListener.finalHit
|
|
|
+ let finalView = finalHit.component.view
|
|
|
+ let finalCalendar = finalView.calendar
|
|
|
|
|
|
- finalComponent.getCalendar().dispatch({
|
|
|
+ finalCalendar.dispatch({
|
|
|
type: 'CLEAR_DRAG'
|
|
|
})
|
|
|
|
|
|
- // TODO: report mutation to dispatcher
|
|
|
- console.log('addable', this.addableEventStore)
|
|
|
+ // TODO: how to let Scheduler extend this?
|
|
|
+ finalCalendar.publiclyTrigger('drop', [
|
|
|
+ {
|
|
|
+ draggedEl: pev.el,
|
|
|
+ date: finalCalendar.dateEnv.toDate(finalHit.range.start),
|
|
|
+ isAllDay: finalHit.isAllDay,
|
|
|
+ jsEvent: pev.origEvent,
|
|
|
+ view: finalView
|
|
|
+ }
|
|
|
+ ])
|
|
|
+
|
|
|
+ if (this.eventCreationData.standardProps) { // TODO: bad way to test if event creation is good
|
|
|
+ finalCalendar.dispatch({
|
|
|
+ type: 'ADD_EVENTS',
|
|
|
+ eventStore: this.addableEventStore,
|
|
|
+ stick: this.eventCreationData.stick // TODO: use this param
|
|
|
+ })
|
|
|
+
|
|
|
+ // signal an external event landed
|
|
|
+ finalCalendar.publiclyTrigger('eventReceive', [
|
|
|
+ {
|
|
|
+ draggedEl: pev.el,
|
|
|
+ event: this.addableEventStore, // TODO: what to put here!?
|
|
|
+ view: finalView
|
|
|
+ }
|
|
|
+ ])
|
|
|
+ }
|
|
|
+
|
|
|
+ this.addableEventStore = null
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
-function computeEventStoreForHit(hit: Hit): EventStore {
|
|
|
+
|
|
|
+function computeEventStoreForHit(hit: Hit, eventCreationData): EventStore {
|
|
|
let calendar = hit.component.getCalendar()
|
|
|
- let def = parseDef({ title: 'test event' }, null, hit.isAllDay, false)
|
|
|
- let instance = createInstance(def.defId, new UnzonedRange(
|
|
|
- hit.range.start,
|
|
|
- calendar.getDefaultEventEnd(hit.isAllDay, hit.range.start)
|
|
|
- ))
|
|
|
+
|
|
|
+ let def = parseDef(
|
|
|
+ eventCreationData.standardProps || {},
|
|
|
+ null,
|
|
|
+ hit.isAllDay,
|
|
|
+ Boolean(eventCreationData.duration) // hasEnd
|
|
|
+ )
|
|
|
+
|
|
|
+ let start = hit.range.start
|
|
|
+
|
|
|
+ // only rely on time info if drop zone is all-day,
|
|
|
+ // otherwise, we already know the time
|
|
|
+ if (hit.isAllDay && eventCreationData.time) {
|
|
|
+ start = calendar.dateEnv.add(start, eventCreationData.time)
|
|
|
+ }
|
|
|
+
|
|
|
+ let end = eventCreationData.duration ?
|
|
|
+ calendar.dateEnv.add(start, eventCreationData.duration) :
|
|
|
+ calendar.getDefaultEventEnd(hit.isAllDay, start)
|
|
|
+
|
|
|
+ let instance = createInstance(def.defId, new UnzonedRange(start, end))
|
|
|
|
|
|
return {
|
|
|
defs: { [def.defId]: def },
|
|
|
instances: { [instance.instanceId]: instance }
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+// same return type as getDraggedElMeta
|
|
|
+// TODO: merge a lot of code with getDraggedElMeta!
|
|
|
+// TODO: use refineProps!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
|
+// ALSO: don't like how `stick` and others are in same namespace. impossible for them to go to extendedProps
|
|
|
+function processExplicitData(data) {
|
|
|
+ let standardProps = assignTo({}, data)
|
|
|
+ let startTime // a Duration
|
|
|
+ let duration
|
|
|
+ let stick
|
|
|
+
|
|
|
+ if (standardProps) {
|
|
|
+
|
|
|
+ // something like 1 or true. still signal event creation
|
|
|
+ if (typeof standardProps !== 'object') {
|
|
|
+ standardProps = {}
|
|
|
+ }
|
|
|
+
|
|
|
+ // pluck special-cased date/time properties
|
|
|
+ startTime = standardProps.start
|
|
|
+ if (startTime == null) { startTime = standardProps.time } // accept 'time' as well
|
|
|
+ duration = standardProps.duration
|
|
|
+ stick = standardProps.stick
|
|
|
+ delete standardProps.start
|
|
|
+ delete standardProps.time
|
|
|
+ delete standardProps.duration
|
|
|
+ delete standardProps.stick
|
|
|
+ }
|
|
|
+
|
|
|
+ // massage into correct data types
|
|
|
+ startTime = startTime != null ? createDuration(startTime) : null
|
|
|
+ duration = duration != null ? createDuration(duration) : null
|
|
|
+ stick = Boolean(stick) // wont be refining undefined?!?! - have a default
|
|
|
+
|
|
|
+ return { standardProps, startTime, duration, stick }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// Extracting Event Data From Elements
|
|
|
+// -----------------------------------
|
|
|
+// TODO: create returned struct
|
|
|
+
|
|
|
+(externalHooks as any).dataAttrPrefix = ''
|
|
|
+
|
|
|
+// Given an element that might represent a dragged FullCalendar event, returns an intermediate data structure
|
|
|
+// to be used for Event Object creation.
|
|
|
+// A defined `.eventProps`, even when empty, indicates that an event should be created.
|
|
|
+function getDraggedElMeta(el) {
|
|
|
+ let standardProps // properties for creating the event, not related to date/time
|
|
|
+ let startTime // a Duration
|
|
|
+ let duration
|
|
|
+ let stick
|
|
|
+
|
|
|
+ standardProps = getEmbeddedElData(el, 'event', true)
|
|
|
+
|
|
|
+ if (standardProps) {
|
|
|
+
|
|
|
+ // something like 1 or true. still signal event creation
|
|
|
+ if (typeof standardProps !== 'object') {
|
|
|
+ standardProps = {}
|
|
|
+ }
|
|
|
+
|
|
|
+ // pluck special-cased date/time properties
|
|
|
+ startTime = standardProps.start
|
|
|
+ if (startTime == null) { startTime = standardProps.time } // accept 'time' as well
|
|
|
+ duration = standardProps.duration
|
|
|
+ stick = standardProps.stick
|
|
|
+ delete standardProps.start
|
|
|
+ delete standardProps.time
|
|
|
+ delete standardProps.duration
|
|
|
+ delete standardProps.stick
|
|
|
+ }
|
|
|
+
|
|
|
+ // fallback to standalone attribute values for each of the date/time properties
|
|
|
+ if (startTime == null) { startTime = getEmbeddedElData(el, 'start') }
|
|
|
+ if (startTime == null) { startTime = getEmbeddedElData(el, 'time') } // accept 'time' as well
|
|
|
+ if (duration == null) { duration = getEmbeddedElData(el, 'duration') }
|
|
|
+ if (stick == null) { stick = getEmbeddedElData(el, 'stick', true) }
|
|
|
+
|
|
|
+ // massage into correct data types
|
|
|
+ startTime = startTime != null ? createDuration(startTime) : null
|
|
|
+ duration = duration != null ? createDuration(duration) : null
|
|
|
+ stick = Boolean(stick)
|
|
|
+
|
|
|
+ return { standardProps, startTime, duration, stick }
|
|
|
+}
|
|
|
+
|
|
|
+function getEmbeddedElData(el, name, shouldParseJson = false) {
|
|
|
+ let prefix = (externalHooks as any).dataAttrPrefix
|
|
|
+ let prefixedName = (prefix ? prefix + '-' : '') + name
|
|
|
+
|
|
|
+ let data = el.getAttribute('data-' + prefixedName) || null
|
|
|
+ if (data && shouldParseJson) {
|
|
|
+ data = JSON.parse(data)
|
|
|
+ }
|
|
|
+
|
|
|
+ return data
|
|
|
+}
|