Ver código fonte

more refactor of view-framework

Adam Shaw 6 anos atrás
pai
commit
94d61b0a12
45 arquivos alterados com 417 adições e 378 exclusões
  1. 1 1
      packages-premium
  2. 36 21
      packages/__tests__/src/globals.js
  3. 18 35
      packages/__tests__/src/legacy/custom-view-class.js
  4. 1 0
      packages/__tests__/src/legacy/custom-view-duration.js
  5. 2 2
      packages/__tests__/src/legacy/nowIndicator.js
  6. 1 1
      packages/__tests__/src/legacy/views-specific-options.js
  7. 9 6
      packages/__tests__/src/performance/rerenders.js
  8. 5 5
      packages/__tests__/src/view-type/exposed-classes.js
  9. 123 50
      packages/core/src/Calendar.ts
  10. 30 93
      packages/core/src/CalendarComponent.ts
  11. 9 9
      packages/core/src/Toolbar.ts
  12. 3 27
      packages/core/src/View.ts
  13. 23 0
      packages/core/src/ViewApi.ts
  14. 2 2
      packages/core/src/common/DayHeader.ts
  15. 2 2
      packages/core/src/common/Scroller.ts
  16. 2 2
      packages/core/src/component/ComponentContext.ts
  17. 4 3
      packages/core/src/component/DateComponent.ts
  18. 2 2
      packages/core/src/component/event-rendering.ts
  19. 4 4
      packages/core/src/component/renderers/FgEventRenderer.ts
  20. 1 1
      packages/core/src/component/renderers/FillRenderer.ts
  21. 2 0
      packages/core/src/event-sources/func-event-source.ts
  22. 3 1
      packages/core/src/main.ts
  23. 4 3
      packages/core/src/structs/view-config.ts
  24. 28 28
      packages/core/src/types/input-types.ts
  25. 1 1
      packages/core/src/validation.ts
  26. 7 7
      packages/daygrid/src/DayTable.ts
  27. 5 3
      packages/daygrid/src/DayTableView.ts
  28. 8 7
      packages/daygrid/src/DayTile.ts
  29. 7 3
      packages/daygrid/src/DayTileEvents.ts
  30. 3 3
      packages/daygrid/src/Popover.ts
  31. 16 13
      packages/daygrid/src/Table.ts
  32. 1 1
      packages/daygrid/src/TableEvents.ts
  33. 2 2
      packages/daygrid/src/TableFills.ts
  34. 3 2
      packages/daygrid/src/TableView.ts
  35. 2 3
      packages/interaction/src/interactions/EventDragging.ts
  36. 4 3
      packages/list/src/ListView.ts
  37. 1 1
      packages/list/src/ListViewEvents.ts
  38. 5 4
      packages/timegrid/src/DayTimeCols.ts
  39. 9 5
      packages/timegrid/src/DayTimeColsView.ts
  40. 18 14
      packages/timegrid/src/TimeCols.ts
  41. 1 1
      packages/timegrid/src/TimeColsEvents.ts
  42. 2 2
      packages/timegrid/src/TimeColsFills.ts
  43. 4 3
      packages/timegrid/src/TimeColsView.ts
  44. 2 2
      packages/timegrid/src/main.ts
  45. 1 0
      tsconfig.json

+ 1 - 1
packages-premium

@@ -1 +1 @@
-Subproject commit dd7e5c7caadd508edd70b1815ea59f003ccc3b1c
+Subproject commit f0cc8a76d785925d39ea9511038ef6fe30a009ae

+ 36 - 21
packages/__tests__/src/globals.js

@@ -99,7 +99,10 @@ function initCalendar(moreOptions, el) {
 }
 
 function getCurrentOptions() {
-  return $.extend.apply($, [ {} ].concat(optionsStack))
+  /** @type {any} */
+  let args = [ {} ].concat(optionsStack)
+
+  return $.extend.apply($, args)
 }
 
 
@@ -117,30 +120,42 @@ function describeOptions(optName, hash, callback) {
     optName = null
   }
 
-  $.each(hash, function(desc, val) {
-    var opts
-
-    if (optName) {
-      opts = {}
-      opts[optName] = val
-    } else {
-      opts = val
+  $.each(
+    hash,
+    /**
+     * @param desc {string}
+     */
+    function(desc, val) {
+      var opts
+
+      if (optName) {
+        opts = {}
+        opts[optName] = val
+      } else {
+        opts = val
+      }
+      opts = $.extend(true, {}, opts)
+
+      describe(desc, function() {
+        pushOptions(opts)
+        callback(val)
+      })
     }
-    opts = $.extend(true, {}, opts)
-
-    describe(desc, function() {
-      pushOptions(opts)
-      callback(val)
-    })
-  })
+  )
 }
 
 function describeValues(hash, callback) {
-  $.each(hash, function(desc, val) {
-    describe(desc, function() {
-      callback(val)
-    })
-  })
+  $.each(
+    hash,
+    /**
+     * @param desc {string}
+     */
+    function(desc, val) {
+      describe(desc, function() {
+        callback(val)
+      })
+    }
+  )
 }
 
 

+ 18 - 35
packages/__tests__/src/legacy/custom-view-class.js

