瀏覽代碼

stateful select-event and interacting-event

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

+ 4 - 2
src/Calendar.ts

@@ -331,7 +331,8 @@ export default class Calendar {
       selection: null,
       dragState: null,
       eventResizeState: null,
-      businessHoursDef: false
+      businessHoursDef: false,
+      selectedEventInstanceId: null
     }
   }
 
@@ -552,7 +553,8 @@ export default class Calendar {
       selection: state.selection,
       dragState: state.dragState,
       eventResizeState: state.eventResizeState,
-      businessHoursDef: renderedView.opt('businessHours')
+      businessHoursDef: renderedView.opt('businessHours'),
+      selectedEventInstanceId: state.selectedEventInstanceId
     }, forceFlags)
 
     if (this.updateViewSize()) { // success?

+ 1 - 47
src/View.ts

@@ -432,52 +432,6 @@ export default abstract class View extends InteractiveDateComponent {
   }
 
 
-  /* Event Selection
-  ------------------------------------------------------------------------------------------------------------------*/
-
-
-  selectEventInstance(eventInstance) {
-    if (
-      !this.selectedEventInstance ||
-      this.selectedEventInstance.instanceId !== eventInstance.instanceId
-    ) {
-      this.unselectEventInstance()
-
-      this.getEventSegs().forEach(function(seg) {
-        if (
-          seg.eventRange.eventInstance.instanceId === eventInstance.instanceId &&
-          seg.el // necessary?
-        ) {
-          seg.el.classList.add('fc-selected')
-        }
-      })
-
-      this.selectedEventInstance = eventInstance
-    }
-  }
-
-
-  unselectEventInstance() {
-    if (this.selectedEventInstance) {
-
-      this.getEventSegs().forEach(function(seg) {
-        if (seg.el) { // necessary?
-          seg.el.classList.remove('fc-selected')
-        }
-      })
-
-      this.selectedEventInstance = null
-    }
-  }
-
-
-  isEventDefSelected(eventDef) {
-    // event references might change on refetchEvents(), while selectedEventInstance doesn't,
-    // so compare IDs
-    return this.selectedEventInstance && this.selectedEventInstance.defId === eventDef.defId
-  }
-
-
   /* Mouse / Touch Unselecting (time range & event unselection)
   ------------------------------------------------------------------------------------------------------------------*/
   // TODO: move consistently to down/start or up/end?
@@ -514,7 +468,7 @@ export default abstract class View extends InteractiveDateComponent {
   processEventUnselect(ev) {
     if (this.selectedEventInstance) {
       if (!elementClosest(ev.target, '.fc-selected')) {
-        this.unselectEventInstance()
+        // TODO: use dispatch to change selectedEventInstanceId
       }
     }
   }

+ 4 - 2
src/agenda/AgendaView.ts

@@ -192,7 +192,8 @@ export default class AgendaView extends View {
       selection: timedSelection,
       dragState: dragStateGroups.timed,
       eventResizeState: eventResizeStateGroups.timed,
-      businessHoursDef: renderState.businessHoursDef
+      businessHoursDef: renderState.businessHoursDef,
+      selectedEventInstanceId: renderState.selectedEventInstanceId
     }, forceFlags)
 
     if (this.dayGrid) {
@@ -202,7 +203,8 @@ export default class AgendaView extends View {
         selection: allDaySeletion,
         dragState: dragStateGroups.allDay,
         eventResizeState: eventResizeStateGroups.allDay,
-        businessHoursDef: renderState.businessHoursDef
+        businessHoursDef: renderState.businessHoursDef,
+        selectedEventInstanceId: renderState.selectedEventInstanceId
       }, forceFlags)
     }
   }

+ 2 - 2
src/basic/DayGrid.ts

