Browse Source

more updates

Adam Shaw 7 years ago
parent
commit
5e42720902

+ 1 - 0
src/Calendar.ts

@@ -934,6 +934,7 @@ export default class Calendar {
   }
 
 
+  // TODO: receive pev?
   triggerDayClick(dateSpan: DateSpan, dayEl: HTMLElement, view: View, ev: UIEvent) {
     this.publiclyTrigger('dayClick', [
       {

+ 6 - 6
src/component/DateComponent.ts

@@ -994,17 +994,17 @@ export default abstract class DateComponent extends Component {
   }
 
 
-  isValidSegInteraction(evTarget: HTMLElement) {
+  isValidSegDownEl(el: HTMLElement) {
     return !this.dragState &&
       !this.eventResizeState &&
-      !elementClosest(evTarget, '.fc-helper')
+      !elementClosest(el, '.fc-helper')
   }
 
 
-  isValidDateInteraction(evTarget: HTMLElement) {
-    return !elementClosest(evTarget, this.segSelector) &&
-      !elementClosest(evTarget, '.fc-more') && // a "more.." link
-      !elementClosest(evTarget, 'a[data-goto]') // a clickable nav link
+  isValidDateDownEl(el: HTMLElement) {
+    return !elementClosest(el, this.segSelector) &&
+      !elementClosest(el, '.fc-more') && // a "more.." link
+      !elementClosest(el, 'a[data-goto]') // a clickable nav link
   }
 
 }

+ 9 - 1
src/component/renderers/EventRenderer.ts

@@ -170,7 +170,7 @@ export default class EventRenderer {
         }
 
         if (el) {
-          (el as any).fcSeg = seg // used by handlers
+          setElSeg(el, seg)
           seg.el = el
           renderedSegs.push(seg)
         }
@@ -483,3 +483,11 @@ export default class EventRenderer {
   }
 
 }
+
+function setElSeg(el: HTMLElement, seg: Seg) {
+  (el as any).fcSeg = seg
+}
+
+export function getElSeg(el: HTMLElement): Seg | null {
+  return (el as any).fcSeg || null
+}

+ 2 - 2
src/interactions-external/ExternalElementDragging.ts

@@ -23,7 +23,7 @@ export default class ExternalElementDragging {
     let hitDragging = this.hitDragging = new HitDragging(dragging, browserContext.componentHash)
     hitDragging.requireInitial = false
     hitDragging.emitter.on('dragstart', this.onDragStart)
-    hitDragging.emitter.on('hitchange', this.onHitChange)
+    hitDragging.emitter.on('hitupdate', this.onHitUpdate)
     hitDragging.emitter.on('dragend', this.onDragEnd)
 
     this.explicitEventCreationData = rawEventCreationData ? processExplicitData(rawEventCreationData) : null
@@ -34,7 +34,7 @@ export default class ExternalElementDragging {
     this.eventCreationData = this.explicitEventCreationData || getDraggedElMeta(ev.subjectEl)
   }
 
-  onHitChange = (hit: Hit | null, isFinal: boolean, ev: PointerDragEvent) => {
+  onHitUpdate = (hit: Hit | null, isFinal: boolean, ev: PointerDragEvent) => {
     let { dragging } = this.hitDragging
     let receivingCalendar: Calendar = null
     let addableEventStore: EventStore = null

+ 15 - 10
src/interactions/DateClicking.ts

@@ -3,6 +3,10 @@ import FeaturefulElementDragging from '../dnd/FeaturefulElementDragging'
 import HitDragging, { isHitsEqual } from './HitDragging'
 import { PointerDragEvent } from '../dnd/PointerDragging'
 
+/*
+Monitors when the user clicks on a specific date/time of a component.
+A pointerdown+pointerup on the same "hit" constitutes a click.
+*/
 export default class DateClicking {
 
   component: DateComponent
@@ -11,33 +15,34 @@ export default class DateClicking {
 
   constructor(component: DateComponent) {
     this.component = component
+
+    // we DO want to watch pointer moves because otherwise finalHit won't get populated
     this.dragging = new FeaturefulElementDragging(component.el)
-    this.hitDragging = new HitDragging(this.dragging, component)
-    this.hitDragging.emitter.on('pointerdown', this.onPointerDown)
-    this.hitDragging.emitter.on('dragend', this.onDragEnd)
+
+    let hitDragging = this.hitDragging = new HitDragging(this.dragging, component)
+    hitDragging.emitter.on('pointerdown', this.handlePointerDown)
+    hitDragging.emitter.on('dragend', this.handleDragEnd)
   }
 
   destroy() {
     this.dragging.destroy()
   }
 
-  onPointerDown = (ev: PointerDragEvent) => {
+  handlePointerDown = (ev: PointerDragEvent) => {
     let { dragging } = this
 
     // do this in pointerdown (not dragend) because DOM might be mutated by the time dragend is fired
     dragging.setIgnoreMove(
-      !this.component.isValidDateInteraction(dragging.pointer.downEl)
+      !this.component.isValidDateDownEl(dragging.pointer.downEl!)
     )
   }
 
-  onDragEnd = (ev: PointerDragEvent) => {
+  // won't even fire if moving was ignored
+  handleDragEnd = (ev: PointerDragEvent) => {
     let { component } = this
     let { pointer } = this.dragging
 
-    if (
-      !pointer.shouldIgnoreMove && // not ignored in onPointerDown
-      !pointer.wasTouchScroll
-    ) {
+    if (!pointer.wasTouchScroll) {
       let { initialHit, finalHit } = this.hitDragging
 
       if (initialHit && finalHit && isHitsEqual(initialHit, finalHit)) {

+ 26 - 24
src/interactions/DateSelecting.ts

@@ -8,12 +8,16 @@ import { PointerDragEvent } from '../dnd/PointerDragging'
 import FeaturefulElementDragging from '../dnd/FeaturefulElementDragging'
 import browserContext from '../common/browser-context'
 
+/*
+Tracks when the user selects a portion of time of a component,
+constituted by a drag over date cells, with a possible delay at the beginning of the drag.
+*/
 export default class DateSelecting {
 
   component: DateComponent
   dragging: FeaturefulElementDragging
   hitDragging: HitDragging
-  dragSelection: DateSpan
+  dragSelection: DateSpan | null = null
 
   constructor(component: DateComponent) {
     this.component = component
@@ -22,56 +26,50 @@ export default class DateSelecting {
     this.dragging.touchScrollAllowed = false
 
     let hitDragging = this.hitDragging = new HitDragging(this.dragging, component)
-    hitDragging.emitter.on('pointerdown', this.onPointerDown)
-    hitDragging.emitter.on('dragstart', this.onDragStart)
-    hitDragging.emitter.on('hitchange', this.onHitChange)
+    hitDragging.emitter.on('pointerdown', this.handlePointerDown)
+    hitDragging.emitter.on('dragstart', this.handleDragStart)
+    hitDragging.emitter.on('hitupdate', this.handleHitUpdate)
   }
 
   destroy() {
     this.dragging.destroy()
   }
 
-  onPointerDown = (ev: PointerDragEvent) => {
+  handlePointerDown = (ev: PointerDragEvent) => {
     let { component, dragging } = this
     let isValid = component.opt('selectable') &&
-      component.isValidDateInteraction(ev.origEvent.target as HTMLElement)
+      component.isValidDateDownEl(ev.origEvent.target as HTMLElement)
 
     // don't bother to watch expensive moves if component won't do selection
     dragging.setIgnoreMove(!isValid)
 
-    dragging.delay = (isValid && ev.isTouch) ?
-      getComponentDelay(component) :
-      null
+    // if touch, require user to hold down
+    dragging.delay = ev.isTouch ? getComponentTouchDelay(component) : null
   }
 
-  onDragStart = (ev: PointerDragEvent) => {
-    browserContext.unselectDates(ev)
+  handleDragStart = (ev: PointerDragEvent) => {
+    browserContext.unselectDates(ev) // clear selection from all other calendars/components
   }
 
-  onHitChange = (hit: Hit | null, isFinal: boolean) => {
+  handleHitUpdate = (hit: Hit | null, isFinal: boolean) => {
     let calendar = this.component.getCalendar()
-    let dragSelection: DateSpan = null
+    let dragSelection: DateSpan | null = null
 
     if (hit) {
       dragSelection = computeSelection(
-        this.hitDragging.initialHit.dateSpan,
+        this.hitDragging.initialHit!.dateSpan,
         hit.dateSpan
       )
     }
 
     if (dragSelection) {
-      calendar.dispatch({
-        type: 'SELECT',
-        selection: dragSelection
-      })
+      calendar.dispatch({ type: 'SELECT', selection: dragSelection })
     } else if (!isFinal) { // only unselect if moved away while dragging
-      calendar.dispatch({
-        type: 'UNSELECT'
-      })
+      calendar.dispatch({ type: 'UNSELECT' })
     }
 
     if (!isFinal) {
-      this.dragSelection = dragSelection
+      this.dragSelection = dragSelection // only clear if moved away from all hits while dragging
     }
   }
 
@@ -85,7 +83,9 @@ export default class DateSelecting {
 
       this.dragSelection = null
 
-    } else if (!wasTouchScroll && component.selection) { // only unselect if this component has a selection
+    // only unselect if this component has a selection.
+    // otherwise, we might be clearing another component's new selection in the same calendar.
+    } else if (!wasTouchScroll && component.selection) {
       let unselectAuto = component.opt('unselectAuto')
       let unselectCancel = component.opt('unselectCancel')
 
@@ -97,11 +97,13 @@ export default class DateSelecting {
 
 }
 
-function getComponentDelay(component): number {
+function getComponentTouchDelay(component: DateComponent): number {
   let delay = component.opt('selectLongPressDelay')
+
   if (delay == null) {
     delay = component.opt('longPressDelay')
   }
+
   return delay
 }
 

+ 11 - 10
src/interactions/EventClicking.ts

@@ -1,6 +1,10 @@
 import DateComponent from '../component/DateComponent'
 import { listenBySelector } from '../util/dom-event'
+import { getElSeg } from '../component/renderers/EventRenderer'
 
+/*
+Detects when the user clicks on an event within a DateComponent
+*/
 export default class EventClicking {
 
   component: DateComponent
@@ -8,30 +12,27 @@ export default class EventClicking {
 
   constructor(component: DateComponent) {
     this.component = component
+
     this.destroy = listenBySelector(
       component.el,
       'click',
       component.segSelector,
-      this.onSegClick
+      this.handleSegClick
     )
   }
 
-  onSegClick = (ev: UIEvent, segEl: HTMLElement) => {
+  handleSegClick = (ev: Event, segEl: HTMLElement) => {
     let { component } = this
-    let seg = (segEl as any).fcSeg // put there by EventRenderer
+    let seg = getElSeg(segEl)!
 
-    if (component.isValidSegInteraction(segEl as HTMLElement)) {
-      let res = component.publiclyTrigger('eventClick', [ // can return `false` to cancel
+    if (component.isValidSegDownEl(ev.target as HTMLElement)) {
+      component.publiclyTrigger('eventClick', [
         {
-          event: seg.eventRange.eventInstance, // TODO: correct arg!
+          event: seg.eventRange!.eventInstance, // TODO: correct arg!
           jsEvent: ev,
           view: component.view
         }
       ])
-
-      if (res === false) {
-        ev.preventDefault() // don't visit link
-      }
     }
   }
 

+ 10 - 6
src/interactions/EventDragging.ts

@@ -1,4 +1,5 @@
 import { default as DateComponent, Seg } from '../component/DateComponent'
+import { getElSeg } from '../component/renderers/EventRenderer'
 import { PointerDragEvent } from '../dnd/PointerDragging'
 import HitDragging, { isHitsEqual, Hit } from './HitDragging'
 import { EventMutation, diffDates, getRelatedEvents, applyMutationToAll } from '../reducers/event-mutation'
@@ -31,7 +32,7 @@ export default class EventDragging {
     hitDragging.useSubjectCenter = true
     hitDragging.emitter.on('pointerdown', this.onPointerDown)
     hitDragging.emitter.on('dragstart', this.onDragStart)
-    hitDragging.emitter.on('hitchange', this.onHitChange)
+    hitDragging.emitter.on('hitupdate', this.onHitUpdate)
     hitDragging.emitter.on('dragend', this.onDragEnd)
   }
 
@@ -50,14 +51,14 @@ export default class EventDragging {
     let origTarget = ev.origEvent.target as HTMLElement
 
     dragging.setIgnoreMove(
-      !this.component.isValidSegInteraction(origTarget) ||
+      !this.component.isValidSegDownEl(origTarget) ||
       Boolean(elementClosest(origTarget, '.fc-resizer'))
     )
   }
 
   computeDragDelay(ev: PointerDragEvent): number {
     if (ev.isTouch) {
-      let seg = (ev.subjectEl as any).fcSeg
+      let seg = getElSeg(ev.subjectEl)
       let eventInstanceId = seg.eventRange.eventInstance.instanceId
 
       if (eventInstanceId !== this.component.selectedEventInstanceId) {
@@ -67,7 +68,7 @@ export default class EventDragging {
   }
 
   onDragStart = (ev: PointerDragEvent) => {
-    this.draggingSeg = (ev.subjectEl as any).fcSeg
+    this.draggingSeg = getElSeg(ev.subjectEl)
 
     if (ev.isTouch) {
       let eventInstanceId = this.draggingSeg.eventRange.eventInstance.instanceId
@@ -83,7 +84,7 @@ export default class EventDragging {
     }
   }
 
-  onHitChange = (hit: Hit | null, isFinal: boolean) => {
+  onHitUpdate = (hit: Hit | null, isFinal: boolean) => {
     let { initialHit } = this.hitDragging // guaranteed
     let initialCalendar = initialHit.component.getCalendar()
     let receivingCalendar: Calendar = null
@@ -99,7 +100,10 @@ export default class EventDragging {
     if (hit) {
       receivingCalendar = hit.component.getCalendar()
 
-      if (!isHitsEqual(initialHit, hit)) {
+      if (
+        initialCalendar !== receivingCalendar || // TODO: write test for this
+        !isHitsEqual(initialHit, hit)
+      ) {
         validMutation = computeEventMutation(initialHit, hit)
 
         if (validMutation) {

+ 23 - 13
src/interactions/EventHovering.ts

@@ -1,6 +1,11 @@
 import DateComponent from '../component/DateComponent'
 import { listenToHoverBySelector } from '../util/dom-event'
+import { getElSeg } from '../component/renderers/EventRenderer'
 
+/*
+Triggers events and adds/removes core classNames when the user's pointer
+enters/leaves event-elements of a component.
+*/
 export default class EventHovering {
 
   component: DateComponent
@@ -8,28 +13,33 @@ export default class EventHovering {
 
   constructor(component: DateComponent) {
     this.component = component
+
     this.destroy = listenToHoverBySelector(
       component.el,
       component.segSelector,
-      this.handleSegEv.bind(this, 'eventMouseover'),
-      this.handleSegEv.bind(this, 'eventMouseout')
+      this.handleSegEnter,
+      this.handleSegLeave
     )
   }
 
-  handleSegEv(triggerType: string, ev: UIEvent, segEl: HTMLElement) {
-    let { component } = this
-    let seg = (segEl as any).fcSeg // put there by EventRenderer
+  handleSegEnter = (ev: Event, segEl: HTMLElement) => {
+    segEl.classList.add('fc-allow-mouse-resize')
+    this.triggerEvent('eventMouseover', ev, segEl)
+  }
 
-    if (triggerType === 'eventMouseover') { // LAME way to test
-      segEl.classList.add('fc-allow-mouse-resize')
-    } else {
-      segEl.classList.remove('fc-allow-mouse-resize')
-    }
+  handleSegLeave = (ev: Event, segEl: HTMLElement) => {
+    segEl.classList.remove('fc-allow-mouse-resize')
+    this.triggerEvent('eventMouseout', ev, segEl)
+  }
+
+  triggerEvent(publicEvName: string, ev: Event, segEl: HTMLElement) {
+    let { component } = this
+    let seg = getElSeg(segEl)!
 
-    if (component.isValidSegInteraction(segEl as HTMLElement)) {
-      component.publiclyTrigger(triggerType, [
+    if (component.isValidSegDownEl(ev.target as HTMLElement)) {
+      component.publiclyTrigger(publicEvName, [
         {
-          event: seg.eventRange.eventInstance, // TODO: correct arg!
+          event: seg.eventRange!.eventInstance, // TODO: correct arg!
           jsEvent: ev,
           view: component.view
         }

+ 5 - 4
src/interactions/EventResizing.ts

@@ -5,6 +5,7 @@ import { elementClosest } from '../util/dom-manip'
 import UnzonedRange from '../models/UnzonedRange'
 import FeaturefulElementDragging from '../dnd/FeaturefulElementDragging'
 import { PointerDragEvent } from '../dnd/PointerDragging'
+import { getElSeg } from '../component/renderers/EventRenderer'
 
 export default class EventDragging {
 
@@ -24,7 +25,7 @@ export default class EventDragging {
     let hitDragging = this.hitDragging = new HitDragging(this.dragging, component)
     hitDragging.emitter.on('pointerdown', this.onPointerDown)
     hitDragging.emitter.on('dragstart', this.onDragStart)
-    hitDragging.emitter.on('hitchange', this.onHitChange)
+    hitDragging.emitter.on('hitupdate', this.onHitUpdate)
     hitDragging.emitter.on('dragend', this.onDragEnd)
   }
 
@@ -38,7 +39,7 @@ export default class EventDragging {
 
     // if touch, need to be working with a selected event
     this.dragging.setIgnoreMove(
-      !this.component.isValidSegInteraction(ev.origEvent.target) ||
+      !this.component.isValidSegDownEl(ev.origEvent.target) ||
       (ev.isTouch && this.component.selectedEventInstanceId !== eventInstanceId)
     )
   }
@@ -47,7 +48,7 @@ export default class EventDragging {
     this.draggingSeg = this.querySeg(ev)
   }
 
-  onHitChange = (hit: Hit | null, isFinal: boolean, ev: PointerDragEvent) => {
+  onHitUpdate = (hit: Hit | null, isFinal: boolean, ev: PointerDragEvent) => {
     let calendar = this.component.getCalendar()
     let { initialHit } = this.hitDragging
     let eventInstance = this.draggingSeg.eventRange.eventInstance
@@ -106,7 +107,7 @@ export default class EventDragging {
   }
 
   querySeg(ev: PointerDragEvent): Seg {
-    return (elementClosest(ev.subjectEl as HTMLElement, this.component.segSelector) as any).fcSeg
+    return getElSeg(elementClosest(ev.subjectEl as HTMLElement, this.component.segSelector))
   }
 
 }

+ 27 - 19
src/interactions/HitDragging.ts

@@ -14,6 +14,10 @@ export interface Hit {
 }
 
 /*
+Tracks movement over multiple droppable areas (aka "hits")
+that exist in one or more DateComponents.
+Relies on an existing draggable.
+
 emits:
 - pointerdown
 - dragstart
@@ -33,10 +37,10 @@ export default class HitDragging {
   requireInitial: boolean = true // if doesn't start out on a hit, won't emit any events
 
   // internal state
-  initialHit: Hit
-  movingHit: Hit
-  finalHit: Hit // won't ever be populated if shouldIgnoreMove
-  coordAdjust: Point
+  initialHit: Hit | null = null
+  movingHit: Hit | null = null
+  finalHit: Hit | null = null // won't ever be populated if shouldIgnoreMove
+  coordAdjust?: Point
 
   constructor(dragging: ElementDragging, droppable: DateComponent | DateComponentHash) {
 
@@ -119,7 +123,7 @@ export default class HitDragging {
 
   handleDragEnd = (ev: PointerDragEvent) => {
     if (this.movingHit) {
-      this.emitter.trigger('hitchange', null, true, ev)
+      this.emitter.trigger('hitupdate', null, true, ev)
     }
 
     this.finalHit = this.movingHit
@@ -127,39 +131,43 @@ export default class HitDragging {
     this.emitter.trigger('dragend', ev)
   }
 
-  handleMove(ev: PointerDragEvent, force?) {
+  handleMove(ev: PointerDragEvent, forceHandle?: boolean) {
     let hit = this.queryHit(
-      ev.pageX + this.coordAdjust.left,
-      ev.pageY + this.coordAdjust.top
+      ev.pageX + this.coordAdjust!.left,
+      ev.pageY + this.coordAdjust!.top
     )
 
-    if (force || !isHitsEqual(this.movingHit, hit)) {
+    if (forceHandle || !isHitsEqual(this.movingHit, hit)) {
       this.movingHit = hit
-      this.emitter.trigger('hitchange', hit, false, ev)
+      this.emitter.trigger('hitupdate', hit, false, ev)
     }
   }
 
   prepareComponents() {
-    for (let id in this.droppableHash) {
-      let component = this.droppableHash[id]
-      component.buildCoordCaches()
+    let { droppableHash } = this
+
+    for (let id in droppableHash) {
+      droppableHash[id].buildCoordCaches()
     }
   }
 
-  queryHit(x, y): Hit {
-    for (let id in this.droppableHash) {
-      let component = this.droppableHash[id]
-      let hit = component.queryHit(x, y)
+  queryHit(x: number, y: number): Hit | null {
+    let { droppableHash } = this
+
+    for (let id in droppableHash) {
+      let hit = droppableHash[id].queryHit(x, y)
 
       if (hit) {
         return hit
       }
     }
+
+    return null
   }
 
 }
 
-export function isHitsEqual(hit0: Hit, hit1: Hit) {
+export function isHitsEqual(hit0: Hit | null, hit1: Hit | null): boolean {
   if (!hit0 && !hit1) {
     return true
   }
@@ -168,5 +176,5 @@ export function isHitsEqual(hit0: Hit, hit1: Hit) {
     return false
   }
 
-  return isDateSpansEqual(hit0.dateSpan, hit1.dateSpan)
+  return isDateSpansEqual(hit0!.dateSpan, hit1!.dateSpan)
 }