瀏覽代碼

get resizing working. break down mutation methods

Adam Shaw 7 年之前
父節點
當前提交
3c00b4920b

+ 4 - 1
src/common/GlobalContext.ts

@@ -5,6 +5,7 @@ import DateSelecting from '../interactions/DateSelecting'
 import EventClicking from '../interactions/EventClicking'
 import EventHovering from '../interactions/EventHovering'
 import EventDragging from '../interactions/EventDragging'
+import EventResizing from '../interactions/EventResizing'
 import Calendar from '../Calendar'
 
 // TODO: how to accept external drags?
@@ -56,7 +57,8 @@ export class GlobalContext { // TODO: rename file to something better
       dateSelecting: new DateSelecting(component, globalContext),
       eventClicking: new EventClicking(component),
       eventHovering: new EventHovering(component),
-      eventDragging: new EventDragging(component, globalContext)
+      eventDragging: new EventDragging(component, globalContext),
+      eventResizing: new EventResizing(component)
     }
   }
 
@@ -68,6 +70,7 @@ export class GlobalContext { // TODO: rename file to something better
     listeners.eventClicking.destroy()
     listeners.eventHovering.destroy()
     listeners.eventDragging.destroy()
+    listeners.eventResizing.destroy()
 
     delete this.listenerHash[component.uid]
   }

+ 1 - 1
src/component/DateComponent.ts

