Adam Shaw 7 tahun lalu
induk
melakukan
0c6554874d

+ 3 - 4
src/common/GlobalContext.ts

@@ -8,7 +8,6 @@ import EventDragging from '../interactions/EventDragging'
 import EventResizing from '../interactions/EventResizing'
 import Calendar from '../Calendar'
 
-// TODO: how to accept external drags?
 // TODO: rename to BrowserContext?
 
 export class GlobalContext { // TODO: rename file to something better
@@ -41,9 +40,9 @@ export class GlobalContext { // TODO: rename file to something better
   }
 
   bind() {
-    this.pointerUpListener = new PointerDragListener(document as any)
-    this.pointerUpListener.ignoreMove = true
-    this.pointerUpListener.on('pointerup', this.onPointerUp)
+    let pointerUpListener = this.pointerUpListener = new PointerDragListener(document as any)
+    pointerUpListener.ignoreMove = true
+    pointerUpListener.emitter.on('pointerup', this.onPointerUp)
   }
 
   unbind() {

+ 1 - 1
src/dnd/DragMirror.ts

@@ -56,7 +56,7 @@ export default class DragMirror {
   onPointerDown = (ev: PointerDragEvent) => {
     this.pointerDownX = ev.pageX
     this.pointerDownY = ev.pageY
-    this.sourceEl = ev.el
+    this.sourceEl = ev.subjectEl
   }
 
   onDragStart = (ev: PointerDragEvent) => {

+ 1 - 1
src/dnd/HitDragListener.ts

@@ -85,7 +85,7 @@ export default class HitDragListener {
   processFirstCoord(ev: PointerDragEvent) {
     let origPoint = { left: ev.pageX, top: ev.pageY }
     let adjustedPoint = origPoint
-    let subjectEl = ev.el as HTMLElement
+    let subjectEl = ev.subjectEl
     let subjectRect
 
     if (subjectEl !== (document as any)) {

+ 3 - 3
src/dnd/IntentfulDragListener.ts

@@ -54,9 +54,9 @@ export class IntentfulDragListenerImpl implements IntentfulDragListener {
     this.dragMirror = new DragMirror(this)
 
     let pointerListener = this.pointerListener = new PointerDragListener(containerEl)
-    pointerListener.on('pointerdown', this.onPointerDown)
-    pointerListener.on('pointermove', this.onPointerMove)
-    pointerListener.on('pointerup', this.onPointerUp)
+    pointerListener.emitter.on('pointerdown', this.onPointerDown)
+    pointerListener.emitter.on('pointermove', this.onPointerMove)
+    pointerListener.emitter.on('pointerup', this.onPointerUp)
   }
 
   destroy() {

+ 85 - 58
src/dnd/PointerDragListener.ts

@@ -1,22 +1,19 @@
 import * as exportHooks from '../exports'
 import { elementClosest } from '../util/dom-manip'
 import { default as EmitterMixin } from '../common/EmitterMixin'
-import { isPrimaryMouseButton } from '../util/dom-event'
 
 (exportHooks as any).touchMouseIgnoreWait = 500
 
 export interface PointerDragEvent {
   origEvent: UIEvent
   isTouch: boolean
-  el: HTMLElement // rename to target?
+  subjectEl: HTMLElement
   pageX: number
   pageY: number
 }
 
-export type PointerEventHandler = (ev: PointerDragEvent) => void
-
 /*
-events:
+emitted events:
 - pointerdown
 - pointermove
 - pointerup
@@ -30,40 +27,47 @@ export default class PointerDragListener {
 
   // options
   selector: string
+  handleSelector: string
   ignoreMove: boolean = false // bad name?
 
+  // internal states
   isDragging: boolean = false
-  isDraggingTouch: boolean = false
+  isTouchDragging: boolean = false
   isTouchScroll: boolean = false
 
   constructor(containerEl: HTMLElement) {
     this.containerEl = containerEl
     this.emitter = new EmitterMixin()
-    containerEl.addEventListener('mousedown', this.onMouseDown)
-    containerEl.addEventListener('touchstart', this.onTouchStart)
+    containerEl.addEventListener('mousedown', this.handleMouseDown)
+    containerEl.addEventListener('touchstart', this.handleTouchStart)
     listenerCreated()
   }
 
   destroy() {
-    this.containerEl.removeEventListener('mousedown', this.onMouseDown)
-    this.containerEl.removeEventListener('touchstart', this.onTouchStart)
+    this.containerEl.removeEventListener('mousedown', this.handleMouseDown)
+    this.containerEl.removeEventListener('touchstart', this.handleTouchStart)
     listenerDestroyed()
   }
 
-  on(name, handler: PointerEventHandler) {
-    this.emitter.on(name, handler)
-  }
+  tryStart(ev: UIEvent): boolean {
+    let subjectEl = this.queryValidSubjectEl(ev)
+    let downEl = ev.target as HTMLElement
 
-  maybeStart(ev: UIEvent) {
-    if ((this.subjectEl = this.queryValidSubjectEl(ev))) {
-      this.downEl = ev.target as HTMLElement
+    if (
+      subjectEl &&
+      (!this.handleSelector || elementClosest(downEl, this.handleSelector))
+    ) {
+      this.subjectEl = subjectEl
+      this.downEl = downEl
       this.isDragging = true // do this first so cancelTouchScroll will work
       this.isTouchScroll = false
       return true
     }
+
+    return false
   }
 
-  cleanup() { // do this last, so that pointerup has access to props
+  cleanup() {
     isWindowTouchMoveCancelled = false
     this.isDragging = false
     this.subjectEl = null
@@ -71,10 +75,6 @@ export default class PointerDragListener {
     // keep isTouchScroll around for later access
   }
 
-  onTouchScroll = () => {
-    this.isTouchScroll = true
-  }
-
   queryValidSubjectEl(ev: UIEvent): HTMLElement {
     if (this.selector) {
       return elementClosest(ev.target as HTMLElement, this.selector)
@@ -83,88 +83,102 @@ export default class PointerDragListener {
     }
   }
 
-  onMouseDown = (ev: MouseEvent) => {
+
+  // Mouse
+  // ----------------------------------------------------------------------------------------------------
+
+  handleMouseDown = (ev: MouseEvent) => {
     if (
       !this.shouldIgnoreMouse() &&
       isPrimaryMouseButton(ev) &&
-      this.maybeStart(ev)
+      this.tryStart(ev)
     ) {
-      this.emitter.trigger('pointerdown', createMouseEvent(ev, this.subjectEl))
+      this.emitter.trigger('pointerdown', createEventFromMouse(ev, this.subjectEl))
 
       if (!this.ignoreMove) {
-        document.addEventListener('mousemove', this.onMouseMove)
+        document.addEventListener('mousemove', this.handleMouseMove)
       }
 
-      document.addEventListener('mouseup', this.onMouseUp)
+      document.addEventListener('mouseup', this.handleMouseUp)
     }
   }
 
-  onMouseMove = (ev: MouseEvent) => {
-    this.emitter.trigger('pointermove', createMouseEvent(ev, this.subjectEl))
+  handleMouseMove = (ev: MouseEvent) => {
+    this.emitter.trigger('pointermove', createEventFromMouse(ev, this.subjectEl))
   }
 
-  onMouseUp = (ev: MouseEvent) => {
-    document.removeEventListener('mousemove', this.onMouseMove)
-    document.removeEventListener('mouseup', this.onMouseUp)
+  handleMouseUp = (ev: MouseEvent) => {
+    document.removeEventListener('mousemove', this.handleMouseMove)
+    document.removeEventListener('mouseup', this.handleMouseUp)
+
+    this.emitter.trigger('pointerup', createEventFromMouse(ev, this.subjectEl))
 
-    this.emitter.trigger('pointerup', createMouseEvent(ev, this.subjectEl))
+    this.cleanup() // call last so that pointerup has access to props
+  }
 
-    this.cleanup()
+  shouldIgnoreMouse() {
+    return ignoreMouseDepth || this.isTouchDragging
   }
 
-  onTouchStart = (ev: TouchEvent) => {
-    if (this.maybeStart(ev)) {
-      this.isDraggingTouch = true
 
-      this.emitter.trigger('pointerdown', createTouchEvent(ev, this.subjectEl))
+  // Touch
+  // ----------------------------------------------------------------------------------------------------
+
+  handleTouchStart = (ev: TouchEvent) => {
+    if (this.tryStart(ev)) {
+      this.isTouchDragging = true
+
+      this.emitter.trigger('pointerdown', createEventFromTouch(ev, this.subjectEl))
 
       // unlike mouse, need to attach to target, not document
       // https://stackoverflow.com/a/45760014
       let target = ev.target
 
       if (!this.ignoreMove) {
-        target.addEventListener('touchmove', this.onTouchMove)
+        target.addEventListener('touchmove', this.handleTouchMove)
       }
 
-      target.addEventListener('touchend', this.onTouchEnd)
-      target.addEventListener('touchcancel', this.onTouchEnd) // treat it as a touch end
+      target.addEventListener('touchend', this.handleTouchEnd)
+      target.addEventListener('touchcancel', this.handleTouchEnd) // treat it as a touch end
 
       // attach a handler to get called when ANY scroll action happens on the page.
       // this was impossible to do with normal on/off because 'scroll' doesn't bubble.
       // http://stackoverflow.com/a/32954565/96342
       window.addEventListener(
         'scroll',
-        this.onTouchScroll, // always bound to `this`
+        this.handleTouchScroll,
         true // useCapture
       )
     }
+
   }
 
-  onTouchMove = (ev: TouchEvent) => {
-    this.emitter.trigger('pointermove', createTouchEvent(ev, this.subjectEl))
+  handleTouchMove = (ev: TouchEvent) => {
+    this.emitter.trigger('pointermove', createEventFromTouch(ev, this.subjectEl))
   }
 
-  onTouchEnd = (ev: TouchEvent) => {
+  handleTouchEnd = (ev: TouchEvent) => {
     if (this.isDragging) { // done to guard against touchend followed by touchcancel
       let target = ev.target
 
-      target.removeEventListener('touchmove', this.onTouchMove)
-      target.removeEventListener('touchend', this.onTouchEnd)
-      target.removeEventListener('touchcancel', this.onTouchEnd)
-      window.removeEventListener('scroll', this.onTouchScroll)
+      target.removeEventListener('touchmove', this.handleTouchMove)
+      target.removeEventListener('touchend', this.handleTouchEnd)
+      target.removeEventListener('touchcancel', this.handleTouchEnd)
+      window.removeEventListener('scroll', this.handleTouchScroll)
 
-      this.emitter.trigger('pointerup', createTouchEvent(ev, this.subjectEl))
+      this.emitter.trigger('pointerup', createEventFromTouch(ev, this.subjectEl))
 
-      this.cleanup()
-      this.isDraggingTouch = false
+      this.cleanup() // call last so that pointerup has access to props
+      this.isTouchDragging = false
       startIgnoringMouse()
     }
   }
 
-  shouldIgnoreMouse() {
-    return ignoreMouseDepth || this.isDraggingTouch
+  handleTouchScroll = () => {
+    this.isTouchScroll = true
   }
 
+  // can be called by user of this class, while dragging
   cancelTouchScroll() {
     if (this.isDragging) {
       isWindowTouchMoveCancelled = true
@@ -173,21 +187,25 @@ export default class PointerDragListener {
 
 }
 
-function createMouseEvent(ev, el: HTMLElement): PointerDragEvent {
+
+// Event Normalization
+// ----------------------------------------------------------------------------------------------------
+
+function createEventFromMouse(ev, subjectEl: HTMLElement): PointerDragEvent {
   return {
     origEvent: ev,
     isTouch: false,
-    el: el,
+    subjectEl,
     pageX: ev.pageX,
     pageY: ev.pageY
   }
 }
 
-function createTouchEvent(ev, el: HTMLElement): PointerDragEvent {
+function createEventFromTouch(ev, subjectEl: HTMLElement): PointerDragEvent {
   let pev = {
     origEvent: ev,
     isTouch: true,
-    el: el
+    subjectEl
   } as PointerDragEvent
 
   let touches = ev.touches
@@ -205,7 +223,14 @@ function createTouchEvent(ev, el: HTMLElement): PointerDragEvent {
   return pev
 }
 
+// Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac)
+function isPrimaryMouseButton(ev: MouseEvent) {
+  return ev.button === 0 && !ev.ctrlKey
+}
+
 
+// Ignoring fake mouse events generated by touch
+// ----------------------------------------------------------------------------------------------------
 
 let ignoreMouseDepth = 0
 
@@ -217,7 +242,9 @@ function startIgnoringMouse() { // can be made non-class function
   }, (exportHooks as any).touchMouseIgnoreWait)
 }
 
-// we want to attach touchmove as early as possible for safari
+
+// We want to attach touchmove as early as possible for Safari
+// ----------------------------------------------------------------------------------------------------
 
 let listenerCnt = 0
 let isWindowTouchMoveCancelled = false

+ 2 - 2
src/interactions/EventDragging.ts

@@ -54,7 +54,7 @@ export default class EventDragging {
 
   computeDragDelay(ev: PointerDragEvent): number {
     if (ev.isTouch) {
-      let seg = (ev.el as any).fcSeg
+      let seg = (ev.subjectEl as any).fcSeg
       let eventInstanceId = seg.eventRange.eventInstance.instanceId
 
       if (eventInstanceId !== this.component.selectedEventInstanceId) {
@@ -79,7 +79,7 @@ export default class EventDragging {
       }
     }
 
-    this.draggingSeg = (ev.el as any).fcSeg
+    this.draggingSeg = (ev.subjectEl as any).fcSeg
 
     if (ev.isTouch) {
       let eventInstanceId = this.draggingSeg.eventRange.eventInstance.instanceId

+ 5 - 4
src/interactions/EventResizing.ts

@@ -4,6 +4,7 @@ import { EventMutation, diffDates, getRelatedEvents, applyMutationToAll } from '
 import { elementClosest } from '../util/dom-manip'
 import UnzonedRange from '../models/UnzonedRange'
 import { IntentfulDragListenerImpl } from '../dnd/IntentfulDragListener'
+import { PointerDragEvent } from '../dnd/PointerDragListener'
 
 export default class EventDragging {
 
@@ -46,7 +47,7 @@ export default class EventDragging {
     this.draggingSeg = this.querySeg(ev)
   }
 
-  onHitOver = (hit, ev) => {
+  onHitOver = (hit, ev: PointerDragEvent) => {
     let calendar = this.component.getCalendar()
     let { initialHit } = this.hitListener
     let eventInstance = this.draggingSeg.eventRange.eventInstance
@@ -54,7 +55,7 @@ export default class EventDragging {
     let mutation = computeMutation(
       initialHit,
       hit,
-      ev.el.classList.contains('.fc-start-resizer'),
+      ev.subjectEl.classList.contains('.fc-start-resizer'),
       eventInstance.range
     )
 
@@ -110,8 +111,8 @@ export default class EventDragging {
     this.draggingSeg = null
   }
 
-  querySeg(ev): Seg {
-    return elementClosest(ev.el, this.component.segSelector).fcSeg
+  querySeg(ev: PointerDragEvent): Seg {
+    return elementClosest(ev.subjectEl, this.component.segSelector).fcSeg
   }
 
 }

+ 3 - 3
src/interactions/ExternalDragging.ts

@@ -46,7 +46,7 @@ export default class ExternalDragging {
       }
     }
 
-    this.eventCreationData = this.explicitEventCreationData || getDraggedElMeta(ev.el)
+    this.eventCreationData = this.explicitEventCreationData || getDraggedElMeta(ev.subjectEl)
   }
 
   onHitOver = (hit) => {
@@ -108,7 +108,7 @@ export default class ExternalDragging {
       // TODO: how to let Scheduler extend this?
       finalCalendar.publiclyTrigger('drop', [
         {
-          draggedEl: pev.el,
+          draggedEl: pev.subjectEl,
           date: finalCalendar.dateEnv.toDate(finalHit.range.start),
           isAllDay: finalHit.isAllDay,
           jsEvent: pev.origEvent,
@@ -126,7 +126,7 @@ export default class ExternalDragging {
         // signal an external event landed
         finalCalendar.publiclyTrigger('eventReceive', [
           {
-            draggedEl: pev.el,
+            draggedEl: pev.subjectEl,
             event: this.addableEventStore, // TODO: what to put here!?
             view: finalView
           }

+ 0 - 6
src/util/dom-event.ts

@@ -1,12 +1,6 @@
 import { elementClosest } from './dom-manip'
 
 
-// Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac)
-export function isPrimaryMouseButton(ev: MouseEvent) {
-  return ev.button === 0 && !ev.ctrlKey
-}
-
-
 // Stops a mouse/touch event from doing it's native browser action
 export function preventDefault(ev) {
   ev.preventDefault()