@@ -6,20 +6,11 @@ describe('custom view class', function() {
 
     class CustomView extends View {
 
-      renderDates(dateProfile) {
-        expect(dateProfile.activeRange.start instanceof Date).toBe(true)
-        expect(dateProfile.activeRange.end instanceof Date).toBe(true)
-      }
-
-      updateSize(isResize, height, isAuto) {
-        expect(typeof isResize).toBe('boolean')
-        expect(typeof height).toBe('number')
-        expect(typeof isAuto).toBe('boolean')
-      }
-
-      renderEvents(eventStore) {
-        let eventRanges = this.sliceEvents(eventStore, true) // allDay=true
+      render(props) {
+        expect(props.dateProfile.activeRange.start instanceof Date).toBe(true)
+        expect(props.dateProfile.activeRange.end instanceof Date).toBe(true)
 
+        let eventRanges = this.sliceEvents(props.eventStore, true) // allDay=true
         expect(Array.isArray(eventRanges)).toBe(true)
         expect(eventRanges.length).toBe(1)
         expect(typeof eventRanges[0].def).toBe('object')
@@ -29,30 +20,24 @@ describe('custom view class', function() {
         expect(eventRanges[0].isEnd).toBe(true)
         expect(eventRanges[0].range.start instanceof Date).toBe(true)
         expect(eventRanges[0].range.end instanceof Date).toBe(true)
-      }
-
-      unrenderEvents() {
-      }
 
-      renderDateSelection(dateSpan) {
-        expect(typeof dateSpan).toBe('object')
-        expect(dateSpan.allDay).toBe(true)
-        expect(dateSpan.range.start instanceof Date).toBe(true)
-        expect(dateSpan.range.end instanceof Date).toBe(true)
+        let dateSelection = props.dateSpan
+        expect(typeof dateSelection).toBe('object')
+        expect(dateSelection.allDay).toBe(true)
+        expect(dateSelection.range.start instanceof Date).toBe(true)
+        expect(dateSelection.range.end instanceof Date).toBe(true)
       }
 
-      unrenderDateSelection() {
+      updateSize(isResize, height, isAuto) {
+        expect(typeof isResize).toBe('boolean')
+        expect(typeof height).toBe('number')
+        expect(typeof isAuto).toBe('boolean')
       }
 
     }
 
-    spyOn(CustomView.prototype, 'initialize').and.callThrough()
-    spyOn(CustomView.prototype, 'renderDates').and.callThrough()
+    spyOn(CustomView.prototype, 'render').and.callThrough()
     spyOn(CustomView.prototype, 'updateSize').and.callThrough()
-    spyOn(CustomView.prototype, 'renderEvents').and.callThrough()
-    spyOn(CustomView.prototype, 'unrenderEvents').and.callThrough()
-    spyOn(CustomView.prototype, 'renderDateSelection').and.callThrough()
-    spyOn(CustomView.prototype, 'unrenderDateSelection').and.callThrough()
 
     initCalendar({
       plugins: [
@@ -73,22 +58,20 @@ describe('custom view class', function() {
       ]
     })
 
-    expect(CustomView.prototype.initialize).toHaveBeenCalled()
-    expect(CustomView.prototype.renderDates).toHaveBeenCalled()
+    expect(CustomView.prototype.render).toHaveBeenCalled()
     expect(CustomView.prototype.updateSize).toHaveBeenCalled()
-    expect(CustomView.prototype.renderEvents).toHaveBeenCalled()
 
     currentCalendar.rerenderEvents()
 
-    expect(CustomView.prototype.unrenderEvents).toHaveBeenCalled()
+    expect(CustomView.prototype.render).toHaveBeenCalled()
 
     currentCalendar.select('2014-12-25', '2014-01-01')
 
-    expect(CustomView.prototype.renderDateSelection).toHaveBeenCalled()
+    expect(CustomView.prototype.render).toHaveBeenCalled()
 
     currentCalendar.unselect()
 
-    expect(CustomView.prototype.unrenderDateSelection).toHaveBeenCalled()
+    expect(CustomView.prototype.render).toHaveBeenCalled()
   })
 
 })

+ 1 - 0
packages/__tests__/src/legacy/custom-view-duration.js

@@ -356,6 +356,7 @@ describe('custom view', function() {
       // also sorta tests plugin system
 
       class CrazyView extends View {
+        render() {}
       }
 
       initCalendar({

+ 2 - 2
packages/__tests__/src/legacy/nowIndicator.js

@@ -1,7 +1,7 @@
 import { getBoundingRect } from '../lib/dom-geom'
 import { isElWithinRtl } from '../lib/dom-misc'
 import { getTimeGridLine } from '../lib/time-grid'
-import { TimeGrid } from '@fullcalendar/timegrid'
+import { TimeCols } from '@fullcalendar/timegrid'
 
 describe('now indicator', function() {
   var options
@@ -70,7 +70,7 @@ describe('now indicator', function() {
     it('doesnt double render indicator arrow', function(done) {
 
       // force the indicator to update every second
-      var getNowIndicatorUnit = spyOnMethod(TimeGrid, 'getNowIndicatorUnit', true)
+      var getNowIndicatorUnit = spyOnMethod(TimeCols, 'getNowIndicatorUnit', true)
         .and.returnValue('second')
 
       options.defaultDate = '2016-01-01' // does NOT have "now" in view

+ 1 - 1
packages/__tests__/src/legacy/views-specific-options.js

@@ -153,7 +153,7 @@ describe('view-specific options', function() {
 
   it('can implicitly target an old-school View subclass', function() {
 
-    function SuperDayGridView() { DayGridView.apply(this, arguments) }
+    function SuperDayGridView() { DayGridView.apply(this, /** @type {any} */ (arguments)) }
     SuperDayGridView.prototype = Object.create(DayGridView.prototype)
 
     initCalendar({

+ 9 - 6
packages/__tests__/src/performance/rerenders.js

@@ -1,7 +1,10 @@
-import { DayGrid } from '@fullcalendar/daygrid'
-import { TimeGrid } from '@fullcalendar/timegrid'
+import { DayTable } from '@fullcalendar/daygrid'
+import { DayTimeCols } from '@fullcalendar/timegrid'
 import { ListView } from '@fullcalendar/list'
 
+/*
+these tests will only work as long as each component has an updateSize method
+*/
 describe('rerender performance', function() {
 
   pushOptions({
@@ -15,13 +18,13 @@ describe('rerender performance', function() {
   ;[
     {
       defaultView: 'dayGridMonth',
-      classes: { DayGrid },
-      changeToView: 'list' // does not have DayGrid!
+      classes: { DayTable },
+      changeToView: 'list' // does not have DayTable!
     },
     {
       defaultView: 'timeGridWeek',
-      classes: { DayGrid, TimeGrid },
-      changeToView: 'list' // does not have DayGrid!
+      classes: { DayTable, DayTimeCols },
+      changeToView: 'list' // does not have DayTable!
     },
     {
       defaultView: 'listWeek',

+ 5 - 5
packages/__tests__/src/view-type/exposed-classes.js

@@ -1,17 +1,17 @@
-import { DayGridView, DayGrid } from '@fullcalendar/daygrid'
+import { DayGridView, DayTable } from '@fullcalendar/daygrid'
 import { ListView } from '@fullcalendar/list'
-import { TimeGridView, TimeGrid } from '@fullcalendar/timegrid'
+import { DayTimeColsView, DayTimeCols } from '@fullcalendar/timegrid'
 
 describe('internal View/Grid classes', function() {
 
   it('are exposed', function() {
 
-    expect(typeof TimeGridView).toBe('function')
+    expect(typeof DayTimeColsView).toBe('function')
     expect(typeof DayGridView).toBe('function')
     expect(typeof ListView).toBe('function')
 
-    expect(typeof DayGrid).toBe('function')
-    expect(typeof TimeGrid).toBe('function')
+    expect(typeof DayTable).toBe('function')
+    expect(typeof DayTimeCols).toBe('function')
   })
 
 })

+ 123 - 50
packages/core/src/Calendar.ts

@@ -7,15 +7,15 @@ import Theme from './theme/Theme'
 import { OptionsInput, EventHandlerName, EventHandlerArgs } from './types/input-types'
 import { Locale, buildLocale, parseRawLocales, RawLocaleMap } from './datelib/locale'
 import { DateEnv, DateInput } from './datelib/env'
-import { DateMarker, startOfDay } from './datelib/marker'
+import { DateMarker, startOfDay, diffWholeDays } from './datelib/marker'
 import { createFormatter } from './datelib/formatting'
 import { Duration, createDuration, DurationInput } from './datelib/duration'
 import reduce from './reducers/main'
 import { parseDateSpan, DateSpanInput, DateSpan, buildDateSpanApi, DateSpanApi, buildDatePointApi, DatePointApi } from './structs/date-span'
 import { memoize, memoizeOutput } from './util/memoize'
 import { mapHash, isPropsEqual } from './util/object'
-import { DateRangeInput } from './datelib/date-range'
-import DateProfileGenerator from './DateProfileGenerator'
+import { DateRangeInput, DateRange } from './datelib/date-range'
+import DateProfileGenerator, { DateProfile } from './DateProfileGenerator'
 import { EventSourceInput, parseEventSource, EventSourceHash } from './structs/event-source'
 import { EventInput, parseEvent, EventDefHash } from './structs/event'
 import { CalendarState, Action } from './reducers/types'
@@ -37,7 +37,8 @@ import StandardTheme from './theme/StandardTheme'
 import { CmdFormatterFunc } from './datelib/formatting-cmd'
 import { NamedTimeZoneImplClass } from './datelib/timezone'
 import { computeContextProps } from './component/ComponentContext'
-import { RenderEngine, TaskQueue } from './view-framework'
+import { TaskRunner, renderer, DelayedRunner } from './view-framework'
+import ViewApi from './ViewApi'
 
 export interface DateClickApi extends DatePointApi {
   dayEl: HTMLElement
@@ -82,6 +83,8 @@ export default class Calendar {
   private buildSelectionConfig = memoize(this._buildSelectionConfig)
   private buildEventUiBySource = memoizeOutput(buildEventUiBySource, isPropsEqual)
   private buildEventUiBases = memoize(buildEventUiBases)
+  private buildViewApi = memoize(buildViewApi)
+  private computeTitle = memoize(computeTitle)
 
   eventUiBases: EventUiHash // solely for validation system
   selectionConfig: EventUi // doesn't need all the info EventUi provides. only validation-related. TODO: separate data structs
@@ -104,8 +107,8 @@ export default class Calendar {
   isHandlingWindowResize: boolean
 
   state: CalendarState
-  actionQueue: TaskQueue<Action>
-  renderEngine: RenderEngine
+  renderRunner: DelayedRunner
+  actionRunner: TaskRunner<Action>
   renderableEventStore: EventStore
 
   afterSizingTriggers: any = {}
@@ -114,24 +117,34 @@ export default class Calendar {
   isEventsUpdated: boolean = false
 
   el: HTMLElement
+  renderCalendarComponent = renderer(CalendarComponent)
   component: CalendarComponent
 
+  view: ViewApi // public API
+
 
   constructor(el: HTMLElement, overrides?: OptionsInput) {
     this.el = el
 
-    this.optionsManager = new OptionsManager(overrides || {})
+    let optionsManager = this.optionsManager = new OptionsManager(overrides || {})
     this.pluginSystem = new PluginSystem()
 
-    this.actionQueue = new TaskQueue({ // no delay. simply so that nested dispatches dont happen
-      runTask: this.runAction.bind(this),
-      drained: this.updateComponent.bind(this)
-    })
+    let renderRunner = this.renderRunner = new DelayedRunner(
+      this.updateComponent.bind(this)
+    )
+
+    this.actionRunner = new TaskRunner(
+      this.runAction.bind(this),
+      (actions) => {
+        let doDelay = computeDoDelay(actions)
+        renderRunner.request(doDelay ? optionsManager.computed.rerenderDelay : null)
+      }
+    )
 
     // only do once. don't do in handleOptions. because can't remove plugins
-    this.addPluginInputs(this.optionsManager.computed.plugins || [])
+    this.addPluginInputs(optionsManager.computed.plugins || [])
 
-    this.handleOptions(this.optionsManager.computed)
+    this.handleOptions(optionsManager.computed)
     this.publiclyTrigger('_init') // for tests
     this.hydrate()
 
@@ -151,20 +164,12 @@ export default class Calendar {
   }
 
 
-  // public API
-  get view(): View {
-    return this.component ? this.component.view : null
-  }
-
-
   // Public API for rendering
   // -----------------------------------------------------------------------------------------------------------------
 
 
   render() {
     if (!this.component) {
-      this.renderEngine = new RenderEngine(this.optionsManager.computed.rerenderDelay)
-      this.component = new CalendarComponent(this.renderEngine)
       this.renderableEventStore = createEmptyEventStore()
       this.bindHandlers() // TODO: have CalendarComponent handle this?
     }
@@ -176,8 +181,8 @@ export default class Calendar {
   destroy() {
     if (this.component) {
       this.unbindHandlers()
-      this.component.unmount() // don't null-out. in case API needs access
-      this.component = null // umm ???
+      this.renderCalendarComponent(false)
+      this.component = null
 
       for (let interaction of this.calendarInteractions) {
         interaction.destroy()
@@ -287,8 +292,7 @@ export default class Calendar {
 
 
   dispatch(action: Action) {
-    this.actionQueue.push(action)
-    this.actionQueue.requestRun()
+    this.actionRunner.request(action)
   }
 
 
@@ -302,7 +306,8 @@ export default class Calendar {
       this.publiclyTrigger('loading', [ false ])
     }
 
-    let view = this.component && this.component.view
+    let viewComponent = this.component && this.component.view
+    let viewApi = this.view
 
     if (oldState.eventStore !== newState.eventStore) {
       if (oldState.eventStore) {
@@ -311,11 +316,11 @@ export default class Calendar {
     }
 
     if (oldState.dateProfile !== newState.dateProfile) {
-      if (oldState.dateProfile && view) { // why would view be null!?
+      if (oldState.dateProfile && viewComponent) { // why would view be null!?
         this.publiclyTrigger('datesDestroy', [
           {
-            view,
-            el: view.rootEl
+            view: viewApi,
+            el: viewComponent.rootEl
           }
         ])
       }
@@ -323,11 +328,11 @@ export default class Calendar {
     }
 
     if (oldState.viewType !== newState.viewType) {
-      if (oldState.viewType && view) { // why would view be null!?
+      if (oldState.viewType && viewComponent) { // why would view be null!?
         this.publiclyTrigger('viewSkeletonDestroy', [
           {
-            view,
-            el: view.rootEl
+            view: viewApi,
+            el: viewComponent.rootEl
           }
         ])
       }
@@ -341,15 +346,11 @@ export default class Calendar {
 
 
   batchRendering(func) {
-    let { renderEngine } = this
+    let { renderRunner } = this
 
-    if (renderEngine) {
-      renderEngine.updateQueue.pause()
-      func()
-      renderEngine.updateQueue.resume()
-    } else {
-      func()
-    }
+    renderRunner.pause()
+    func()
+    renderRunner.resume()
   }
 
 
@@ -357,15 +358,11 @@ export default class Calendar {
   don't call this directly. use executeRender instead
   */
   updateComponent() {
-    let { state, component } = this
+    let { state } = this
     let { viewType } = state
     let viewSpec = this.viewSpecs[viewType]
     let rawOptions = this.optionsManager.computed
 
-    if (!component) {
-      return
-    }
-
     if (!viewSpec) {
       throw new Error(`View type "${viewType}" is not valid`)
     }
@@ -381,7 +378,11 @@ export default class Calendar {
     let eventUiBySource = this.buildEventUiBySource(state.eventSources)
     let eventUiBases = this.eventUiBases = this.buildEventUiBases(renderableEventStore.defs, eventUiSingleBase, eventUiBySource)
 
-    component.update(this.el, {
+    let title = this.computeTitle(state.dateProfile, this.dateEnv, viewSpec.options)
+    let viewApi = this.view = this.buildViewApi(viewSpec.type, title, state.dateProfile, this.dateEnv)
+
+    let component = this.renderCalendarComponent({
+      parentEl: this.el,
       ...state,
       viewSpec,
       dateProfileGenerator: this.dateProfileGenerators[viewType],
@@ -391,9 +392,11 @@ export default class Calendar {
       dateSelection: state.dateSelection,
       eventSelection: state.eventSelection,
       eventDrag: state.eventDrag,
-      eventResize: state.eventResize
+      eventResize: state.eventResize,
+      title
     }, {
       calendar: this,
+      view: viewApi,
       pluginHooks: this.pluginSystem.hooks,
       theme: this.theme,
       dateEnv: this.dateEnv,
@@ -405,7 +408,7 @@ export default class Calendar {
       this.isViewUpdated = false
       this.publiclyTrigger('viewSkeletonRender', [
         {
-          view: component.view,
+          view: viewApi,
           el: component.view.rootEl
         }
       ])
@@ -415,7 +418,7 @@ export default class Calendar {
       this.isDatesUpdated = false
       this.publiclyTrigger('datesRender', [
         {
-          view: component.view,
+          view: viewApi,
           el: component.view.rootEl
         }
       ])
@@ -956,7 +959,7 @@ export default class Calendar {
 
 
   // TODO: receive pev?
-  triggerDateClick(dateSpan: DateSpan, dayEl: HTMLElement, view: View, ev: UIEvent) {
+  triggerDateClick(dateSpan: DateSpan, dayEl: HTMLElement, view: ViewApi, ev: UIEvent) {
     const arg = {
       ...this.buildDatePointApi(dateSpan),
       dayEl,
@@ -1272,3 +1275,73 @@ function buildEventUiBases(eventDefs: EventDefHash, eventUiSingleBase: EventUi,
 
   return eventUiBases
 }
+
+
+function buildViewApi(type: string, title: string, dateProfile: DateProfile, dateEnv: DateEnv) {
+  return new ViewApi(type, title, dateProfile, dateEnv)
+}
+
+
+// Title and Date Formatting
+// -----------------------------------------------------------------------------------------------------------------
+
+
+// Computes what the title at the top of the calendar should be for this view
+function computeTitle(dateProfile, dateEnv: DateEnv, viewOptions) {
+  let range: DateRange
+
+  // for views that span a large unit of time, show the proper interval, ignoring stray days before and after
+  if (/^(year|month)$/.test(dateProfile.currentRangeUnit)) {
+    range = dateProfile.currentRange
+  } else { // for day units or smaller, use the actual day range
+    range = dateProfile.activeRange
+  }
+
+  return dateEnv.formatRange(
+    range.start,
+    range.end,
+    createFormatter(
+      viewOptions.titleFormat || computeTitleFormat(dateProfile),
+      viewOptions.titleRangeSeparator
+    ),
+    { isEndExclusive: dateProfile.isRangeAllDay }
+  )
+}
+
+
+// Generates the format string that should be used to generate the title for the current date range.
+// Attempts to compute the most appropriate format if not explicitly specified with `titleFormat`.
+function computeTitleFormat(dateProfile) {
+  let currentRangeUnit = dateProfile.currentRangeUnit
+
+  if (currentRangeUnit === 'year') {
+    return { year: 'numeric' }
+  } else if (currentRangeUnit === 'month') {
+    return { year: 'numeric', month: 'long' } // like "September 2014"
+  } else {
+    let days = diffWholeDays(
+      dateProfile.currentRange.start,
+      dateProfile.currentRange.end
+    )
+    if (days !== null && days > 1) {
+      // multi-day range. shorter, like "Sep 9 - 10 2014"
+      return { year: 'numeric', month: 'short', day: 'numeric' }
+    } else {
+      // one day. longer, like "September 9 2014"
+      return { year: 'numeric', month: 'long', day: 'numeric' }
+    }
+  }
+}
+
+
+function computeDoDelay(actions: Action[]) {
+  for (let action of actions) {
+    switch (action.type) {
+      case 'INIT':
+      case 'SET_EVENT_DRAG':
+      case 'SET_EVENT_RESIZE':
+        return false
+    }
+  }
+  return true
+}

+ 30 - 93
packages/core/src/CalendarComponent.ts

@@ -1,30 +1,30 @@
 import ComponentContext, { computeContextProps } from './component/ComponentContext'
-import { Component, renderer } from './view-framework'
+import { Component, renderer, DomLocation } from './view-framework'
 import { ViewSpec } from './structs/view-spec'
 import View, { ViewProps } from './View'
 import Toolbar from './Toolbar'
 import DateProfileGenerator, { DateProfile } from './DateProfileGenerator'
 import { createElement, applyStyle } from './util/dom-manip'
-import { rangeContainsMarker, DateRange } from './datelib/date-range'
+import { rangeContainsMarker } from './datelib/date-range'
 import { EventUiHash } from './component/event-ui'
 import { parseBusinessHours } from './structs/business-hours'
 import { memoize } from './util/memoize'
 import { computeHeightAndMargins } from './util/dom-geom'
-import { createFormatter } from './datelib/formatting'
-import { diffWholeDays, DateMarker } from './datelib/marker'
+import { DateMarker } from './datelib/marker'
 import { CalendarState } from './reducers/types'
 import { ViewPropsTransformerClass } from './plugin-system'
 import { __assign } from 'tslib'
-import { ViewClass } from './structs/view-config'
+import { listRenderer } from './view-framework'
 
 
-export interface CalendarComponentProps extends CalendarState {
+export type CalendarComponentProps = DomLocation & CalendarState & {
   viewSpec: ViewSpec
   dateProfileGenerator: DateProfileGenerator // for the current view
   eventUiBases: EventUiHash
+  title: string
 }
 
-export default class CalendarComponent extends Component<CalendarComponentProps> {
+export default class CalendarComponent extends Component<CalendarComponentProps, ComponentContext> {
 
   view: View
   header: Toolbar
@@ -33,11 +33,9 @@ export default class CalendarComponent extends Component<CalendarComponentProps>
   isHeightAuto: boolean
   viewHeight: number
 
-  private computeTitle = memoize(computeTitle)
   private parseBusinessHours = memoize((input) => {
     return parseBusinessHours(input, this.context.calendar)
   })
-  private buildViewComponent = renderer(this._buildViewComponent, this._clearViewComponent)
   private computeViewContextProps = memoize(computeContextProps)
   private buildViewPropTransformers = memoize(buildViewPropTransformers)
   private updateClassNames = renderer(this._setClassNames, this._unsetClassNames)
@@ -45,20 +43,20 @@ export default class CalendarComponent extends Component<CalendarComponentProps>
   private buildToolbarProps = memoize(this._buildToolbarProps)
   private renderHeader = renderer(Toolbar)
   private renderFooter = renderer(Toolbar)
+  private renderViews = listRenderer()
 
 
   /*
   renders INSIDE of an outer div
   */
   render(props: CalendarComponentProps, context: ComponentContext) {
-    let title = this.computeTitle(props.dateProfile, props.viewSpec.options)
     let toolbarProps = this.buildToolbarProps(
       props.viewSpec,
       props.dateProfile,
       props.dateProfileGenerator,
       props.currentDate,
       context.calendar.getNow(),
-      title
+      props.title
     )
     let innerEls: HTMLElement[] = []
 
@@ -66,7 +64,7 @@ export default class CalendarComponent extends Component<CalendarComponentProps>
     this.updateClassNames(true)
 
     if (context.options.header) {
-      let header = this.renderHeader(true, {
+      let header = this.renderHeader({
         extraClassName: 'fc-header-toolbar',
         layout: context.options.header,
         ...toolbarProps
@@ -77,11 +75,11 @@ export default class CalendarComponent extends Component<CalendarComponentProps>
     }
 
     let viewContainerEl = this.renderViewContainer(true)
-    this.renderView(props, title, viewContainerEl, context)
+    this.renderView(props, viewContainerEl, context)
     innerEls.push(viewContainerEl)
 
     if (context.options.footer) {
-      let footer = this.renderFooter(true, {
+      let footer = this.renderFooter({
         extraClassName: 'fc-footer-toolbar',
         layout: context.options.footer,
         ...toolbarProps
@@ -114,7 +112,7 @@ export default class CalendarComponent extends Component<CalendarComponentProps>
 
 
   _setClassNames(props: {}, context: ComponentContext) {
-    let classList = this.location.parent.classList
+    let classList = this.location.parentEl.classList
     let classNames: string[] = [
       'fc',
       'fc-' + context.options.dir,
@@ -130,7 +128,7 @@ export default class CalendarComponent extends Component<CalendarComponentProps>
 
 
   _unsetClassNames(classNames: string[]) {
-    let classList = this.location.parent.classList
+    let classList = this.location.parentEl.classList
 
     for (let className of classNames) {
       classList.remove(className)
@@ -171,14 +169,10 @@ export default class CalendarComponent extends Component<CalendarComponentProps>
   }
 
 
-  renderView(props: CalendarComponentProps, title: string, viewContainerEl: HTMLElement, context: ComponentContext) {
+  renderView(props: CalendarComponentProps, viewContainerEl: HTMLElement, context: ComponentContext) {
     let { pluginHooks, options } = context
     let { viewSpec } = props
 
-    let view = this.view = this.buildViewComponent(true, { viewClass: viewSpec.class })
-    view.type = viewSpec.type
-    view.title = title
-
     let viewProps: ViewProps = {
       viewSpec,
       dateProfileGenerator: props.dateProfileGenerator,
@@ -201,26 +195,21 @@ export default class CalendarComponent extends Component<CalendarComponentProps>
       )
     }
 
-    view.update(
-      viewContainerEl,
-      viewProps,
+    let views = this.renderViews({
+      parentEl: viewContainerEl
+    }, [
       {
-        ...context,
-        view,
-        options: viewSpec.options,
-        ...this.computeViewContextProps(viewSpec.options)
+        id: '',
+        componentClass: viewSpec.class,
+        props: viewProps
       }
-    )
-  }
-
-
-  _buildViewComponent(props: { viewClass: ViewClass }) {
-    return new props.viewClass(this.renderEngine)
-  }
-
+    ], {
+      ...context,
+      options: viewSpec.options,
+      ...this.computeViewContextProps(viewSpec.options)
+    })
 
-  _clearViewComponent(view: View) {
-    view.unmount()
+    this.view = views[0] as View
   }
 
 
@@ -255,7 +244,7 @@ export default class CalendarComponent extends Component<CalendarComponentProps>
     } else if (typeof heightInput === 'function') { // exists and is a function
       this.viewHeight = heightInput() - this.queryToolbarsHeight()
     } else if (heightInput === 'parent') { // set to height of parent element
-      let parentEl = this.location.parent.parentNode as HTMLElement
+      let parentEl = this.location.parentEl.parentNode as HTMLElement
       this.viewHeight = parentEl.getBoundingClientRect().height - this.queryToolbarsHeight()
     } else {
       this.viewHeight = Math.round(
@@ -286,7 +275,7 @@ export default class CalendarComponent extends Component<CalendarComponentProps>
 
 
   freezeHeight() {
-    let rootEl = this.location.parent
+    let rootEl = this.location.parentEl
 
     applyStyle(rootEl, {
       height: rootEl.getBoundingClientRect().height,
@@ -296,7 +285,7 @@ export default class CalendarComponent extends Component<CalendarComponentProps>
 
 
   thawHeight() {
-    let rootEl = this.location.parent
+    let rootEl = this.location.parentEl
 
     applyStyle(rootEl, {
       height: '',
@@ -307,58 +296,6 @@ export default class CalendarComponent extends Component<CalendarComponentProps>
 }
 
 
-// Title and Date Formatting
-// -----------------------------------------------------------------------------------------------------------------
-
-
-// Computes what the title at the top of the calendar should be for this view
-function computeTitle(this: CalendarComponent, dateProfile, viewOptions) {
-  let range: DateRange
-
-  // for views that span a large unit of time, show the proper interval, ignoring stray days before and after
-  if (/^(year|month)$/.test(dateProfile.currentRangeUnit)) {
-    range = dateProfile.currentRange
-  } else { // for day units or smaller, use the actual day range
-    range = dateProfile.activeRange
-  }
-
-  return this.context.dateEnv.formatRange(
-    range.start,
-    range.end,
-    createFormatter(
-      viewOptions.titleFormat || computeTitleFormat(dateProfile),
-      viewOptions.titleRangeSeparator
-    ),
-    { isEndExclusive: dateProfile.isRangeAllDay }
-  )
-}
-
-
-// Generates the format string that should be used to generate the title for the current date range.
-// Attempts to compute the most appropriate format if not explicitly specified with `titleFormat`.
-function computeTitleFormat(dateProfile) {
-  let currentRangeUnit = dateProfile.currentRangeUnit
-
-  if (currentRangeUnit === 'year') {
-    return { year: 'numeric' }
-  } else if (currentRangeUnit === 'month') {
-    return { year: 'numeric', month: 'long' } // like "September 2014"
-  } else {
-    let days = diffWholeDays(
-      dateProfile.currentRange.start,
-      dateProfile.currentRange.end
-    )
-    if (days !== null && days > 1) {
-      // multi-day range. shorter, like "Sep 9 - 10 2014"
-      return { year: 'numeric', month: 'short', day: 'numeric' }
-    } else {
-      // one day. longer, like "September 9 2014"
-      return { year: 'numeric', month: 'long', day: 'numeric' }
-    }
-  }
-}
-
-
 // Plugin
 // -----------------------------------------------------------------------------------------------------------------
 

+ 9 - 9
packages/core/src/Toolbar.ts

@@ -1,7 +1,7 @@
 import { htmlEscape } from './util/html'
 import { htmlToElement, appendToElement, findElements, createElement } from './util/dom-manip'
 import ComponentContext from './component/ComponentContext'
-import { Component, renderer } from './view-framework'
+import { Component, renderer, DomLocation } from './view-framework'
 import { ViewSpec } from './structs/view-spec'
 import Calendar from './Calendar'
 import Theme from './theme/Theme'
@@ -10,7 +10,7 @@ import Theme from './theme/Theme'
 /* Toolbar with buttons and title
 ----------------------------------------------------------------------------------------------------------------------*/
 
-export interface ToolbarRenderProps {
+export interface ToolbarRenderProps extends DomLocation {
   extraClassName: string
   layout: any
   title: string
@@ -20,7 +20,7 @@ export interface ToolbarRenderProps {
   isNextEnabled: boolean
 }
 
-export default class Toolbar extends Component<ToolbarRenderProps> {
+export default class Toolbar extends Component<ToolbarRenderProps, ComponentContext> {
 
   private renderBase = renderer(this._renderBase)
   private renderTitle = renderer(renderTitle)
@@ -34,16 +34,16 @@ export default class Toolbar extends Component<ToolbarRenderProps> {
 
   render(props: ToolbarRenderProps) {
 
-    let el = this.renderBase(true, {
+    let el = this.renderBase({
       extraClassName: props.extraClassName,
       layout: props.layout
     })
 
-    this.renderTitle(true, { el, text: props.title })
-    this.renderActiveButton(true, { el, buttonName: props.activeButton })
-    this.renderToday(true, { el, isEnabled: props.isTodayEnabled })
-    this.renderPrev(true, { el, isEnabled: props.isPrevEnabled })
-    this.renderNext(true, { el, isEnabled: props.isNextEnabled })
+    this.renderTitle({ el, text: props.title })
+    this.renderActiveButton({ el, buttonName: props.activeButton })
+    this.renderToday({ el, isEnabled: props.isTodayEnabled })
+    this.renderPrev({ el, isEnabled: props.isPrevEnabled })
+    this.renderNext({ el, isEnabled: props.isNextEnabled })
 
     return el
   }

+ 3 - 27
packages/core/src/View.ts

@@ -38,9 +38,6 @@ export default abstract class View extends DateComponent<ViewProps> {
   triggerWith: EmitterInterface['triggerWith']
   hasHandlers: EmitterInterface['hasHandlers']
 
-  type: string // subclass' view name (string). for the API
-  title: string // the text that will be displayed in the header's title. SET BY CALLER for API
-
   // now indicator
   isNowIndicatorRendered: boolean
   initialNowDate: DateMarker // result first getNow call
@@ -49,27 +46,6 @@ export default abstract class View extends DateComponent<ViewProps> {
   nowIndicatorIntervalID: any // "
 
 
-  // Date Setting/Unsetting
-  // -----------------------------------------------------------------------------------------------------------------
-
-
-  get activeStart(): Date {
-    return this.context.dateEnv.toDate(this.props.dateProfile.activeRange.start)
-  }
-
-  get activeEnd(): Date {
-    return this.context.dateEnv.toDate(this.props.dateProfile.activeRange.end)
-  }
-
-  get currentStart(): Date {
-    return this.context.dateEnv.toDate(this.props.dateProfile.currentRange.start)
-  }
-
-  get currentEnd(): Date {
-    return this.context.dateEnv.toDate(this.props.dateProfile.currentRange.end)
-  }
-
-
   // Sizing
   // -----------------------------------------------------------------------------------------------------------------
 
@@ -127,14 +103,14 @@ export default abstract class View extends DateComponent<ViewProps> {
   // which is defined by this.getNowIndicatorUnit().
   // TODO: somehow do this for the current whole day's background too
   // USAGE: must be called manually from subclasses' render methods! don't need to call stopNowIndicator tho
-  startNowIndicator(dateProfile: DateProfile, dateProfileGenerator: DateProfileGenerator) {
+  startNowIndicator() {
     let { calendar, dateEnv, options } = this.context
     let unit
     let update
     let delay // ms wait value
 
     if (options.nowIndicator && !this.initialNowDate) {
-      unit = this.getNowIndicatorUnit(dateProfile, dateProfileGenerator)
+      unit = this.getNowIndicatorUnit()
 
       if (unit) {
         update = this.updateNowIndicator.bind(this)
@@ -205,7 +181,7 @@ export default abstract class View extends DateComponent<ViewProps> {
   }
 
 
-  getNowIndicatorUnit(dateProfile: DateProfile, dateProfileGenerator: DateProfileGenerator) {
+  getNowIndicatorUnit() {
     // subclasses should implement
   }
 

+ 23 - 0
packages/core/src/ViewApi.ts

@@ -0,0 +1,23 @@
+import { DateProfile } from './DateProfileGenerator'
+import { DateEnv } from './datelib/env'
+
+export default class ViewApi {
+
+  activeStart: Date
+  activeEnd: Date
+  currentStart: Date
+  currentEnd: Date
+
+  constructor(
+    public type: string,
+    public title: string,
+    dateProfile: DateProfile,
+    dateEnv: DateEnv
+  ) {
+    this.activeStart = dateEnv.toDate(dateProfile.activeRange.start)
+    this.activeEnd = dateEnv.toDate(dateProfile.activeRange.end)
+    this.currentStart = dateEnv.toDate(dateProfile.currentRange.start)
+    this.currentEnd = dateEnv.toDate(dateProfile.currentRange.end)
+  }
+
+}

+ 2 - 2
packages/core/src/common/DayHeader.ts

@@ -1,4 +1,4 @@
-import { Component } from '../view-framework'
+import { Component, DomLocation } from '../view-framework'
 import ComponentContext from '../component/ComponentContext'
 import { htmlToElement } from '../util/dom-manip'
 import { DateMarker } from '../datelib/marker'
@@ -6,7 +6,7 @@ import { DateProfile } from '../DateProfileGenerator'
 import { createFormatter } from '../datelib/formatting'
 import { computeFallbackHeaderFormat, renderDateCell } from './table-utils'
 
-export interface DayHeaderProps {
+export interface DayHeaderProps extends DomLocation {
   dates: DateMarker[]
   dateProfile: DateProfile
   datesRepDistinctDays: boolean

+ 2 - 2
packages/core/src/common/Scroller.ts

@@ -1,7 +1,7 @@
 import { computeEdges } from '../util/dom-geom'
 import { createElement, applyStyle, applyStyleProp } from '../util/dom-manip'
 import { ElementScrollController } from './scroll-controller'
-import { Component } from '../view-framework'
+import { Component, DomLocation } from '../view-framework'
 
 export interface ScrollbarWidths {
   left: number
@@ -9,7 +9,7 @@ export interface ScrollbarWidths {
   bottom: number
 }
 
-export interface ScrollerProps {
+export interface ScrollerProps extends DomLocation {
   overflowX: string
   overflowY: string
 }

+ 2 - 2
packages/core/src/component/ComponentContext.ts

@@ -1,5 +1,5 @@
 import Calendar from '../Calendar'
-import View from '../View'
+import ViewApi from '../ViewApi'
 import Theme from '../theme/Theme'
 import { DateEnv } from '../datelib/env'
 import { parseFieldSpecs } from '../util/misc'
@@ -10,7 +10,7 @@ import { PluginHooks } from '../plugin-system'
 export default interface ComponentContext {
   calendar: Calendar
   pluginHooks: PluginHooks
-  view?: View
+  view?: ViewApi
   dateEnv: DateEnv
   theme: Theme
   options: any

+ 4 - 3
packages/core/src/component/DateComponent.ts

@@ -7,13 +7,14 @@ import { Hit } from '../interactions/hit'
 import { elementClosest } from '../util/dom-manip'
 import { isDateSelectionValid, isInteractionValid } from '../validation'
 import { EventInteractionState } from '../interactions/event-interaction-state'
+import ComponentContext from '../component/ComponentContext'
 
-export type DateComponentHash = { [uid: string]: DateComponent<any> }
+export type DateComponentHash = { [uid: string]: DateComponent<any, any> }
 
 // NOTE: for fg-events, eventRange.range is NOT sliced,
 // thus, we need isStart/isEnd
 export interface Seg {
-  component?: DateComponent<any>
+  component?: DateComponent<any, any>
   isStart: boolean
   isEnd: boolean
   eventRange?: EventRenderRange
@@ -33,7 +34,7 @@ PURPOSES:
 - hook up to fg, fill, and mirror renderers
 - interface for dragging and hits
 */
-export default abstract class DateComponent<PropsType, StateType = {}> extends Component<PropsType, StateType> {
+export default abstract class DateComponent<Props, State={}, Snapshot={}> extends Component<Props, ComponentContext, State, Snapshot> {
 
   // self-config, overridable by subclasses. must set on prototype
   fgSegSelector: string // lets eventRender produce elements without fc-event class

+ 2 - 2
packages/core/src/component/event-rendering.ts

@@ -249,12 +249,12 @@ export function triggerWillRemoveSegs(context: ComponentContext, segs: Seg[], is
 // is-interactable
 
 export function computeEventDraggable(context: ComponentContext, eventDef: EventDef, eventUi: EventUi) {
-  let { pluginHooks, view } = context
+  let { pluginHooks, calendar } = context
   let transformers = pluginHooks.isDraggableTransformers
   let val = eventUi.startEditable
 
   for (let transformer of transformers) {
-    val = transformer(val, eventDef, eventUi, view)
+    val = transformer(val, eventDef, eventUi, calendar.component.view) // yuck
   }
 
   return val

+ 4 - 4
packages/core/src/component/renderers/FgEventRenderer.ts

@@ -20,7 +20,7 @@ export interface BaseFgEventRendererProps {
 
 export default abstract class FgEventRenderer<
   FgEventRendererProps extends BaseFgEventRendererProps = BaseFgEventRendererProps
-> extends Component<FgEventRendererProps> {
+> extends Component<FgEventRendererProps, ComponentContext> {
 
   private updateComputedOptions = memoize(this._updateComputedOptions)
   private renderSegsPlain = renderer(this._renderSegsPlain, this._unrenderSegsPlain)
@@ -40,17 +40,17 @@ export default abstract class FgEventRenderer<
   renderSegs(props: BaseFgEventRendererProps, context: ComponentContext) {
     this.updateComputedOptions(context.options)
 
-    let { segs } = this.renderSegsPlain(true, {
+    let { segs } = this.renderSegsPlain({
       segs: props.segs,
       mirrorInfo: props.mirrorInfo
     })
 
-    this.renderSelectedInstance(true, {
+    this.renderSelectedInstance({
       segs,
       instanceId: props.selectedInstanceId
     })
 
-    this.renderHiddenInstances(true, {
+    this.renderHiddenInstances({
       segs,
       hiddenInstances: props.hiddenInstances
     })

+ 1 - 1
packages/core/src/component/renderers/FillRenderer.ts

@@ -11,7 +11,7 @@ export interface BaseFillRendererProps {
 }
 
 // use for highlight, background events, business hours
-export default abstract class FillRenderer<FillRendererProps extends BaseFillRendererProps> extends Component<FillRendererProps> {
+export default abstract class FillRenderer<FillRendererProps extends BaseFillRendererProps> extends Component<FillRendererProps, ComponentContext> {
 
   renderSegs = renderer(this._renderSegs, this._unrenderSegs)
 

+ 2 - 0
packages/core/src/event-sources/func-event-source.ts

@@ -7,6 +7,8 @@ export type EventSourceFunc = (
   arg: {
     start: Date
     end: Date
+    startStr: string
+    endStr: string
     timeZone: string
   },
   successCallback: (events: EventInput[]) => void,

+ 3 - 1
packages/core/src/main.ts

@@ -59,6 +59,7 @@ export {
   findElements,
   findDirectChildren,
   htmlToElement,
+  htmlToElements,
   createElement,
   insertAfterElement,
   prependToElement,
@@ -104,6 +105,7 @@ export { default as ComponentContext } from './component/ComponentContext'
 export { default as DateComponent, Seg, EventSegUiInteractionState } from './component/DateComponent'
 export { default as Calendar, DatePointTransform, DateSpanTransform, DateSelectionApi } from './Calendar'
 export { default as View, ViewProps, renderViewEl } from './View'
+export { default as ViewApi } from './ViewApi'
 export { default as FgEventRenderer, buildSegCompareObj, BaseFgEventRendererProps, sortEventSegs } from './component/renderers/FgEventRenderer'
 export { default as FillRenderer, BaseFillRendererProps } from './component/renderers/FillRenderer'
 
@@ -170,4 +172,4 @@ export { default as EventApi } from './api/EventApi'
 
 export { default as requestJson } from './util/requestJson'
 
-export { Component, renderer } from './view-framework'
+export { Component, renderer, DomLocation, listRenderer, ListRendererItem } from './view-framework'

+ 4 - 3
packages/core/src/structs/view-config.ts

@@ -1,7 +1,7 @@
-import View from '../View'
+import View, { ViewProps } from '../View'
 import { refineProps } from '../util/misc'
 import { mapHash } from '../util/object'
-import { RenderEngine } from '../view-framework'
+import { ComponentContext } from '@fullcalendar/core'
 
 /*
 A view-config represents information for either:
@@ -10,7 +10,8 @@ B) options to customize an existing view, in which case only provides options.
 */
 
 export type ViewClass = new(
-  renderEngine: RenderEngine
+  props: ViewProps,
+  context: ComponentContext
 ) => View
 
 export interface ViewConfigObjectInput {

+ 28 - 28
packages/core/src/types/input-types.ts

@@ -3,7 +3,7 @@ Huge thanks to these people:
 https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/fullcalendar/index.d.ts
 */
 
-import View from '../View'
+import ViewApi from '../ViewApi'
 import { EventSourceInput, EventInputTransformer } from '../structs/event-source'
 import { Duration, DurationInput } from '../datelib/duration'
 import { DateInput } from '../datelib/env'
@@ -112,7 +112,7 @@ export interface OptionsInputBase {
   windowResizeDelay?: number
   eventLimit?: boolean | number
   eventLimitClick?: 'popover' | 'week' | 'day' | 'timeGridWeek' | 'timeGridDay' | string |
-    ((arg: { date: Date, allDay: boolean, dayEl: HTMLElement, moreEl: HTMLElement, segs: any[], hiddenSegs: any[], jsEvent: MouseEvent, view: View }) => void),
+    ((arg: { date: Date, allDay: boolean, dayEl: HTMLElement, moreEl: HTMLElement, segs: any[], hiddenSegs: any[], jsEvent: MouseEvent, view: ViewApi }) => void),
   timeZone?: string | boolean
   now?: DateInput | (() => DateInput)
   defaultView?: string
@@ -205,37 +205,37 @@ export interface OptionsInputBase {
   selectMinDistance?: number
   timeZoneParam?: string
   titleRangeSeparator?: string
-  datesRender?(arg: { view: View, el: HTMLElement }): void
-  datesDestroy?(arg: { view: View, el: HTMLElement }): void
-  dayRender?(arg: { view: View, date: Date, allDay?: boolean, el: HTMLElement }): void
-  windowResize?(view: View): void
-  dateClick?(arg: { date: Date, dateStr: string, allDay: boolean, resource?: any, dayEl: HTMLElement, jsEvent: MouseEvent, view: View }): void // resource for Scheduler
-  eventClick?(arg: { el: HTMLElement, event: EventApi, jsEvent: MouseEvent, view: View }): boolean | void
-  eventMouseEnter?(arg: { el: HTMLElement, event: EventApi, jsEvent: MouseEvent, view: View }): void
-  eventMouseLeave?(arg: { el: HTMLElement, event: EventApi, jsEvent: MouseEvent, view: View }): void
-  select?(arg: { start: Date, end: Date, startStr: string, endStr: string, allDay: boolean, resource?: any, jsEvent: MouseEvent, view: View }): void // resource for Scheduler
-  unselect?(arg: { view: View, jsEvent: Event }): void
+  datesRender?(arg: { view: ViewApi, el: HTMLElement }): void
+  datesDestroy?(arg: { view: ViewApi, el: HTMLElement }): void
+  dayRender?(arg: { view: ViewApi, date: Date, allDay?: boolean, el: HTMLElement }): void
+  windowResize?(view: ViewApi): void
+  dateClick?(arg: { date: Date, dateStr: string, allDay: boolean, resource?: any, dayEl: HTMLElement, jsEvent: MouseEvent, view: ViewApi }): void // resource for Scheduler
+  eventClick?(arg: { el: HTMLElement, event: EventApi, jsEvent: MouseEvent, view: ViewApi }): boolean | void
+  eventMouseEnter?(arg: { el: HTMLElement, event: EventApi, jsEvent: MouseEvent, view: ViewApi }): void
+  eventMouseLeave?(arg: { el: HTMLElement, event: EventApi, jsEvent: MouseEvent, view: ViewApi }): void
+  select?(arg: { start: Date, end: Date, startStr: string, endStr: string, allDay: boolean, resource?: any, jsEvent: MouseEvent, view: ViewApi }): void // resource for Scheduler
+  unselect?(arg: { view: ViewApi, jsEvent: Event }): void
   loading?(isLoading: boolean): void
-  eventRender?(arg: { isMirror: boolean, isStart: boolean, isEnd: boolean, event: EventApi, el: HTMLElement, view: View }): void
-  eventPositioned?(arg: { isMirror: boolean, isStart: boolean, isEnd: boolean, event: EventApi, el: HTMLElement, view: View }): void
-  _eventsPositioned?(arg: { view: View }): void
-  eventDestroy?(arg: { isMirror: boolean, event: EventApi, el: HTMLElement, view: View }): void
-  eventDragStart?(arg: { event: EventApi, el: HTMLElement, jsEvent: MouseEvent, view: View }): void
-  eventDragStop?(arg: { event: EventApi, el: HTMLElement, jsEvent: MouseEvent, view: View }): void
-  eventDrop?(arg: { el: HTMLElement, event: EventApi, oldEvent: EventApi, delta: Duration, revert: () => void, jsEvent: Event, view: View }): void
-  eventResizeStart?(arg: { el: HTMLElement, event: EventApi, jsEvent: MouseEvent, view: View }): void
-  eventResizeStop?(arg: { el: HTMLElement, event: EventApi, jsEvent: MouseEvent, view: View }): void
-  eventResize?(arg: { el: HTMLElement, startDelta: Duration, endDelta: Duration, prevEvent: EventApi, event: EventApi, revert: () => void, jsEvent: Event, view: View }): void
-  drop?(arg: { date: Date, dateStr: string, allDay: boolean, draggedEl: HTMLElement, jsEvent: MouseEvent, view: View }): void
-  eventReceive?(arg: { event: EventApi, draggedEl: HTMLElement, view: View }): void
-  eventLeave?(arg: { draggedEl: HTMLElement, event: EventApi, view: View }): void
-  viewSkeletonRender?(arg: { el: HTMLElement, view: View }): void
-  viewSkeletonDestroy?(arg: { el: HTMLElement, view: View }): void
+  eventRender?(arg: { isMirror: boolean, isStart: boolean, isEnd: boolean, event: EventApi, el: HTMLElement, view: ViewApi }): void
+  eventPositioned?(arg: { isMirror: boolean, isStart: boolean, isEnd: boolean, event: EventApi, el: HTMLElement, view: ViewApi }): void
+  _eventsPositioned?(arg: { view: ViewApi }): void
+  eventDestroy?(arg: { isMirror: boolean, event: EventApi, el: HTMLElement, view: ViewApi }): void
+  eventDragStart?(arg: { event: EventApi, el: HTMLElement, jsEvent: MouseEvent, view: ViewApi }): void
+  eventDragStop?(arg: { event: EventApi, el: HTMLElement, jsEvent: MouseEvent, view: ViewApi }): void
+  eventDrop?(arg: { el: HTMLElement, event: EventApi, oldEvent: EventApi, delta: Duration, revert: () => void, jsEvent: Event, view: ViewApi }): void
+  eventResizeStart?(arg: { el: HTMLElement, event: EventApi, jsEvent: MouseEvent, view: ViewApi }): void
+  eventResizeStop?(arg: { el: HTMLElement, event: EventApi, jsEvent: MouseEvent, view: ViewApi }): void
+  eventResize?(arg: { el: HTMLElement, startDelta: Duration, endDelta: Duration, prevEvent: EventApi, event: EventApi, revert: () => void, jsEvent: Event, view: ViewApi }): void
+  drop?(arg: { date: Date, dateStr: string, allDay: boolean, draggedEl: HTMLElement, jsEvent: MouseEvent, view: ViewApi }): void
+  eventReceive?(arg: { event: EventApi, draggedEl: HTMLElement, view: ViewApi }): void
+  eventLeave?(arg: { draggedEl: HTMLElement, event: EventApi, view: ViewApi }): void
+  viewSkeletonRender?(arg: { el: HTMLElement, view: ViewApi }): void
+  viewSkeletonDestroy?(arg: { el: HTMLElement, view: ViewApi }): void
   _destroyed?(): void
   _init?(): void
   _noEventDrop?(): void
   _noEventResize?(): void
-  resourceRender?(arg: { resource: any, el: HTMLElement, view: View }): void
+  resourceRender?(arg: { resource: any, el: HTMLElement, view: ViewApi }): void
 }
 
 export interface ViewOptionsInput extends OptionsInputBase {

+ 1 - 1
packages/core/src/validation.ts

@@ -30,7 +30,7 @@ export function isDateSelectionValid(dateSelection: DateSpan, calendar: Calendar
 }
 
 function isNewPropsValid(newProps, calendar: Calendar) {
-  let view = calendar.view
+  let view = calendar.component.view
 
   let props = {
     businessHours: view ? view.props.businessHours : createEmptyEventStore(), // why? yuck

+ 7 - 7
packages/daygrid/src/DayTable.ts

@@ -11,11 +11,12 @@ import {
   Slicer,
   Hit,
   ComponentContext,
-  renderer
+  renderer,
+  DomLocation
 } from '@fullcalendar/core'
 import { default as Table, TableSeg, TableRenderProps } from './Table'
 
-export interface DayTableProps {
+export interface DayTableProps extends DomLocation {
   renderProps: TableRenderProps
   dateProfile: DateProfile | null
   dayTableModel: DayTableModel
@@ -30,19 +31,19 @@ export interface DayTableProps {
   isRigid: boolean
 }
 
-export default class DayTable extends DateComponent<DayTableProps> {
+export default class DayTable extends DateComponent<DayTableProps, ComponentContext> {
 
   private renderTable = renderer(Table)
   private registerInteractive = renderer(this._registerInteractive, this._unregisterInteractive)
-
   private slicer = new DayTableSlicer()
+
   table: Table
 
 
   render(props: DayTableProps, context: ComponentContext) {
     let { dateProfile, dayTableModel } = props
 
-    let table = this.renderTable(true, {
+    let table = this.table = this.renderTable({
       ...this.slicer.sliceProps(props, dateProfile, props.nextDayThreshold, context.calendar, dayTableModel),
       dateProfile,
       cells: dayTableModel.cells,
@@ -50,11 +51,10 @@ export default class DayTable extends DateComponent<DayTableProps> {
       renderProps: props.renderProps
     })
 
-    this.registerInteractive(true, {
+    this.registerInteractive({
       el: table.rootEl
     })
 
-    this.table = table
     return table
   }
 

+ 5 - 3
packages/daygrid/src/DayTableView.ts

@@ -27,18 +27,20 @@ export default class DayTableView extends TableView {
     let { dateProfile } = props
     let dayTableModel = this.buildDayTableModel(dateProfile, props.dateProfileGenerator)
 
-    let { rootEl, headerWrapEl, tableWrapEl } = this.renderLayout({
+    let { rootEl, headerWrapEl, contentWrapEl } = this.renderLayout({
       type: props.viewSpec.type
     }, context)
 
-    this.header = this.renderHeader(headerWrapEl, { // location might be null
+    this.header = this.renderHeader({
+      parentEl: headerWrapEl, // might be null
       dateProfile,
       dates: dayTableModel.headerDates,
       datesRepDistinctDays: dayTableModel.rowCnt === 1,
       renderIntroHtml: this.renderHeadIntroHtml
     })
 
-    this.table = this.renderTable(tableWrapEl, {
+    this.table = this.renderTable({
+      parentEl: contentWrapEl,
       renderProps: this.tableRenderProps,
       dateProfile,
       dayTableModel,

+ 8 - 7
packages/daygrid/src/DayTile.ts

@@ -6,12 +6,13 @@ import {
   addDays, DateMarker,
   ComponentContext,
   EventInstanceHash,
-  renderer
+  renderer,
+  DomLocation,
+  htmlToElements
 } from '@fullcalendar/core'
 import DayTileEvents from './DayTileEvents'
-import { htmlToElements } from '@fullcalendar/core/util/dom-manip'
 
-export interface DayTileProps {
+export interface DayTileProps extends DomLocation {
   date: DateMarker
   fgSegs: Seg[]
   selectedInstanceId: string
@@ -25,11 +26,11 @@ export default class DayTile extends DateComponent<DayTileProps> {
 
 
   render(props: DayTileProps) {
-    let { rootEls, segContainerEl } = this.renderFrame(true, {
+    let { rootEls, segContainerEl } = this.renderFrame({
       date: props.date
     })
 
-    this.renderEvents(true, {
+    this.renderEvents({
       segs: props.fgSegs,
       segContainerEl,
       selectedInstanceId: props.selectedInstanceId,
@@ -48,7 +49,7 @@ export default class DayTile extends DateComponent<DayTileProps> {
     // HACK referencing parent's elements.
     // also, if parent's elements change, this will break.
     calendar.registerInteractiveComponent(this, {
-      el: this.location.parent,
+      el: this.location.parentEl,
       useEventCenter: false
     })
   }
@@ -69,7 +70,7 @@ export default class DayTile extends DateComponent<DayTileProps> {
           allDay: true,
           range: { start: date, end: addDays(date, 1) }
         },
-        dayEl: this.location.parent, // HACK
+        dayEl: this.location.parentEl, // HACK
         rect: {
           left: 0,
           top: 0,

+ 7 - 3
packages/daygrid/src/DayTileEvents.ts

@@ -2,7 +2,8 @@ import {
   Seg,
   ComponentContext,
   BaseFgEventRendererProps,
-  renderer
+  renderer,
+  DomLocation
 } from '@fullcalendar/core'
 import CellEvents from './CellEvents'
 
@@ -24,12 +25,15 @@ export default class DayTileEvents extends CellEvents<DayTileEventsProps> {
       hiddenInstances: props.hiddenInstances
     }, context)
 
-    this.attachSegs(props.segContainerEl, { segs })
+    this.attachSegs({
+      parentEl: props.segContainerEl,
+      segs
+    })
   }
 
 }
 
 
-function attachSegs(props: { segs: Seg[] }) {
+function attachSegs(props: { segs: Seg[] } & DomLocation) {
   return props.segs.map((seg) => seg.el)
 }

+ 3 - 3
packages/daygrid/src/Popover.ts

@@ -6,10 +6,10 @@ import {
   createElement,
   applyStyle,
   listenBySelector,
-  computeClippingRect, computeRect, Component, ComponentContext
+  computeClippingRect, computeRect, Component, ComponentContext, DomLocation
 } from '@fullcalendar/core'
 
-export interface PopoverProps {
+export interface PopoverProps extends DomLocation {
   clippingEl: HTMLElement
   extraClassName?: string
   top?: number
@@ -18,7 +18,7 @@ export interface PopoverProps {
   onClose?: () => void
 }
 
-export default class Popover extends Component<PopoverProps> {
+export default class Popover extends Component<PopoverProps, ComponentContext> {
 
 
   render(props: PopoverProps, context: ComponentContext) {

+ 16 - 13
packages/daygrid/src/Table.ts

@@ -27,6 +27,7 @@ import TableMirrorEvents from './TableMirrorEvents'
 import TableFills from './TableFills'
 import DayTile from './DayTile'
 import { renderDayBgRowHtml } from './DayBgRow'
+import { DomLocation } from '@fullcalendar/core/view-framework'
 
 const DAY_NUM_FORMAT = createFormatter({ day: 'numeric' })
 const WEEK_NUM_FORMAT = createFormatter({ week: 'numeric' })
@@ -65,7 +66,7 @@ export interface CellModel {
   htmlAttrs?: string
 }
 
-export interface TableProps {
+export interface TableProps extends DomLocation {
   renderProps: TableRenderProps
   dateProfile: DateProfile
   cells: CellModel[][]
@@ -82,7 +83,7 @@ export interface TableProps {
   isRigid: boolean
 }
 
-export default class Table extends Component<TableProps, TableState> {
+export default class Table extends Component<TableProps, ComponentContext, TableState> {
 
   private renderCells = renderer(this._renderCells)
   private renderFgEvents = renderer(TableEvents)
@@ -108,7 +109,7 @@ export default class Table extends Component<TableProps, TableState> {
     let segPopoverState = state.segPopover
     let colCnt = props.cells[0].length
 
-    let { rootEl, rowEls, cellEls } = this.renderCells(true, {
+    let { rootEl, rowEls, cellEls } = this.renderCells({
       renderProps: props.renderProps,
       dateProfile: props.dateProfile,
       cells: props.cells,
@@ -116,7 +117,7 @@ export default class Table extends Component<TableProps, TableState> {
     })
 
     if (props.eventDrag) {
-      this.renderHighlight(true, {
+      this.renderHighlight({
         type: 'highlight',
         renderProps: props.renderProps,
         segs: props.eventDrag.segs,
@@ -124,7 +125,7 @@ export default class Table extends Component<TableProps, TableState> {
         colCnt
       })
     } else {
-      this.renderHighlight(true, {
+      this.renderHighlight({
         type: 'highlight',
         renderProps: props.renderProps,
         segs: props.dateSelectionSegs,
@@ -133,7 +134,7 @@ export default class Table extends Component<TableProps, TableState> {
       })
     }
 
-    this.renderBusinessHours(true, {
+    this.renderBusinessHours({
       type: 'businessHours',
       renderProps: props.renderProps,
       segs: props.businessHourSegs,
@@ -141,7 +142,7 @@ export default class Table extends Component<TableProps, TableState> {
       colCnt
     })
 
-    this.renderBgEvents(true, {
+    this.renderBgEvents({
       type: 'bgEvent',
       renderProps: props.renderProps,
       segs: props.bgEventSegs,
@@ -149,7 +150,7 @@ export default class Table extends Component<TableProps, TableState> {
       colCnt
     })
 
-    let eventsRenderer = this.renderFgEvents(true, {
+    let eventsRenderer = this.renderFgEvents({
       renderProps: props.renderProps,
       segs: props.fgEventSegs,
       rowEls,
@@ -161,7 +162,7 @@ export default class Table extends Component<TableProps, TableState> {
     })
 
     if (props.eventResize) {
-      this.renderMirrorEvents(true, {
+      this.renderMirrorEvents({
         renderProps: props.renderProps,
         segs: props.eventResize.segs,
         rowEls,
@@ -176,9 +177,10 @@ export default class Table extends Component<TableProps, TableState> {
       segPopoverState &&
       segPopoverState.origFgSegs === props.fgEventSegs // will close popover when events change
     ) {
-      let viewEl = context.view.rootEl
+      let viewEl = context.calendar.component.view.rootEl // yuck
 
-      let popover = this.renderPopover(viewEl, { // will be outside of all scrollers within the view
+      let popover = this.renderPopover({ // will be outside of all scrollers within the view
+        parentEl: viewEl,
         top: segPopoverState.top,
         left: segPopoverState.left,
         right: segPopoverState.right,
@@ -186,7 +188,8 @@ export default class Table extends Component<TableProps, TableState> {
         clippingEl: viewEl
       })
 
-      this.renderTileForPopover(popover.rootEl, { // renders the close icon too, for clicking
+      this.renderTileForPopover({ // renders the close icon too, for clicking
+        parentEl: popover.rootEl,
         date: state.segPopover.date,
         fgSegs: state.segPopover.fgSegs,
         selectedInstanceId: props.eventSelection,
@@ -728,7 +731,7 @@ export default class Table extends Component<TableProps, TableState> {
       if (clickOption === 'popover') {
         let _col = isRtl ? colCnt - col - 1 : col // HACK: props.cells has different dir system?
         let topEl = rowCnt === 1
-          ? context.view.rootEl // will cause the popover to cover any sort of header
+          ? context.calendar.component.view.rootEl // will cause the popover to cover any sort of header
           : rowEls[row] // will align with top of row
         let left, right
 

+ 1 - 1
packages/daygrid/src/TableEvents.ts

@@ -36,7 +36,7 @@ export default class TableEvents extends CellEvents<TableEventsProps> {
       hiddenInstances: props.hiddenInstances
     }, context)
 
-    let rowStructs = this.attachSegs(true, {
+    let rowStructs = this.attachSegs({
       renderProps: props.renderProps,
       segs,
       rowEls: props.rowEls,

+ 2 - 2
packages/daygrid/src/TableFills.ts

@@ -39,12 +39,12 @@ export default class TableFills extends FillRenderer<TableFillsProps> {
       })
     }
 
-    segs = this.renderSegs(true, {
+    segs = this.renderSegs({
       type: props.type,
       segs
     })
 
-    this.attachSegs(true, {
+    this.attachSegs({
       type: props.type,
       renderProps: props.renderProps,
       segs,

+ 3 - 2
packages/daygrid/src/TableView.ts

@@ -44,9 +44,10 @@ export default abstract class TableView extends View {
   renderLayout(options: { type: string }, context: ComponentContext) {
     this.processOptions(context.options)
 
-    let res = this.renderSkeleton(true, options)
+    let res = this.renderSkeleton(options)
 
-    let scroller = this.renderScroller(res.contentWrapEl, {
+    let scroller = this.renderScroller({
+      parentEl: res.contentWrapEl,
       overflowX: 'hidden',
       overflowY: 'auto'
     })

+ 2 - 3
packages/interaction/src/interactions/EventDragging.ts

@@ -10,7 +10,6 @@ import {
   diffDates, enableCursor, disableCursor,
   EventRenderRange, getElSeg,
   EventApi,
-  View,
   eventDragMutationMassager,
   Interaction, InteractionSettings, interactionSettingsStore,
   EventDropTransformers
@@ -308,7 +307,7 @@ export default class EventDragging extends Interaction { // TODO: rename to Even
             ...receivingCalendar.buildDatePointApi(finalHit.dateSpan),
             draggedEl: ev.subjectEl as HTMLElement,
             jsEvent: ev.origEvent as MouseEvent, // Is this always a mouse event? See #4655
-            view: finalHit.component as View // should this be finalHit.component.view? See #4644
+            view: finalHit.component.context.view
           }
           receivingCalendar.publiclyTrigger('drop', [ dropArg ])
 
@@ -320,7 +319,7 @@ export default class EventDragging extends Interaction { // TODO: rename to Even
                 mutatedRelevantEvents.defs[eventDef.defId],
                 mutatedRelevantEvents.instances[eventInstance.instanceId]
               ),
-              view: finalHit.component as View // should this be finalHit.component.view? See #4644
+              view: finalHit.component.context.view
             }
           ])
         }

+ 4 - 3
packages/list/src/ListView.ts

@@ -41,16 +41,17 @@ export default class ListView extends View {
   render(props: ViewProps) {
     let { dayDates, dayRanges } = this.computeDateVars(props.dateProfile)
 
-    let rootEl = this.renderSkeleton(true, {
+    let rootEl = this.renderSkeleton({
       viewSpec: props.viewSpec
     })
 
-    this.scroller = this.renderScroller(rootEl, {
+    this.scroller = this.renderScroller({
+      parentEl: rootEl,
       overflowX: 'hidden',
       overflowY: 'auto'
     })
 
-    this.eventRenderer = this.renderEvents(true, {
+    this.eventRenderer = this.renderEvents({
       segs: this.eventStoreToSegs(props.eventStore, props.eventUiBases, dayRanges),
       dayDates,
       contentEl: this.scroller.el,

+ 1 - 1
packages/list/src/ListViewEvents.ts

@@ -34,7 +34,7 @@ export default class ListViewEvents extends FgEventRenderer<ListViewEventsProps>
       hiddenInstances: props.hiddenInstances
     }, context)
 
-    this.attachSegs(true, {
+    this.attachSegs({
       segs,
       dayDates: props.dayDates,
       contentEl: props.contentEl

+ 5 - 4
packages/timegrid/src/DayTimeCols.ts

@@ -13,11 +13,12 @@ import {
   Slicer,
   Hit,
   ComponentContext,
-  renderer
+  renderer,
+  DomLocation
 } from '@fullcalendar/core'
 import TimeCols, { TimeColsSeg, TimeColsRenderProps } from './TimeCols'
 
-export interface DayTimeColsProps {
+export interface DayTimeColsProps extends DomLocation {
   renderProps: TimeColsRenderProps
   dateProfile: DateProfile | null
   dayTableModel: DayTableModel
@@ -47,14 +48,14 @@ export default class DayTimeCols extends DateComponent<DayTimeColsProps> {
 
     let dayRanges = this.buildDayRanges(dayTableModel, dateProfile, dateEnv)
 
-    let timeCols = this.renderTimeCols(true, {
+    let timeCols = this.renderTimeCols({
       ...this.slicer.sliceProps(props, dateProfile, null, context.calendar, dayRanges),
       renderProps: props.renderProps,
       dateProfile,
       cells: dayTableModel.cells[0] // give the first row
     })
 
-    this.registerInteractive(true, { el: timeCols.rootEl })
+    this.registerInteractive({ el: timeCols.rootEl })
 
     this.dayRanges = dayRanges
     this.timeCols = timeCols

+ 9 - 5
packages/timegrid/src/DayTimeColsView.ts

@@ -34,16 +34,19 @@ export default class DayTimeColsView extends TimeColsView {
       rootEl,
       headerWrapEl,
       contentWrapEl
-    } = this.renderLayout({ type: props.viewSpec.type }, context)
+    } = this.renderLayout({ type: props.viewSpec.type })
 
-    this.renderDayHeader(headerWrapEl, { // might be null
+    this.renderDayHeader({
+      parentEl: headerWrapEl, // might be null
       dateProfile,
       dates: dayTableModel.headerDates,
       datesRepDistinctDays: true,
       renderIntroHtml: this.renderHeadIntroHtml
     })
 
-    let allDayTable = this.renderDayTable({ parent: contentWrapEl, prepend: true }, { // might be null... TODO: make sure API handles this!!!
+    let allDayTable = this.renderDayTable({
+      parentEl: contentWrapEl, // might be null
+      prepend: true,
       ...splitProps['allDay'],
       dateProfile,
       dayTableModel,
@@ -52,14 +55,15 @@ export default class DayTimeColsView extends TimeColsView {
       renderProps: this.tableRenderProps
     })
 
-    let timeCols = this.renderDayTimeCols(contentWrapEl, {
+    let timeCols = this.renderDayTimeCols({
+      parentEl: contentWrapEl,
       ...splitProps['timed'],
       dateProfile,
       dayTableModel,
       renderProps: this.timeColsRenderProps
     })
 
-    this.startNowIndicator(dateProfile, dateProfileGenerator)
+    this.startNowIndicator()
 
     this.allDayTable = allDayTable
     this.timeCols = timeCols

+ 18 - 14
packages/timegrid/src/TimeCols.ts

@@ -25,6 +25,7 @@ import {
   sortEventSegs,
   memoize,
   renderer,
+  DomLocation,
 } from '@fullcalendar/core'
 import { renderDayBgRowHtml } from '@fullcalendar/daygrid'
 import TimeColsEvents from './TimeColsEvents'
@@ -74,7 +75,7 @@ export interface TimeColsProps {
   eventResize: EventSegUiInteractionState | null
 }
 
-export default class TimeCols extends Component<TimeColsProps> {
+export default class TimeCols extends Component<TimeColsProps, ComponentContext> {
 
   processOptions = memoize(this._processOptions)
   renderSkeleton = renderer(renderSkeleton)
@@ -173,14 +174,16 @@ export default class TimeCols extends Component<TimeColsProps> {
       slatContainerEl
     } = this.renderSkeleton(true)
 
-    this.renderBgColumns(rootBgContainerEl, {
+    this.renderBgColumns({
+      parentEl: rootBgContainerEl,
       rootEl,
       cells: props.cells,
       dateProfile: props.dateProfile,
       renderProps: props.renderProps
     })
 
-    this.renderSlats(slatContainerEl, {
+    this.renderSlats({
+      parentEl: slatContainerEl,
       rootEl,
       dateProfile: props.dateProfile
     })
@@ -192,31 +195,32 @@ export default class TimeCols extends Component<TimeColsProps> {
       fgContainerEls,
       highlightContainerEls,
       mirrorContainerEls
-    } = this.renderContentSkeleton(contentSkeletonEl, {
+    } = this.renderContentSkeleton({
+      parentEl: contentSkeletonEl,
       colCnt: props.cells.length,
       renderProps: props.renderProps
     })
 
     let segRenderers = [
-      this.renderBusinessHours(true, {
+      this.renderBusinessHours({
         type: 'businessHours',
         containerEls: businessContainerEls,
         segs: props.businessHourSegs,
       }),
 
-      this.renderDateSelection(true, {
+      this.renderDateSelection({
         type: 'highlight',
         containerEls: highlightContainerEls,
         segs: options.selectMirror ? null : props.dateSelectionSegs // do highlight if NO mirror
       }),
 
-      this.renderBgEvents(true, {
+      this.renderBgEvents({
         type: 'bgEvent',
         containerEls: bgContainerEls,
         segs: props.bgEventSegs
       }),
 
-      this.renderFgEvents(true, {
+      this.renderFgEvents({
         containerEls: fgContainerEls,
         segs: props.fgEventSegs,
         selectedInstanceId: props.eventSelection,
@@ -240,21 +244,21 @@ export default class TimeCols extends Component<TimeColsProps> {
   handleMirror(props: TimeColsProps, mirrorContainerEls: HTMLElement[], options): TimeColsEvents | null {
 
     if (props.eventDrag) {
-      return this.renderMirrorEvents(true, {
+      return this.renderMirrorEvents({
         containerEls: mirrorContainerEls,
         segs: props.eventDrag.segs,
         mirrorInfo: { isDragging: true, sourceSeg: props.eventDrag.sourceSeg }
       })
 
     } else if (props.eventResize) {
-      return this.renderMirrorEvents(true, {
+      return this.renderMirrorEvents({
         containerEls: mirrorContainerEls,
         segs: props.eventResize.segs,
         mirrorInfo: { isDragging: true, sourceSeg: props.eventResize.sourceSeg }
       })
 
     } else if (options.selectMirror) {
-      return this.renderMirrorEvents(true, {
+      return this.renderMirrorEvents({
         containerEls: mirrorContainerEls,
         segs: props.dateSelectionSegs,
         mirrorInfo: { isSelecting: true }
@@ -294,7 +298,7 @@ export default class TimeCols extends Component<TimeColsProps> {
 
 
   _renderSlats(
-    { rootEl, dateProfile }: { rootEl: HTMLElement, dateProfile: DateProfile },
+    { rootEl, dateProfile }: { rootEl: HTMLElement, dateProfile: DateProfile } & DomLocation,
     context: ComponentContext
   ) {
     let tableEl = createElement(
@@ -362,7 +366,7 @@ export default class TimeCols extends Component<TimeColsProps> {
 
   // goes behind the slats
   _renderBgColumns(
-    { rootEl, cells, dateProfile, renderProps }: { rootEl: HTMLElement, cells: TimeColsCell[], dateProfile: DateProfile, renderProps: any },
+    { rootEl, cells, dateProfile, renderProps }: { rootEl: HTMLElement, cells: TimeColsCell[], dateProfile: DateProfile, renderProps: any } & DomLocation,
     context: ComponentContext
   ) {
     let { calendar, view, isRtl, theme, dateEnv } = context
@@ -650,7 +654,7 @@ function renderSkeleton(props: {}, context: ComponentContext) {
 
 // Renders the DOM that the view's content will live in
 // goes in front of the slats
-function renderContentSkeleton({ colCnt, renderProps }: { colCnt: number, renderProps: any  }, context: ComponentContext) {
+function renderContentSkeleton({ colCnt, renderProps }: { colCnt: number, renderProps: any  } & DomLocation, context: ComponentContext) {
   let { isRtl } = context
   let parts = []
 

+ 1 - 1
packages/timegrid/src/TimeColsEvents.ts

@@ -46,7 +46,7 @@ export default class TimeColsEvents extends FgEventRenderer<TimeColsEventsProps>
       hiddenInstances: props.hiddenInstances
     }, context)
 
-    this.segsByCol = this.attachSegs(true, {
+    this.segsByCol = this.attachSegs({
       segs,
       containerEls: props.containerEls
     })

+ 2 - 2
packages/timegrid/src/TimeColsFills.ts

@@ -13,12 +13,12 @@ export default class TimeColsFills extends FillRenderer<TimeColsFillsProps> {
 
 
   render(props: TimeColsFillsProps) {
-    let segs = this.renderSegs(true, {
+    let segs = this.renderSegs({
       type: props.type,
       segs: props.segs
     })
 
-    this.attachSegs(true, {
+    this.attachSegs({
       segs,
       containerEls: props.containerEls
     })

+ 4 - 3
packages/timegrid/src/TimeColsView.ts

@@ -45,10 +45,11 @@ export default abstract class TimeColsView extends View {
   }
 
 
-  renderLayout(props: { type: string }, context: ComponentContext) {
-    let res = this.renderSkeleton(true, { type: props.type })
+  renderLayout(props: { type: string }) {
+    let res = this.renderSkeleton({ type: props.type })
 
-    let scroller = this.renderScroller(res.contentWrapEl, {
+    let scroller = this.renderScroller({
+      parentEl: res.contentWrapEl,
       overflowX: 'hidden',
       overflowY: 'auto'
     })

+ 2 - 2
packages/timegrid/src/main.ts

@@ -2,9 +2,9 @@ import { createPlugin } from '@fullcalendar/core'
 import TimeColsView from './TimeColsView'
 import DayTimeColsView, { buildDayTableModel } from './DayTimeColsView'
 import { TimeColsSeg, TimeColsRenderProps } from './TimeCols'
-import { DayTimeColsSlicer, buildDayRanges } from './DayTimeCols'
+import { default as DayTimeCols, DayTimeColsSlicer, buildDayRanges } from './DayTimeCols'
 
-export { DayTimeColsView, TimeColsView, buildDayTableModel, buildDayRanges, DayTimeColsSlicer, TimeColsSeg, TimeColsRenderProps }
+export { DayTimeCols, DayTimeColsView, TimeColsView, buildDayTableModel, buildDayRanges, DayTimeColsSlicer, TimeColsSeg, TimeColsRenderProps }
 export { default as TimeCols } from './TimeCols'
 
 export default createPlugin({

+ 1 - 0
tsconfig.json

@@ -16,6 +16,7 @@
     "allowJs": true,
     "checkJs": true,
     "baseUrl": ".",
+    "strictBindCallApply": true,
     "paths": {
       "@fullcalendar/core": [ "packages/core/src/main" ],
       "@fullcalendar/core/*": [ "packages/core/src/*" ],