@@ -381,7 +381,7 @@ export default abstract class DateComponent extends Component {
     let { renderedFlags } = this
 
     if ((!flags || flags.eventResize) && renderedFlags.eventResize) {
-      this.unrenderEventResize()
+      this.unrenderEventResizeState()
       renderedFlags.eventResize = false
     }
 

+ 2 - 2
src/component/renderers/EventRenderer.ts

@@ -452,7 +452,7 @@ export default class EventRenderer {
   }
 
 
-  isEventDefGenerallyEditable(eventDef) {
+  isEventDefGenerallyEditable(eventDef) { // NOT USED YET
     return false // TODO
   }
 
@@ -471,7 +471,7 @@ export default class EventRenderer {
 
   // Computes if the given event is allowed to be resized by the user at all
   isEventDefResizable(eventDef) {
-    return false // TODO
+    return true // TODO
   }
 
 

+ 31 - 12
src/interactions/EventDragging.ts

@@ -1,9 +1,10 @@
 import { default as DateComponent, Seg } from '../component/DateComponent'
 import { PointerDragEvent } from '../dnd/PointerDragListener'
 import HitDragListener, { isHitsEqual, Hit } from '../dnd/HitDragListener'
-import { EventMutation, computeEventDisplacement, diffDates } from '../reducers/event-mutation'
+import { EventMutation, diffDates, getRelatedEvents, applyMutationToAll } from '../reducers/event-mutation'
 import { GlobalContext } from '../common/GlobalContext'
 import { startOfDay } from '../datelib/marker'
+import { elementClosest } from '../util/dom-manip'
 
 export default class EventDragging {
 
@@ -42,6 +43,12 @@ export default class EventDragging {
 
     // to prevent from cloning the sourceEl before it is selected
     dragListener.dragMirror.disable()
+
+    let origTarget = ev.origEvent.target as HTMLElement
+
+    this.hitListener.dragListener.pointerListener.ignoreMove =
+      !this.component.isValidSegInteraction(origTarget) ||
+      elementClosest(origTarget, '.fc-resizer')
   }
 
   computeDragDelay(ev: PointerDragEvent): number {
@@ -87,20 +94,18 @@ export default class EventDragging {
 
   onHitOver = (hit, ev) => {
     let { initialHit } = this.hitListener
-
-    this.mutation = computeEventMutation(initialHit, hit)
-
     let calendar = hit.component.getCalendar()
-    let displacement = computeEventDisplacement(
+
+    let mutation = computeEventMutation(initialHit, hit)
+    let related = getRelatedEvents(
       calendar.state.eventStore,
-      this.draggingSeg.eventRange.eventInstance.instanceId,
-      this.mutation,
-      calendar
+      this.draggingSeg.eventRange.eventInstance.instanceId
     )
+    let mutatedRelated = applyMutationToAll(related, mutation, calendar)
 
     calendar.dispatch({
       type: 'SET_DRAG',
-      displacement,
+      displacement: mutatedRelated,
       origSeg: this.draggingSeg,
       isTouch: ev.isTouch
     })
@@ -114,12 +119,19 @@ export default class EventDragging {
       dragMirror.disable()
     }
 
-    dragMirror.needsRevert = isHitsEqual(initialHit, hit)
+    let isSame = isHitsEqual(initialHit, hit)
+    dragMirror.needsRevert = isSame
+
+    if (!isSame) {
+      this.mutation = mutation
+    }
   }
 
   onHitOut = (hit, ev) => { // 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
@@ -133,6 +145,10 @@ export default class EventDragging {
     dragMirror.needsRevert = true
   }
 
+  /*
+  TODO: rethinking ordering of onDocumentPointerUp firing,
+  we want something that will always fire LAST, in case drag never activated
+  */
   onDocumentPointerUp = (ev, isTouchScroll) => {
     if (
       !this.mutation &&
@@ -149,6 +165,8 @@ export default class EventDragging {
     let { initialHit } = this.hitListener
     let initialCalendar = initialHit.component.getCalendar()
 
+    // TODO: what about across calendars?
+
     initialCalendar.dispatch({
       type: 'CLEAR_DRAG'
     })
@@ -185,7 +203,7 @@ function computeEventMutation(hit0: Hit, hit1: Hit): EventMutation {
     }
   }
 
-  let dateDelta = diffDates(
+  let delta = diffDates(
     date0, date1,
     hit0.component.getDateEnv(),
     hit0.component === hit1.component ?
@@ -194,7 +212,8 @@ function computeEventMutation(hit0: Hit, hit1: Hit): EventMutation {
   )
 
   return {
-    dateDelta,
+    startDelta: delta,
+    endDelta: delta,
     standardProps
   }
 }

+ 6 - 0
src/interactions/EventHovering.ts

@@ -20,6 +20,12 @@ export default class EventHovering {
     let { component } = this
     let seg = (segEl as any).fcSeg // put there by EventRenderer
 
+    if (triggerType === 'eventMouseover') { // LAME way to test
+      segEl.classList.add('fc-allow-mouse-resize')
+    } else {
+      segEl.classList.remove('fc-allow-mouse-resize')
+    }
+
     if (component.isValidSegInteraction(segEl as HTMLElement)) {
       component.publiclyTrigger(triggerType, [
         {

+ 135 - 0
src/interactions/EventResizing.ts

@@ -0,0 +1,135 @@
+import { default as DateComponent, Seg } from '../component/DateComponent'
+import HitDragListener, { isHitsEqual, Hit } from '../dnd/HitDragListener'
+import { EventMutation, diffDates, getRelatedEvents, applyMutationToAll } from '../reducers/event-mutation'
+import { elementClosest } from '../util/dom-manip'
+import UnzonedRange from '../models/UnzonedRange'
+
+export default class EventDragging {
+
+  component: DateComponent
+  hitListener: HitDragListener
+  draggingSeg: Seg
+  mutation: EventMutation
+
+  constructor(component: DateComponent) {
+    this.component = component
+
+    let hitListener = this.hitListener = new HitDragListener(component)
+    hitListener.dragListener.pointerListener.selector = '.fc-resizer'
+    hitListener.dragListener.touchScrollAllowed = false
+    hitListener.on('pointerdown', this.onPointerDown)
+    hitListener.on('dragstart', this.onDragStart)
+    hitListener.on('hitover', this.onHitOver)
+    hitListener.on('hitout', this.onHitOut)
+    hitListener.on('dragend', this.onDragEnd)
+  }
+
+  destroy() {
+    this.hitListener.destroy()
+  }
+
+  onPointerDown = (ev) => {
+    let seg = this.querySeg(ev)
+    let eventInstanceId = seg.eventRange.eventInstance.instanceId
+
+    // if touch, need to be working with a selected event
+    this.hitListener.dragListener.pointerListener.ignoreMove =
+      !this.component.isValidSegInteraction(ev.origEvent.target) ||
+      (ev.isTouch && this.component.selectedEventInstanceId !== eventInstanceId)
+  }
+
+  onDragStart = (ev) => {
+    this.draggingSeg = this.querySeg(ev)
+  }
+
+  onHitOver = (hit, ev) => {
+    let calendar = this.component.getCalendar()
+    let { initialHit } = this.hitListener
+    let eventInstance = this.draggingSeg.eventRange.eventInstance
+
+    let mutation = computeMutation(
+      initialHit,
+      hit,
+      ev.el.classList.contains('.fc-start-resizer'),
+      eventInstance.range
+    )
+
+    if (!mutation) {
+      calendar.dispatch({
+        type: 'CLEAR_EVENT_RESIZE'
+      })
+    } else {
+      let related = getRelatedEvents(calendar.state.eventStore, eventInstance.instanceId)
+      let mutatedRelated = applyMutationToAll(related, mutation, calendar)
+
+      calendar.dispatch({
+        type: 'SET_EVENT_RESIZE',
+        eventResizeState: {
+          eventStore: mutatedRelated,
+          origSeg: this.draggingSeg,
+          isTouch: ev.isTouch
+        }
+      })
+
+      if (!isHitsEqual(initialHit, hit)) {
+        this.mutation = mutation
+      }
+    }
+  }
+
+  onHitOut = (hit, ev) => {
+    let calendar = this.component.getCalendar()
+
+    calendar.dispatch({
+      type: 'CLEAR_EVENT_RESIZE'
+    })
+
+    this.mutation = null
+  }
+
+  onDragEnd = (ev) => {
+    let calendar = this.component.getCalendar()
+
+    calendar.dispatch({
+      type: 'CLEAR_EVENT_RESIZE'
+    })
+
+    if (this.mutation) {
+      calendar.dispatch({
+        type: 'MUTATE_EVENTS',
+        mutation: this.mutation,
+        instanceId: this.draggingSeg.eventRange.eventInstance.instanceId
+      })
+    }
+
+    this.mutation = null
+    this.draggingSeg = null
+  }
+
+  querySeg(ev): Seg {
+    return elementClosest(ev.el, this.component.segSelector).fcSeg
+  }
+
+}
+
+function computeMutation(hit0: Hit, hit1: Hit, isFromStart: boolean, instanceRange: UnzonedRange): EventMutation {
+  let dateEnv = hit0.component.getDateEnv()
+  let date0 = hit0.range.start
+  let date1 = hit1.range.start
+
+  let delta = diffDates(
+    date0, date1,
+    dateEnv,
+    hit0.component.largeUnit
+  )
+
+  if (isFromStart) {
+    if (dateEnv.add(instanceRange.start, delta) > instanceRange.end) {
+      return { startDelta: delta }
+    }
+  } else {
+    if (dateEnv.add(instanceRange.end, delta) > instanceRange.start) {
+      return { endDelta: delta }
+    }
+  }
+}

+ 40 - 16
src/reducers/event-mutation.ts

@@ -6,13 +6,38 @@ import { assignTo } from '../util/object'
 import Calendar from '../Calendar'
 
 export interface EventMutation {
-  dateDelta?: Duration
+  startDelta?: Duration
   endDelta?: Duration
   standardProps?: any // for the def
   extendedProps?: any // for the def
 }
 
-export function computeEventDisplacement(eventStore: EventStore, instanceId: string, mutation: EventMutation, calendar: Calendar): EventStore {
+export function applyMutationToRelated(eventStore: EventStore, instanceId: string, mutation: EventMutation, calendar: Calendar): EventStore {
+  let relatedStore = getRelatedEvents(eventStore, instanceId)
+  relatedStore = applyMutationToAll(relatedStore, mutation, calendar)
+  return mergeStores(eventStore, relatedStore)
+}
+
+export function applyMutationToAll(eventStore: EventStore, mutation: EventMutation, calendar: Calendar): EventStore {
+  let newStore = { defs: {}, instances: {} }
+
+  for (let defId in eventStore.defs) {
+    let def = eventStore.defs[defId]
+
+    newStore.defs[defId] = applyMutationToDef(def, mutation)
+  }
+
+  for (let instanceId in eventStore.instances) {
+    let instance = eventStore.instances[instanceId]
+    let def = eventStore.defs[instance.defId]
+
+    newStore.instances[instanceId] = applyMutationToInstance(instance, def, mutation, calendar)
+  }
+
+  return newStore
+}
+
+export function getRelatedEvents(eventStore: EventStore, instanceId: string): EventStore {
   let newStore = { defs: {}, instances: {} } // TODO: better name
   let eventInstance = eventStore.instances[instanceId]
   let eventDef = eventStore.defs[eventInstance.defId]
@@ -24,19 +49,18 @@ export function computeEventDisplacement(eventStore: EventStore, instanceId: str
       let def = eventStore.defs[defId]
 
       if (def === eventDef || matchGroupId && matchGroupId === def.groupId) {
-        newStore.defs[defId] = applyMutationToDef(def, mutation)
+        newStore.defs[defId] = def
       }
     }
 
     for (let instanceId in eventStore.instances) {
       let instance = eventStore.instances[instanceId]
-      let def = newStore.defs[instance.defId] // the already-modified version
 
       if (
         instance === eventInstance ||
         matchGroupId && matchGroupId === eventStore.defs[instance.defId].groupId
       ) {
-        newStore.instances[instanceId] = applyMutationToInstance(instance, def, mutation, calendar)
+        newStore.instances[instanceId] = instance
       }
     }
   }
@@ -44,14 +68,10 @@ export function computeEventDisplacement(eventStore: EventStore, instanceId: str
   return newStore
 }
 
-// Applying
-
-export function applyMutation(eventStore: EventStore, instanceId: string, mutation: EventMutation, calendar: Calendar): EventStore {
-  let displacement = computeEventDisplacement(eventStore, instanceId, mutation, calendar)
-
+function mergeStores(store0: EventStore, store1: EventStore): EventStore {
   return {
-    defs: assignTo({}, eventStore.defs, displacement.defs),
-    instances: assignTo({}, eventStore.instances, displacement.instances)
+    defs: assignTo({}, store0.defs, store1.defs),
+    instances: assignTo({}, store0.instances, store1.instances)
   }
 }
 
@@ -62,6 +82,10 @@ function applyMutationToDef(eventDef: EventDef, mutation: EventMutation) {
     assignTo(copy, mutation.standardProps)
   }
 
+  if (mutation.startDelta || mutation.endDelta) {
+    copy.hasEnd = true
+  }
+
   if (mutation.extendedProps) {
     copy.extendedProps = assignTo({}, copy.extendedProps, mutation.extendedProps)
   }
@@ -88,10 +112,10 @@ function applyMutationToInstance(
     copy.range = new UnzonedRange(start, end)
   }
 
-  if (mutation.dateDelta) {
+  if (mutation.startDelta) {
     copy.range = new UnzonedRange(
-      dateEnv.add(copy.range.start, mutation.dateDelta),
-      dateEnv.add(copy.range.end, mutation.dateDelta)
+      dateEnv.add(copy.range.start, mutation.startDelta),
+      copy.range.end
     )
   }
 
@@ -102,7 +126,7 @@ function applyMutationToInstance(
     )
   } else if (mutation.endDelta) {
     copy.range = new UnzonedRange(
-      copy.range.end,
+      copy.range.start,
       dateEnv.add(copy.range.end, mutation.endDelta),
     )
   }

+ 2 - 2
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 { applyMutation } from './event-mutation'
+import { applyMutationToRelated } from './event-mutation'
 
 // types
 
@@ -129,7 +129,7 @@ export function reduceEventStore(eventStore: EventStore, action: any, calendar:
       return excludeSource(eventStore, action.sourceId)
 
     case 'MUTATE_EVENTS':
-      return applyMutation(eventStore, action.instanceId, action.mutation, calendar)
+      return applyMutationToRelated(eventStore, action.instanceId, action.mutation, calendar)
 
     default:
       return eventStore

+ 10 - 0
src/reducers/main.ts

@@ -143,6 +143,16 @@ export function reduce(state: CalendarState, action: any, calendar: Calendar): C
         selectedEventInstanceId: null
       })
 
+    case 'SET_EVENT_RESIZE':
+      return assignTo({}, state, {
+        eventResizeState: action.eventResizeState
+      })
+
+    case 'CLEAR_EVENT_RESIZE':
+      return assignTo({}, state, {
+        eventResizeState: null
+      })
+
   }
 
   return newState