Sfoglia il codice sorgente

get event creation on drop working

Adam Shaw 7 anni fa
parent
commit
518067e943

+ 2 - 2
src/agenda/AgendaView.ts

@@ -453,13 +453,13 @@ function splitInteractionState(state: EventInteractionState) {
     allDay = {
       eventStore: eventStoreGroups.allDay,
       origSeg: state.origSeg,
-      isTouch: state.isTouch
+      willCreateEvent: state.willCreateEvent
     }
 
     timed = {
       eventStore: eventStoreGroups.timed,
       origSeg: state.origSeg,
-      isTouch: state.isTouch
+      willCreateEvent: state.willCreateEvent
     }
   }
 

+ 6 - 9
src/agenda/TimeGrid.ts

@@ -616,16 +616,13 @@ export default class TimeGrid extends DateComponent {
 
   // Renders a visual indication of an event being dragged over the specified date(s).
   // A returned value of `true` signals that a mock "helper" event has been rendered.
-  renderDrag(eventStore: EventStore, origSeg, isTouch) {
+  renderDrag(eventStore: EventStore, origSeg, willCreateEvent) {
     let segs = this.eventStoreToSegs(eventStore)
 
-    if (origSeg) { // if there is event information for this drag, render a helper event
-
+    // if from within the calendar OR external element that will create an event, render helper
+    if (origSeg || willCreateEvent) {
       if (segs.length) {
-        this.helperRenderer.renderEventDraggingSegs(segs, origSeg, isTouch)
-
-        // signal that a helper has been rendered
-        return true
+        this.helperRenderer.renderEventDraggingSegs(segs, origSeg)
       }
     } else { // otherwise, just render a highlight
       this.renderHighlightSegs(segs)
@@ -645,10 +642,10 @@ export default class TimeGrid extends DateComponent {
 
 
   // Renders a visual indication of an event being resized
-  renderEventResize(eventStore: EventStore, origSeg, isTouch) {
+  renderEventResize(eventStore: EventStore, origSeg) {
     let segs = this.eventStoreToSegs(eventStore)
 
-    this.helperRenderer.renderEventResizingSegs(segs, origSeg, isTouch)
+    this.helperRenderer.renderEventResizingSegs(segs, origSeg)
   }
 
 

+ 4 - 6
src/basic/DayGrid.ts

@@ -360,16 +360,14 @@ export default class DayGrid extends DateComponent {
 
   // Renders a visual indication of an event or external element being dragged.
   // `eventLocation` has zoned start and end (optional)
-  renderDrag(eventStore: EventStore, origSeg, isTouch) {
+  renderDrag(eventStore: EventStore, origSeg) {
     let segs = this.eventStoreToSegs(eventStore)
 
     this.renderHighlightSegs(segs)
 
     // render drags from OTHER components as helpers
     if (segs.length && origSeg && origSeg.component !== this) {
-      this.helperRenderer.renderEventDraggingSegs(segs, origSeg, isTouch)
-
-      return true // signal helpers rendered
+      this.helperRenderer.renderEventDraggingSegs(segs, origSeg)
     }
   }
 
@@ -386,12 +384,12 @@ export default class DayGrid extends DateComponent {
 
 
   // Renders a visual indication of an event being resized
-  renderEventResize(eventStore: EventStore, origSeg, isTouch) {
+  renderEventResize(eventStore: EventStore, origSeg) {
     let segs = this.eventStoreToSegs(eventStore)
 
     this.renderHighlightSegs(segs)
 
-    this.helperRenderer.renderEventResizingSegs(segs, origSeg, isTouch)
+    this.helperRenderer.renderEventResizingSegs(segs, origSeg)
   }
 
 

+ 4 - 4
src/component/DateComponent.ts

@@ -580,7 +580,7 @@ export default abstract class DateComponent extends Component {
     if (dragState.origSeg) {
       this.hideRelatedSegs(dragState.origSeg)
     }
-    this.renderDrag(dragState.eventStore, dragState.origSeg, dragState.isTouch)
+    this.renderDrag(dragState.eventStore, dragState.origSeg, dragState.willCreateEvent)
   }
 
 
@@ -595,7 +595,7 @@ export default abstract class DateComponent extends Component {
   // Renders a visual indication of a event or external-element drag over the given drop zone.
   // If an external-element, seg will be `null`.
   // Must return elements used for any mock events.
-  renderDrag(eventStore: EventStore, origSeg?, isTouch = false) {
+  renderDrag(eventStore: EventStore, origSeg, willCreateEvent) {
     // subclasses can implement
   }
 
@@ -614,7 +614,7 @@ export default abstract class DateComponent extends Component {
     if (eventResizeState.origSeg) {
       this.hideRelatedSegs(eventResizeState.origSeg)
     }
-    this.renderEventResize(eventResizeState.eventStore, eventResizeState.origSeg, eventResizeState.isTouch)
+    this.renderEventResize(eventResizeState.eventStore, eventResizeState.origSeg)
   }
 
 
@@ -627,7 +627,7 @@ export default abstract class DateComponent extends Component {
 
 
   // Renders a visual indication of an event being resized.
-  renderEventResize(eventStore: EventStore, origSeg: any, isTouch: boolean) {
+  renderEventResize(eventStore: EventStore, origSeg: any) {
     // subclasses can implement
   }
 

+ 3 - 3
src/component/renderers/HelperRenderer.ts

@@ -18,17 +18,17 @@ export default abstract class HelperRenderer {
   }
 
 
-  renderEventDraggingSegs(segs: Seg[], sourceSeg, isTouch) {
+  renderEventDraggingSegs(segs: Seg[], sourceSeg) {
     this.renderEventSegs(
       segs,
       sourceSeg,
       'fc-dragging',
-      isTouch ? null : this.view.opt('dragOpacity')
+      this.view.opt('dragOpacity') // TODO: use CSS to prevent this
     )
   }
 
 
-  renderEventResizingSegs(segs: Seg[], sourceSeg, isTouch) {
+  renderEventResizingSegs(segs: Seg[], sourceSeg) {
     this.renderEventSegs(
       segs,
       sourceSeg,

+ 1 - 1
src/dnd/IntentfulDragListener.ts

@@ -184,8 +184,8 @@ export class IntentfulDragListenerImpl implements IntentfulDragListener {
   }
 
   stopDrag(ev) {
+    this.isDragging = false // go first because DragMirror::enable relies on it :(
     this.emitter.trigger('dragend', ev)
-    this.isDragging = false
   }
 
 

+ 10 - 8
src/interactions/EventDragging.ts

@@ -93,7 +93,7 @@ export default class EventDragging {
     }
   }
 
-  onHitOver = (hit, ev) => {
+  onHitOver = (hit) => {
     let { initialHit } = this.hitListener
     let calendar = hit.component.getCalendar()
 
@@ -106,9 +106,10 @@ export default class EventDragging {
 
     calendar.dispatch({
       type: 'SET_DRAG',
-      displacement: mutatedRelated,
-      origSeg: this.draggingSeg,
-      isTouch: ev.isTouch
+      dragState: {
+        eventStore: mutatedRelated,
+        origSeg: this.draggingSeg
+      }
     })
 
     let { dragMirror } = this.dragListener
@@ -128,16 +129,17 @@ export default class EventDragging {
     }
   }
 
-  onHitOut = (hit, ev) => { // TODO: onHitChange?
+  onHitOut = (hit) => { // TODO: onHitChange?
     this.mutation = null
 
     // 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: this.draggingSeg,
-      isTouch: ev.isTouch
+      dragState: {
+        eventStore: { defs: {}, instances: {} }, // TODO: better way to make empty event-store
+        origSeg: this.draggingSeg
+      }
     })
 
     let { dragMirror } = this.dragListener

+ 5 - 4
src/interactions/ExternalDraggableEvent.ts

@@ -2,16 +2,17 @@ import { IntentfulDragListenerImpl } from '../dnd/IntentfulDragListener'
 import ExternalDragging from './ExternalDragging'
 
 export interface ExternalDraggableEventSettings {
-
+  el: HTMLElement
+  event?: any
 }
 
 export default class ExternalDraggableEvent {
 
   externalDragging: ExternalDragging
 
-  constructor(el: HTMLElement, settings: ExternalDraggableEventSettings) {
-    let dragListener = new IntentfulDragListenerImpl(el)
-    this.externalDragging = new ExternalDragging(dragListener)
+  constructor(settings: ExternalDraggableEventSettings) {
+    let dragListener = new IntentfulDragListenerImpl(settings.el)
+    this.externalDragging = new ExternalDragging(dragListener, settings.event)
   }
 
   destroy() {

+ 180 - 21
src/interactions/ExternalDragging.ts

@@ -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
+}

+ 2 - 2
src/reducers/event-interaction.ts

@@ -3,6 +3,6 @@ import { Seg } from '../component/DateComponent'
 
 export interface EventInteractionState {
   eventStore: EventStore
-  origSeg: Seg
-  isTouch: boolean
+  origSeg?: Seg
+  willCreateEvent?: boolean // doesn't apply to resize :(
 }

+ 1 - 1
src/reducers/event-mutation.ts

@@ -68,7 +68,7 @@ export function getRelatedEvents(eventStore: EventStore, instanceId: string): Ev
   return newStore
 }
 
-function mergeStores(store0: EventStore, store1: EventStore): EventStore {
+export function mergeStores(store0: EventStore, store1: EventStore): EventStore {
   return {
     defs: assignTo({}, store0.defs, store1.defs),
     instances: assignTo({}, store0.instances, store1.instances)

+ 4 - 1
src/reducers/event-store.ts

@@ -4,7 +4,7 @@ import Calendar from '../Calendar'
 import { filterHash } from '../util/object'
 import { parseClassName, refineProps, ClassNameInput } from './utils'
 import { expandRecurring } from './recurring-events'
-import { applyMutationToRelated } from './event-mutation'
+import { applyMutationToRelated, mergeStores } from './event-mutation' // TODO: move mergeStores here
 
 // types
 
@@ -131,6 +131,9 @@ export function reduceEventStore(eventStore: EventStore, action: any, calendar:
     case 'MUTATE_EVENTS':
       return applyMutationToRelated(eventStore, action.instanceId, action.mutation, calendar)
 
+    case 'ADD_EVENTS':
+      return mergeStores(eventStore, action.eventStore)
+
     default:
       return eventStore
   }

+ 1 - 5
src/reducers/main.ts

@@ -121,11 +121,7 @@ export function reduce(state: CalendarState, action: any, calendar: Calendar): C
 
     case 'SET_DRAG':
       return assignTo({}, state, {
-        dragState: {
-          eventStore: action.displacement, // pass these in as one drag state?
-          isTouch: action.isTouch,
-          origSeg: action.origSeg
-        }
+        dragState: action.dragState
       })
 
     case 'CLEAR_DRAG':