Przeglądaj źródła

date-change detection. bunch other stuff

Adam Shaw 5 lat temu
rodzic
commit
2e1c8febbb

+ 1 - 1
packages-premium

@@ -1 +1 @@
-Subproject commit cf06dd1d6dbe18ca0ec77ebd36d0db4f7dfb13eb
+Subproject commit c039ffa1942908e3873692844bc2fdd7351c70e1

+ 25 - 12
packages/core/src/CalendarApi.tsx

@@ -55,7 +55,8 @@ export class CalendarApi {
     this.dispatch({
       type: 'SET_OPTION',
       optionName: name,
-      optionValue: val
+      optionValue: val,
+      isDynamic: true
     })
   }
 
@@ -74,22 +75,33 @@ export class CalendarApi {
     let normalUpdates = {}
     let specialUpdates = {}
 
-    for (let name in updates) {
-      if (changeHandlers[name]) {
-        specialUpdates[name] = updates[name]
+    for (let optionName in updates) {
+      if (changeHandlers[optionName]) {
+        specialUpdates[optionName] = updates[optionName]
       } else {
-        normalUpdates[name] = updates[name]
+        normalUpdates[optionName] = updates[optionName]
       }
     }
 
     this.batchRendering(() => {
 
-      this.dispatch({
-        type: 'MUTATE_OPTIONS',
-        updates: normalUpdates,
-        removals,
-        isDynamic
-      })
+      for (let optionName in updates) {
+        this.dispatch({
+          type: 'SET_OPTION',
+          optionName,
+          optionValue: updates[optionName],
+          isDynamic
+        })
+      }
+
+      for (let optionName of removals) {
+        this.dispatch({
+          type: 'SET_OPTION',
+          optionName,
+          optionValue: null,
+          isDynamic
+        })
+      }
 
       // special updates
       for (let name in specialUpdates) {
@@ -136,7 +148,8 @@ export class CalendarApi {
           this.dispatch({ // not very efficient to do two dispatches
             type: 'SET_OPTION',
             optionName: 'visibleRange',
-            optionValue: dateOrRange
+            optionValue: dateOrRange,
+            isDynamic: true
           })
 
         } else {

+ 4 - 3
packages/core/src/CalendarComponent.tsx

@@ -336,9 +336,10 @@ function buildToolbarProps(
   now: DateMarker,
   title: string
 ) {
-  let todayInfo = dateProfileGenerator.build(now)
-  let prevInfo = dateProfileGenerator.buildPrev(dateProfile, currentDate)
-  let nextInfo = dateProfileGenerator.buildNext(dateProfile, currentDate)
+  // don't force any date-profiles to valid date profiles (the `false`) so that we can tell if it's invalid
+  let todayInfo = dateProfileGenerator.build(now, undefined, false) // TODO: need `undefined` or else INFINITE LOOP for some reason
+  let prevInfo = dateProfileGenerator.buildPrev(dateProfile, currentDate, false)
+  let nextInfo = dateProfileGenerator.buildNext(dateProfile, currentDate, false)
 
   return {
     title,

+ 75 - 61
packages/core/src/DateProfileGenerator.ts

@@ -1,8 +1,8 @@
 import { DateMarker, startOfDay, addDays } from './datelib/marker'
-import { Duration, createDuration, getWeeksFromInput, asRoughDays, asRoughMs, greatestDurationDenominator, durationsEqual } from './datelib/duration'
-import { DateRange, OpenDateRange, constrainMarkerToRange, intersectRanges, rangesIntersect, parseRange, rangesEqual } from './datelib/date-range'
+import { Duration, createDuration, getWeeksFromInput, asRoughDays, asRoughMs, greatestDurationDenominator, DurationInput } from './datelib/duration'
+import { DateRange, OpenDateRange, constrainMarkerToRange, intersectRanges, rangesIntersect, parseRange } from './datelib/date-range'
 import { ViewSpec } from './structs/view-spec'
-import { DateEnv } from './datelib/env'
+import { DateEnv, DateInput } from './datelib/env'
 import { computeVisibleDayRange } from './util/date'
 import { _getNow } from './reducers/current-date'
 
@@ -20,8 +20,45 @@ export interface DateProfile {
   dateIncrement: Duration
 }
 
+export interface DateProfileGeneratorProps extends DateProfileOptions {
+  viewSpec: ViewSpec
+  dateEnv: DateEnv
+}
+
+export interface DateProfileOptions {
+  slotMinTime: DurationInput
+  slotMaxTime: DurationInput
+  showNonCurrentDates?: boolean
+  dayCount?: number
+  dateAlignment?: string
+  dateIncrement?: DurationInput
+  hiddenDays?: number[]
+  weekends?: boolean
+  now?: DateInput // for _getNow
+  validRange?: OpenDateRange // for getRangeOption
+  visibleRange?: OpenDateRange // for getRangeOption
+  monthMode?: boolean
+  fixedWeekCount?: number
+}
+
+export const DATE_PROFILE_OPTIONS: { [T in keyof DateProfileOptions]-?: boolean } = {
+  slotMinTime: true,
+  slotMaxTime: true,
+  showNonCurrentDates: true,
+  dayCount: true,
+  dateAlignment: true,
+  dateIncrement: true,
+  hiddenDays: true,
+  weekends: true,
+  now: true,
+  validRange: true,
+  visibleRange: true,
+  monthMode: true,
+  fixedWeekCount: true
+}
 
-export class DateProfileGenerator {
+
+export class DateProfileGenerator { // only publicly used for isHiddenDay :(
 
   slotMinTime: Duration
   slotMaxTime: Duration
@@ -29,14 +66,10 @@ export class DateProfileGenerator {
   isHiddenDayHash: boolean[]
 
 
-  constructor(
-    protected viewSpec: ViewSpec,
-    protected options: any,
-    protected dateEnv: DateEnv
-  ) {
-    this.slotMinTime = createDuration(options.slotMinTime)
-    this.slotMaxTime = createDuration(options.slotMaxTime)
-    this.nowDate = _getNow(options, dateEnv)
+  constructor(protected props: DateProfileGeneratorProps) {
+    this.slotMinTime = createDuration(props.slotMinTime)
+    this.slotMaxTime = createDuration(props.slotMaxTime)
+    this.nowDate = _getNow(props, props.dateEnv) // uses props.now. bad system
     this.initHiddenDays()
   }
 
@@ -46,35 +79,35 @@ export class DateProfileGenerator {
 
 
   // Builds a structure with info about what the dates/ranges will be for the "prev" view.
-  buildPrev(currentDateProfile: DateProfile, currentDate: DateMarker): DateProfile {
-    let { dateEnv } = this
+  buildPrev(currentDateProfile: DateProfile, currentDate: DateMarker, forceToValid?: boolean): DateProfile {
+    let { dateEnv } = this.props
 
     let prevDate = dateEnv.subtract(
       dateEnv.startOf(currentDate, currentDateProfile.currentRangeUnit), // important for start-of-month
       currentDateProfile.dateIncrement
     )
 
-    return this.build(prevDate, -1)
+    return this.build(prevDate, -1, forceToValid)
   }
 
 
   // Builds a structure with info about what the dates/ranges will be for the "next" view.
-  buildNext(currentDateProfile: DateProfile, currentDate: DateMarker): DateProfile {
-    let { dateEnv } = this
+  buildNext(currentDateProfile: DateProfile, currentDate: DateMarker, forceToValid?: boolean): DateProfile {
+    let { dateEnv } = this.props
 
     let nextDate = dateEnv.add(
       dateEnv.startOf(currentDate, currentDateProfile.currentRangeUnit), // important for start-of-month
       currentDateProfile.dateIncrement
     )
 
-    return this.build(nextDate, 1)
+    return this.build(nextDate, 1, forceToValid)
   }
 
 
   // Builds a structure holding dates/ranges for rendering around the given date.
   // Optional direction param indicates whether the date is being incremented/decremented
   // from its previous value. decremented = -1, incremented = 1 (default).
-  build(currentDate: DateMarker, direction?, forceToValid = false): DateProfile {
+  build(currentDate: DateMarker, direction?, forceToValid = true): DateProfile {
     let validRange: DateRange
     let currentInfo
     let isRangeAllDay
@@ -99,7 +132,7 @@ export class DateProfileGenerator {
     renderRange = this.trimHiddenDays(renderRange)
     activeRange = renderRange
 
-    if (!this.options.showNonCurrentDates) {
+    if (!this.props.showNonCurrentDates) {
       activeRange = intersectRanges(activeRange, currentInfo.range)
     }
 
@@ -161,7 +194,7 @@ export class DateProfileGenerator {
   // See build() for a description of `direction`.
   // Guaranteed to have `range` and `unit` properties. `duration` is optional.
   buildCurrentRangeInfo(date: DateMarker, direction) {
-    let { viewSpec, dateEnv } = this
+    let { viewSpec, dateEnv } = this.props
     let duration = null
     let unit = null
     let range = null
@@ -171,7 +204,7 @@ export class DateProfileGenerator {
       duration = viewSpec.duration
       unit = viewSpec.durationUnit
       range = this.buildRangeFromDuration(date, direction, duration, unit)
-    } else if ((dayCount = this.options.dayCount)) {
+    } else if ((dayCount = this.props.dayCount)) {
       unit = 'day'
       range = this.buildRangeFromDayCount(date, direction, dayCount)
     } else if ((range = this.buildCustomVisibleRange(date))) {
@@ -194,11 +227,12 @@ export class DateProfileGenerator {
   // Returns a new activeRange to have time values (un-ambiguate)
   // slotMinTime or slotMaxTime causes the range to expand.
   adjustActiveRange(range: DateRange) {
-    let { dateEnv, slotMinTime, slotMaxTime } = this
+    let { dateEnv, viewSpec } = this.props
+    let { slotMinTime, slotMaxTime } = this
     let start = range.start
     let end = range.end
 
-    if (this.viewSpec.optionDefaults.usesMinMaxTime) {
+    if (viewSpec.optionDefaults.usesMinMaxTime) {
 
       // expand active range if slotMinTime is negative (why not when positive?)
       if (asRoughDays(slotMinTime) < 0) {
@@ -221,8 +255,7 @@ export class DateProfileGenerator {
   // Builds the "current" range when it is specified as an explicit duration.
   // `unit` is the already-computed greatestDurationDenominator unit of duration.
   buildRangeFromDuration(date: DateMarker, direction, duration: Duration, unit) {
-    let { dateEnv } = this
-    let alignment = this.options.dateAlignment
+    let { dateEnv, dateAlignment } = this.props
     let dateIncrementInput
     let dateIncrementDuration
     let start: DateMarker
@@ -230,23 +263,23 @@ export class DateProfileGenerator {
     let res
 
     // compute what the alignment should be
-    if (!alignment) {
-      dateIncrementInput = this.options.dateIncrement
+    if (!dateAlignment) {
+      dateIncrementInput = this.props.dateIncrement
 
       if (dateIncrementInput) {
         dateIncrementDuration = createDuration(dateIncrementInput)
 
         // use the smaller of the two units
         if (asRoughMs(dateIncrementDuration) < asRoughMs(duration)) {
-          alignment = greatestDurationDenominator(
+          dateAlignment = greatestDurationDenominator(
             dateIncrementDuration,
             !getWeeksFromInput(dateIncrementInput)
           ).unit
         } else {
-          alignment = unit
+          dateAlignment = unit
         }
       } else {
-        alignment = unit
+        dateAlignment = unit
       }
     }
 
@@ -259,7 +292,7 @@ export class DateProfileGenerator {
     }
 
     function computeRes() {
-      start = dateEnv.startOf(date, alignment)
+      start = dateEnv.startOf(date, dateAlignment)
       end = dateEnv.add(start, duration)
       res = { start, end }
     }
@@ -278,14 +311,13 @@ export class DateProfileGenerator {
 
   // Builds the "current" range when a dayCount is specified.
   buildRangeFromDayCount(date: DateMarker, direction, dayCount) {
-    let { dateEnv } = this
-    let customAlignment = this.options.dateAlignment
+    let { dateEnv, dateAlignment } = this.props
     let runningCount = 0
     let start: DateMarker = date
     let end: DateMarker
 
-    if (customAlignment) {
-      start = dateEnv.startOf(start, customAlignment)
+    if (dateAlignment) {
+      start = dateEnv.startOf(start, dateAlignment)
     }
 
     start = startOfDay(start)
@@ -306,7 +338,7 @@ export class DateProfileGenerator {
   // Builds a normalized range object for the "visible" range,
   // which is a way to define the currentRange and activeRange at the same time.
   buildCustomVisibleRange(date: DateMarker) {
-    let { dateEnv } = this
+    let { dateEnv } = this.props
     let visibleRange = this.getRangeOption('visibleRange', dateEnv.toDate(date))
 
     if (visibleRange && (visibleRange.start == null || visibleRange.end == null)) {
@@ -328,12 +360,12 @@ export class DateProfileGenerator {
   // Compute the duration value that should be added/substracted to the current date
   // when a prev/next operation happens.
   buildDateIncrement(fallback): Duration {
-    let dateIncrementInput = this.options.dateIncrement
+    let dateIncrementInput = this.props.dateIncrement
     let customAlignment
 
     if (dateIncrementInput) {
       return createDuration(dateIncrementInput)
-    } else if ((customAlignment = this.options.dateAlignment)) {
+    } else if ((customAlignment = this.props.dateAlignment)) {
       return createDuration(1, customAlignment)
     } else if (fallback) {
       return fallback
@@ -347,14 +379,14 @@ export class DateProfileGenerator {
   // WARNING: passed-in arguments will be given to generator functions as-is and can cause side-effects.
   // Always clone your objects if you fear mutation.
   getRangeOption(name, ...otherArgs): OpenDateRange {
-    let val = this.options[name]
+    let val = this.props[name]
 
     if (typeof val === 'function') {
       val = val.apply(null, otherArgs)
     }
 
     if (val) {
-      val = parseRange(val, this.dateEnv)
+      val = parseRange(val, this.props.dateEnv)
     }
 
     if (val) {
@@ -371,12 +403,12 @@ export class DateProfileGenerator {
 
   // Initializes internal variables related to calculating hidden days-of-week
   initHiddenDays() {
-    let hiddenDays = this.options.hiddenDays || [] // array of day-of-week indices that are hidden
+    let hiddenDays = this.props.hiddenDays || [] // array of day-of-week indices that are hidden
     let isHiddenDayHash = [] // is the day-of-week hidden? (hash with day-of-week-index -> bool)
     let dayCnt = 0
     let i
 
-    if (this.options.weekends === false) {
+    if (this.props.weekends === false) {
       hiddenDays.push(0, 6) // 0=sunday, 6=saturday
     }
 
@@ -443,21 +475,3 @@ export class DateProfileGenerator {
   }
 
 }
-
-
-// TODO: find a way to avoid comparing DateProfiles. it's tedious
-export function isDateProfilesEqual(p0: DateProfile, p1: DateProfile) {
-  return rangesEqual(p0.validRange, p1.validRange) &&
-    rangesEqual(p0.activeRange, p1.activeRange) &&
-    rangesEqual(p0.renderRange, p1.renderRange) &&
-    durationsEqual(p0.slotMinTime, p1.slotMinTime) &&
-    durationsEqual(p0.slotMaxTime, p1.slotMaxTime)
-  /*
-  TODO: compare more?
-    currentRange: DateRange
-    currentRangeUnit: string
-    isRangeAllDay: boolean
-    isValid: boolean
-    dateIncrement: Duration
-  */
-}

+ 6 - 2
packages/core/src/plugin-system.ts

@@ -40,7 +40,7 @@ export function createPlugin(input: PluginDefInput): PluginDef {
 }
 
 
-export function buildPluginHooks(pluginDefs: PluginDef[]): PluginHooks {
+export function buildPluginHooks(pluginDefs: PluginDef[] | null, globalDefs: PluginDef[]): PluginHooks {
   let isAdded: { [pluginId: string]: boolean } = {}
   let hooks: PluginHooks = {
     reducers: [],
@@ -82,7 +82,11 @@ export function buildPluginHooks(pluginDefs: PluginDef[]): PluginHooks {
     }
   }
 
-  addDefs(pluginDefs)
+  if (pluginDefs) {
+    addDefs(pluginDefs)
+  }
+
+  addDefs(globalDefs)
 
   return hooks
 }

+ 29 - 30
packages/core/src/reducers/CalendarStateReducer.ts

@@ -7,9 +7,9 @@ import { DateEnv } from '../datelib/env'
 import { CalendarApi } from '../CalendarApi'
 import { StandardTheme } from '../theme/StandardTheme'
 import { EventSourceHash } from '../structs/event-source'
-import { buildViewSpecs, ViewSpec } from '../structs/view-spec'
+import { buildViewSpecs } from '../structs/view-spec'
 import { mapHash, isPropsEqual } from '../util/object'
-import { DateProfileGenerator } from '../DateProfileGenerator'
+import { DateProfileGenerator, DateProfileGeneratorProps } from '../DateProfileGenerator'
 import { reduceViewType } from './view-type'
 import { reduceCurrentDate, getInitialDate } from './current-date'
 import { reduceDateProfile } from './date-profile'
@@ -41,7 +41,7 @@ export class CalendarStateReducer {
   private buildDateEnv = memoize(buildDateEnv)
   private buildTheme = memoize(buildTheme)
   private buildViewSpecs = memoize(buildViewSpecs)
-  private buildDateProfileGenerator = memoize(buildDateProfileGenerator)
+  private buildDateProfileGenerator = memoizeObjArg(buildDateProfileGenerator)
   private buildComputedOptions = memoize(buildComputedOptions)
   private buildViewUiProps = memoizeObjArg(buildViewUiProps)
   private buildEventUiBySource = memoize(buildEventUiBySource, isPropsEqual)
@@ -123,20 +123,10 @@ export class CalendarStateReducer {
         break
 
       case 'SET_OPTION':
-        dynamicOptionOverrides = { ...dynamicOptionOverrides, [action.optionName]: action.optionValue }
-        break
-
-      case 'MUTATE_OPTIONS':
-        let { updates, removals, isDynamic } = action
-
-        if (Object.keys(updates).length || removals.length) {
-          let hash = isDynamic
-            ? (dynamicOptionOverrides = { ...dynamicOptionOverrides, ...updates })
-            : (optionOverrides = { ...optionOverrides, ...updates })
-
-          for (let removal of removals) {
-            delete hash[removal]
-          }
+        if (action.isDynamic) {
+          dynamicOptionOverrides = { ...dynamicOptionOverrides, [action.optionName]: action.optionValue }
+        } else {
+          optionOverrides = { ...optionOverrides, [action.optionName]: action.optionValue }
         }
         break
     }
@@ -163,9 +153,7 @@ export class CalendarStateReducer {
       dynamicOptionOverrides
     )
 
-    let pluginHooks = this.buildPluginHooks(
-      globalPlugins.concat(calendarOptions.plugins || [])
-    )
+    let pluginHooks = this.buildPluginHooks(calendarOptions.plugins, globalPlugins)
 
     let prevDateEnv = state ? state.dateEnv : null
     let dateEnv = this.buildDateEnv(
@@ -212,14 +200,25 @@ export class CalendarStateReducer {
 
     let currentDate = state.currentDate || getInitialDate(reducerContext) // weird how we do INIT
 
-    let prevDateProfileGenerator = state.dateProfileGenerator
-    let dateProfileGenerator = this.buildDateProfileGenerator(viewSpec, viewOptions, dateEnv)
-    let dateProfile = state.dateProfile
-
-    if (prevDateProfileGenerator !== dateProfileGenerator) { // weird. happens for INIT as well
-      dateProfile = dateProfileGenerator.build(currentDate, undefined, true) // forceToValid=true
-    }
+    let dateProfileGenerator = this.buildDateProfileGenerator({ // TODO: pluck based on DATE_PROFILE_OPTIONS?
+      viewSpec,
+      dateEnv,
+      slotMinTime: viewOptions.slotMinTime,
+      slotMaxTime: viewOptions.slotMaxTime,
+      showNonCurrentDates: viewOptions.showNonCurrentDates,
+      dayCount: viewOptions.dayCount,
+      dateAlignment: viewOptions.dateAlignment,
+      dateIncrement: viewOptions.dateIncrement,
+      hiddenDays: viewOptions.hiddenDays,
+      weekends: viewOptions.weekends,
+      now: viewOptions.now,
+      validRange: viewOptions.validRange,
+      visibleRange: viewOptions.visibleRange,
+      monthMode: viewOptions.monthMode,
+      fixedWeekCount: viewOptions.fixedWeekCount
+    })
 
+    let dateProfile = state.dateProfile
     dateProfile = reduceDateProfile(dateProfile, action, currentDate, dateProfileGenerator)
     currentDate = reduceCurrentDate(currentDate, action, dateProfile)
 
@@ -332,10 +331,10 @@ function buildTheme(rawOptions, pluginHooks: PluginHooks) {
 }
 
 
-function buildDateProfileGenerator(viewSpec: ViewSpec, viewOptions: any, dateEnv: DateEnv): DateProfileGenerator {
-  let DateProfileGeneratorClass = viewSpec.optionDefaults.dateProfileGeneratorClass || DateProfileGenerator
+function buildDateProfileGenerator(props: DateProfileGeneratorProps): DateProfileGenerator {
+  let DateProfileGeneratorClass = props.viewSpec.optionDefaults.dateProfileGeneratorClass || DateProfileGenerator
 
-  return new DateProfileGeneratorClass(viewSpec, viewOptions, dateEnv)
+  return new DateProfileGeneratorClass(props)
 }
 
 

+ 23 - 22
packages/core/src/reducers/date-profile.ts

@@ -1,45 +1,46 @@
-import { DateProfile, DateProfileGenerator, isDateProfilesEqual } from '../DateProfileGenerator'
+import { DateProfile, DateProfileGenerator, DATE_PROFILE_OPTIONS } from '../DateProfileGenerator'
 import { Action } from './types'
 import { DateMarker } from '../datelib/marker'
 import { rangeContainsMarker } from '../datelib/date-range'
 
 
 export function reduceDateProfile(currentDateProfile: DateProfile | null, action: Action, currentDate: DateMarker, dateProfileGenerator: DateProfileGenerator): DateProfile {
-  let newDateProfile: DateProfile
+  switch (action.type) {
+    case 'INIT':
+      return dateProfileGenerator.build(currentDate)
 
-  // the "INIT" will happen in CalendarStateReducer
+    case 'CHANGE_VIEW_TYPE':
+      return dateProfileGenerator.build(action.dateMarker || currentDate)
 
-  switch (action.type) {
     case 'CHANGE_DATE':
-    case 'CHANGE_VIEW_TYPE':
       if (
         !currentDateProfile.activeRange ||
-        !rangeContainsMarker(currentDateProfile.currentRange, (action as any).dateMarker)
+        !rangeContainsMarker(currentDateProfile.currentRange, action.dateMarker) // don't move if date already in view
       ) {
-        newDateProfile = dateProfileGenerator.build(
-          action.dateMarker || currentDate,
-          undefined,
-          true // forceToValid
-        )
+        return dateProfileGenerator.build(action.dateMarker)
       }
       break
 
     case 'PREV':
-      newDateProfile = dateProfileGenerator.buildPrev(currentDateProfile, currentDate)
+      let dp0 = dateProfileGenerator.buildPrev(currentDateProfile, currentDate)
+      if (dp0.isValid) {
+        return dp0
+      }
       break
 
     case 'NEXT':
-      newDateProfile = dateProfileGenerator.buildNext(currentDateProfile, currentDate)
+      let dp1 = dateProfileGenerator.buildNext(currentDateProfile, currentDate)
+      if (dp1.isValid) {
+        return dp1
+      }
       break
-  }
 
-  if (
-    newDateProfile &&
-    newDateProfile.isValid &&
-    !(currentDateProfile && isDateProfilesEqual(currentDateProfile, newDateProfile))
-  ) {
-    return newDateProfile
-  } else {
-    return currentDateProfile
+    case 'SET_OPTION':
+      if (DATE_PROFILE_OPTIONS[action.optionName]) {
+        return dateProfileGenerator.build(currentDate) // dateProfileGenerator will be newly-created
+      }
+      break
   }
+
+  return currentDateProfile
 }

+ 1 - 2
packages/core/src/reducers/types.ts

@@ -49,8 +49,7 @@ export type Action =
 
   { type: 'INIT', optionOverrides: object } | // wont it create another rerender?
 
-  { type: 'SET_OPTION', optionName: string, optionValue: any } | // TODO: more strictly type
-  { type: 'MUTATE_OPTIONS', updates: object, removals: string[], isDynamic: boolean } |
+  { type: 'SET_OPTION', optionName: string, optionValue: any, isDynamic: boolean } | // TODO: more strictly type
 
   { type: 'PREV' } |
   { type: 'NEXT' } |

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

@@ -9,7 +9,7 @@ export class TableDateProfileGenerator extends DateProfileGenerator {
 
   // Computes the date range that will be rendered.
   buildRenderRange(currentRange, currentRangeUnit, isRangeAllDay): DateRange {
-    let { dateEnv } = this
+    let { dateEnv } = this.props
     let renderRange = super.buildRenderRange(currentRange, currentRangeUnit, isRangeAllDay)
     let start = renderRange.start
     let end = renderRange.end
@@ -28,8 +28,8 @@ export class TableDateProfileGenerator extends DateProfileGenerator {
 
     // ensure 6 weeks
     if (
-      this.options.monthMode &&
-      this.options.fixedWeekCount
+      this.props.monthMode &&
+      this.props.fixedWeekCount
     ) {
       let rowCnt = Math.ceil( // could be partial weeks due to hiddenDays
         diffWeeks(start, end)

+ 2 - 3
packages/timegrid/src/TimeCols.tsx

@@ -112,9 +112,8 @@ export class TimeCols extends BaseComponent<TimeColsProps, TimeColsState> {
   }
 
 
-  componentDidUpdate(prevProps: TimeColsProps, prevState: TimeColsState) {
-    let didContextUpdate = prevProps === this.props && prevState === this.state // only way to detect context change. if props/start didnt
-    this.scrollResponder.update(didContextUpdate) // if context changed, dateProfile probably changed
+  componentDidUpdate(prevProps: TimeColsProps) {
+    this.scrollResponder.update(prevProps.dateProfile !== this.props.dateProfile)
   }