@@ -306,9 +306,9 @@ export default class DayGrid extends InteractiveDateComponent {
 
 
   // Retrieves all rendered segment objects currently rendered on the grid
-  getOwnEventSegs() {
+  getAllEventSegs() {
     // append the segments from the "more..." popover
-    return super.getOwnEventSegs().concat(this.popoverSegs || [])
+    return super.getAllEventSegs().concat(this.popoverSegs || [])
   }
 
 

+ 123 - 75
src/component/DateComponent.ts

@@ -24,6 +24,7 @@ export interface DateComponentRenderState {
   dragState: EventInteractionState | null
   eventResizeState: EventInteractionState | null
   businessHoursDef: BusinessHourDef
+  selectedEventInstanceId: string
 }
 
 export interface Seg {
@@ -70,6 +71,8 @@ export default abstract class DateComponent extends Component {
   eventStore: EventStore
   dragState: EventInteractionState
   eventResizeState: EventInteractionState
+  interactingEventDefId: string
+  selectedEventInstanceId: string
 
 
   constructor(_view, _options?) {
@@ -224,11 +227,11 @@ export default abstract class DateComponent extends Component {
 
     // unrendering
     if (isEventResizeDirty && this.isEventResizeRendered) {
-      this.unrenderEventResize()
+      this.unrenderEventResizeState()
       this.isEventResizeRendered = false
     }
     if (isDragDirty && this.isDragRendered) {
-      this.unrenderDrag()
+      this.unrenderDragState()
       this.isDragRendered = false
     }
     if (isEventsDirty && this.isEventsRendered) {
@@ -277,15 +280,17 @@ export default abstract class DateComponent extends Component {
     }
     if ((isDragDirty || !this.isDragRendered) && renderState.dragState && this.isDatesRendered) {
       let { dragState } = renderState
-      this.renderDrag(dragState.eventStore, dragState.origSeg, dragState.isTouch)
+      this.renderDragState(dragState)
       this.isDragRendered = true
     }
     if ((isEventResizeDirty || !this.isEventResizeRendered) && renderState.eventResizeState && this.isDatesRendered) {
       let { eventResizeState } = renderState
-      this.renderEventResize(eventResizeState.eventStore, eventResizeState.origSeg, eventResizeState.isTouch)
+      this.renderEventResizeState(eventResizeState)
       this.isEventResizeRendered = true
     }
 
+    this.updateSelectedEventInstance(renderState.selectedEventInstanceId)
+
     this.renderChildren(renderState, forceFlags)
   }
 
@@ -296,12 +301,14 @@ export default abstract class DateComponent extends Component {
 
 
   removeElement() {
+    this.updateSelectedEventInstance()
+
     if (this.isEventResizeRendered) {
-      this.unrenderEventResize()
+      this.unrenderEventResizeState()
       this.isEventResizeRendered = false
     }
     if (this.isDragRendered) {
-      this.unrenderDrag()
+      this.unrenderDragState()
       this.isDragRendered = false
     }
     if (this.isEventsRendered) {
@@ -410,26 +417,6 @@ export default abstract class DateComponent extends Component {
   }
 
 
-  getBusinessHourSegs() { // recursive
-    let segs = this.getOwnBusinessHourSegs()
-
-    this.iterChildren(function(child) {
-      segs.push.apply(segs, child.getBusinessHourSegs())
-    })
-
-    return segs
-  }
-
-
-  getOwnBusinessHourSegs() {
-    if (this.businessHourRenderer) {
-      return this.businessHourRenderer.getSegs()
-    }
-
-    return []
-  }
-
-
   // Event Displaying
   // -----------------------------------------------------------------------------------------------------------------
 
@@ -453,35 +440,87 @@ export default abstract class DateComponent extends Component {
   }
 
 
-  getEventSegs() { // recursive
-    let segs = this.getOwnEventSegs()
+  // Drag-n-Drop Rendering (for both events and external elements)
+  // ---------------------------------------------------------------------------------------------------------------
 
-    this.iterChildren(function(child) {
-      segs.push.apply(segs, child.getEventSegs())
-    })
 
-    return segs
+  renderDragState(dragState: EventInteractionState) {
+    this.updateEventInteractionState(dragState)
+    this.renderDrag(dragState.eventStore, dragState.origSeg, dragState.isTouch)
   }
 
 
-  getOwnEventSegs() { // just for itself
-    if (this.eventRenderer) {
-      return this.eventRenderer.getSegs()
-    }
+  unrenderDragState() {
+    this.updateEventInteractionState()
+    this.unrenderDrag()
+  }
 
-    return []
+
+  // 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) {
+    // subclasses can implement
+    // TODO: how to determine if just one child rendered the drag so we don't have to render the helper?
+  }
+
+
+  // Unrenders a visual indication of an event or external-element being dragged.
+  unrenderDrag() {
+    // subclasses can implement
+  }
+
+
+  // Event Resizing
+  // ---------------------------------------------------------------------------------------------------------------
+
+
+  renderEventResizeState(dragState: EventInteractionState) {
+    this.updateEventInteractionState(dragState)
+    this.renderEventResize(dragState.eventStore, dragState.origSeg, dragState.isTouch)
+  }
+
+
+  unrenderEventResizeState() {
+    this.updateEventInteractionState()
+    this.unrenderEventResize()
+  }
+
+
+  // Renders a visual indication of an event being resized.
+  renderEventResize(eventStore: EventStore, origSeg: any, isTouch: boolean) {
+    // subclasses can implement
+  }
+
+
+  // Unrenders a visual indication of an event being resized.
+  unrenderEventResize() {
+    // subclasses can implement
   }
 
 
-  // Event Rendering Utils
+  // Event Interaction Utils
   // -----------------------------------------------------------------------------------------------------------------
 
 
-  // Hides all rendered event segments linked to the given event
-  // RECURSIVE with subcomponents
-  showEventsWithId(eventDefId) {
+  updateEventInteractionState(dragState?: EventInteractionState) {
+    let eventDefId = (dragState && dragState.origSeg) ? dragState.origSeg.eventRange.eventDef.defId : null
+
+    if (this.interactingEventDefId && this.interactingEventDefId !== eventDefId) {
+      this.showEventByDefId(this.interactingEventDefId)
+      this.interactingEventDefId = null
+    }
+
+    if (eventDefId && !this.interactingEventDefId) {
+      this.hideEventsByDefId(eventDefId)
+      this.interactingEventDefId = eventDefId
+    }
+  }
+
 
-    this.getEventSegs().forEach(function(seg) {
+  // Hides all rendered event segments linked to the given event
+  showEventByDefId(eventDefId) {
+    this.getAllEventSegs().forEach(function(seg) {
       if (
         seg.eventRange.eventDef.id === eventDefId &&
         seg.el // necessary?
@@ -489,16 +528,12 @@ export default abstract class DateComponent extends Component {
         seg.el.style.visibility = ''
       }
     })
-
-    this.callChildren('showEventsWithId', arguments)
   }
 
 
   // Shows all rendered event segments linked to the given event
-  // RECURSIVE with subcomponents
-  hideEventsWithId(eventDefId) {
-
-    this.getEventSegs().forEach(function(seg) {
+  hideEventsByDefId(eventDefId) {
+    this.getAllEventSegs().forEach(function(seg) {
       if (
         seg.eventRange.eventDef.id === eventDefId &&
         seg.el // necessary?
@@ -506,29 +541,58 @@ export default abstract class DateComponent extends Component {
         seg.el.style.visibility = 'hidden'
       }
     })
+  }
 
-    this.callChildren('hideEventsWithId', arguments)
+
+  getAllEventSegs() {
+    if (this.eventRenderer) {
+      return this.eventRenderer.getSegs()
+    } else {
+      return []
+    }
   }
 
 
-  // Drag-n-Drop Rendering (for both events and external elements)
-  // ---------------------------------------------------------------------------------------------------------------
+  // Event Instance Selection (aka long-touch focus)
+  // -----------------------------------------------------------------------------------------------------------------
+  // TODO: show/hide according to groupId?
 
 
-  // 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) {
-    // subclasses can implement
+  updateSelectedEventInstance(instanceId?) {
+    if (this.selectedEventInstanceId && this.selectedEventInstanceId !== instanceId) {
+      this.unselectAllEvents()
+      this.selectEventsByInstanceId = null
+    }
+
+    if (instanceId && !this.selectedEventInstanceId) {
+      this.selectEventsByInstanceId(instanceId)
+      this.selectedEventInstanceId = instanceId
+    }
   }
 
 
-  // Unrenders a visual indication of an event or external-element being dragged.
-  unrenderDrag() {
-    // subclasses can implement
+  selectEventsByInstanceId(instanceId) {
+    this.getAllEventSegs().forEach(function(seg) {
+      if (
+        seg.eventRange.eventInstance.instanceId === instanceId &&
+        seg.el // necessary?
+      ) {
+        seg.el.classList.add('fc-selected')
+      }
+    })
   }
 
 
+  unselectAllEvents() {
+    this.getAllEventSegs().forEach(function(seg) {
+      if (seg.el) { // necessary?
+        seg.el.classList.remove('fc-selected')
+      }
+    })
+  }
+
+
+
   // EXTERNAL Drag-n-Drop
   // ---------------------------------------------------------------------------------------------------------------
   // Doesn't need to implement a response, but must pass to children
@@ -549,22 +613,6 @@ export default abstract class DateComponent extends Component {
   }
 
 
-  // Event Resizing
-  // ---------------------------------------------------------------------------------------------------------------
-
-
-  // Renders a visual indication of an event being resized.
-  renderEventResize(eventStore: EventStore, origSeg: any, isTouch: boolean) {
-    // subclasses can implement
-  }
-
-
-  // Unrenders a visual indication of an event being resized.
-  unrenderEventResize() {
-    // subclasses can implement
-  }
-
-
   // Selection
   // ---------------------------------------------------------------------------------------------------------------
 

+ 2 - 3
src/component/InteractiveDateComponent.ts

@@ -136,10 +136,9 @@ export default abstract class InteractiveDateComponent extends DateComponent {
 
 
   canStartResize(seg, ev) {
-    let view = this.view
-    let eventDef = seg.eventRange.eventDef
+    let { eventDef, eventInstance } = seg.eventRange
 
-    return (!getEvIsTouch(ev) || view.isEventDefSelected(eventDef)) &&
+    return (!getEvIsTouch(ev) || this.selectedEventInstanceId === eventInstance.instanceId) &&
       this.isEventDefResizable(eventDef) &&
       ev.target.classList.contains('fc-resizer')
   }

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

@@ -208,7 +208,7 @@ export default class EventRenderer {
     }
 
     // event is currently selected? attach a className.
-    if (this.view.isEventDefSelected(seg.eventRange.eventDef)) {
+    if (seg.eventRange.eventInstance.instanceId === this.component.selectedEventInstanceId) {
       classes.push('fc-selected')
     }
 

+ 2 - 1
src/reducers/main.ts

@@ -20,7 +20,8 @@ export function reduce(state: CalendarState, action: any, calendar: Calendar): C
     selection: state.selection,
     dragState: state.dragState,
     eventResizeState: state.eventResizeState,
-    businessHoursDef: state.businessHoursDef
+    businessHoursDef: state.businessHoursDef,
+    selectedEventInstanceId: state.selectedEventInstanceId
   }
 
   calendar.trigger(action.type, action) // for testing hooks