浏览代码

row expanding. fix lots of bugs

Adam Shaw 6 年之前
父节点
当前提交
00f2ef68f8

+ 1 - 1
packages-premium

@@ -1 +1 @@
-Subproject commit b483620b230c22efee01b6a92308bfd243ad05be
+Subproject commit 1771a4058dbfb506d4882968e15594b6ee8800d5

+ 1 - 1
packages/core/src/Calendar.tsx

@@ -823,7 +823,7 @@ export default class Calendar {
 
 
   resizeRunner = new DelayedRunner(() => {
-    this.triggerResizeHandlers(false)
+    this.triggerResizeHandlers(true) // should window resizes be considered "forced" ?
     this.publiclyTrigger('windowResize', [ this.context.view ])
   })
 

+ 9 - 2
packages/core/src/CalendarComponent.tsx

@@ -68,7 +68,7 @@ export default class CalendarComponent extends BaseComponent<CalendarComponentPr
 
     let calendarHeight: string | number = ''
     let viewHeight: string | number = ''
-    let viewAspectRatio: number | undefined = options.aspectRatio
+    let viewAspectRatio: number | undefined
 
     if (isHeightAuto(options)) {
       viewHeight = 'auto'
@@ -76,6 +76,8 @@ export default class CalendarComponent extends BaseComponent<CalendarComponentPr
       calendarHeight = options.height
     } else if (options.contentHeight != null) {
       viewHeight = options.contentHeight
+    } else {
+      viewAspectRatio = options.aspectRatio
     }
 
     // TODO: move this somewhere after real render!
@@ -93,7 +95,12 @@ export default class CalendarComponent extends BaseComponent<CalendarComponentPr
             { ...toolbarProps }
           />
         }
-        <ViewContainer height={viewHeight} aspectRatio={viewAspectRatio} elRef={this.setViewContainerEl} onClick={this.handleNavLinkClick}>
+        <ViewContainer
+          height={viewHeight}
+          aspectRatio={viewAspectRatio}
+          elRef={this.setViewContainerEl}
+          onClick={this.handleNavLinkClick}
+        >
           {this.renderView(props, this.context)}
         </ViewContainer>
         {footer &&

+ 3 - 3
packages/core/src/ViewContainer.tsx

@@ -20,11 +20,11 @@ export default class ViewContainer extends BaseComponent<ViewContainerProps> {
     let height: CssDimValue = ''
     let paddingBottom: CssDimValue = ''
 
-    if (props.height) { // TODO: better test
-      height = props.height
-    } else {
+    if (props.aspectRatio) { // TODO: better test
       classNames.push('fc-view-container--aspectratio')
       paddingBottom = (1 / props.aspectRatio) * 100 + '%'
+    } else {
+      height = props.height
     }
 
     return (

+ 21 - 0
packages/core/src/common/PositionCache.ts

@@ -45,6 +45,27 @@ export default class PositionCache {
   }
 
 
+  // HACK for when DOM isn't ready yet but we coords for intermediate rendering
+  buildZeros() {
+    let len = this.els.length
+    let zeros = []
+
+    for (let i = 0; i < len; i++) {
+      zeros.push(0)
+    }
+
+    if (this.isHorizontal) {
+      this.lefts = zeros
+      this.rights = zeros
+    }
+
+    if (this.isVertical) {
+      this.tops = zeros
+      this.bottoms = zeros
+    }
+  }
+
+
   // Populates the left/right internal coordinate arrays
   buildElHorizontals(originClientLeft: number) {
     let lefts = []

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

@@ -178,7 +178,9 @@ export { default as SimpleScrollGrid, SimpleScrollGridSection } from './scrollgr
 export {
   CssDimValue, ScrollerLike, SectionConfig, ColProps, ChunkConfig, hasShrinkWidth, renderMicroColGroup,
   getScrollGridClassNames, getSectionClassNames, getChunkVGrow, getNeedsYScrolling, renderChunkContent, computeForceScrollbars, computeShrinkWidth,
-  getChunkClassNames, ChunkContentCallbackArgs
+  getChunkClassNames, ChunkContentCallbackArgs,
+  computeScrollerClientWidths, computeScrollerClientHeights,
+  sanitizeShrinkWidth
 } from './scrollgrid/util'
 export { default as Scroller, ScrollerProps, OverflowValue } from './scrollgrid/Scroller'
 export { getScrollbarWidths } from './util/scrollbar-width'

+ 2 - 1
packages/core/src/options.ts

@@ -80,8 +80,9 @@ export const globalDefaults = {
   windowResizeDelay: 100, // milliseconds before an updateSize happens
 
   longPressDelay: 1000,
-  eventDragMinDistance: 5 // only applies to mouse
+  eventDragMinDistance: 5, // only applies to mouse
 
+  expandRows: false
 }
 
 

+ 1 - 44
packages/core/src/scrollgrid/Scroller.tsx

@@ -16,7 +16,6 @@ export interface ScrollerProps {
   elRef?: Ref<HTMLElement>
 }
 
-
 export default class Scroller extends BaseComponent<ScrollerProps> implements ScrollerLike {
 
   private el: HTMLElement // TODO: just use this.base?
@@ -39,9 +38,7 @@ export default class Scroller extends BaseComponent<ScrollerProps> implements Sc
         maxHeight: props.maxHeight || '',
         overflowX: props.overflowX,
         overflowY: props.overflowY
-      }}>
-        {props.children}
-      </div>
+      }}>{props.children}</div>
     )
   }
 
@@ -52,46 +49,6 @@ export default class Scroller extends BaseComponent<ScrollerProps> implements Sc
   }
 
 
-  componentDidMount() {
-    this.handleSizing()
-    this.context.addResizeHandler(this.handleSizing)
-  }
-
-
-  componentDidUpdate(prevProps: ScrollerProps) {
-    this.handleSizing(prevProps.overflowY !== this.props.overflowY)
-  }
-
-
-  componentWillUnmount() {
-    this.context.removeResizeHandler(this.handleSizing)
-  }
-
-
-  handleSizing = (overflowYChanged?: boolean) => { // SIZING HACKS
-    let rootEl = this.el
-    let childNodes = rootEl.childNodes
-
-    if (childNodes.length === 1) {
-      let tableEl = childNodes[0] as HTMLElement
-      if (tableEl.nodeName === 'TABLE') {
-
-        // for IE tables vertically expanding really jankily, esp DURING scrolling
-        // do first. might affect y-scrollbars which affect clientWidth
-        if (tableEl.classList.contains('vgrow')) {
-          tableEl.style.height = (rootEl.clientHeight - 1) + 'px' // -1 for IE
-        }
-
-        // for Safari bug, change in y-scrollbars wouldn't change table width (with 100% width)
-        // won't do anything if the <col>s already have an opinion about width
-        if (overflowYChanged) {
-          tableEl.style.width = rootEl.clientWidth + 'px'
-        }
-      }
-    }
-  }
-
-
   needsXScrolling() {
     return this.el.scrollWidth > this.el.clientWidth + 1 || // IE shittiness
       this.props.overflowX === 'auto' && Boolean(this.getXScrollbarWidth()) // hack safeguard

+ 69 - 27
packages/core/src/scrollgrid/SimpleScrollGrid.tsx

@@ -1,10 +1,11 @@
 import { VNode, h } from '../vdom'
 import ComponentContext from '../component/ComponentContext'
-import { BaseComponent, setRef, componentNeedsResize } from '../vdom-util'
+import { BaseComponent, setRef } from '../vdom-util'
 import Scroller, { OverflowValue } from './Scroller'
 import RefMap from '../util/RefMap'
 import { ColProps, SectionConfig, renderMicroColGroup, computeShrinkWidth, getScrollGridClassNames, getSectionClassNames, getNeedsYScrolling,
-  renderChunkContent, getChunkVGrow, computeForceScrollbars, ChunkConfig, hasShrinkWidth, CssDimValue, getChunkClassNames } from './util'
+  renderChunkContent, getChunkVGrow, computeForceScrollbars, ChunkConfig, hasShrinkWidth, CssDimValue, getChunkClassNames, computeScrollerClientWidths, computeScrollerClientHeights,
+  } from './util'
 import { memoize } from '../util/memoize'
 
 
@@ -21,24 +22,37 @@ export interface SimpleScrollGridSection extends SectionConfig {
 }
 
 interface SimpleScrollGridState {
-  shrinkWidth?: number
-  forceYScrollbars: boolean
+  isSizingReady: boolean
+  shrinkWidth: null | number
+  forceYScrollbars: null | boolean
+  scrollerClientWidths: null | { [index: string]: number }
+  scrollerClientHeights: null | { [index: string]: number }
 }
 
-const STATE_IS_SIZING = {
-  shrinkWidth: true,
-  forceYScrollbars: true
+const INITIAL_SIZING_STATE: SimpleScrollGridState = {
+  isSizingReady: false,
+  shrinkWidth: null,
+  forceYScrollbars: null,
+  scrollerClientWidths: null,
+  scrollerClientHeights: null
 }
 
 
-export default class SimpleScrollGrid extends BaseComponent<SimpleScrollGridProps> {
+export default class SimpleScrollGrid extends BaseComponent<SimpleScrollGridProps, SimpleScrollGridState> {
 
   renderMicroColGroup = memoize(renderMicroColGroup) // yucky to memoize VNodes, but much more efficient for consumers
   scrollerRefs = new RefMap<Scroller>()
   scrollerElRefs = new RefMap<HTMLElement, [ChunkConfig]>(this._handleScrollerEl.bind(this))
 
-  state = {
-    forceYScrollbars: false
+  state = INITIAL_SIZING_STATE
+
+
+  static getDerivedStateFromProps(props, state: SimpleScrollGridState) {
+    if (state.isSizingReady) { // from a prop change
+      return INITIAL_SIZING_STATE
+    } else if (state.scrollerClientWidths) { // the last sizing-state was just set
+      return { isSizingReady: true }
+    }
   }
 
 
@@ -82,14 +96,24 @@ export default class SimpleScrollGrid extends BaseComponent<SimpleScrollGridProp
       return chunkConfig.outerContent
     }
 
+    let { state } = this
+    let { isSizingReady } = state
+
     let needsYScrolling = getNeedsYScrolling(this.props, sectionConfig, chunkConfig) // TODO: do lazily
-    let overflowY: OverflowValue = this.state.forceYScrollbars ? 'scroll' : (needsYScrolling ? 'auto' : 'hidden')
-    let content = renderChunkContent(sectionConfig, chunkConfig, microColGroupNode, '', true)
+    let overflowY: OverflowValue = state.forceYScrollbars ? 'scroll' : (needsYScrolling ? 'auto' : 'hidden')
     let vGrow = getChunkVGrow(this.props, sectionConfig, chunkConfig)
 
+    let content = renderChunkContent(sectionConfig, chunkConfig, {
+      tableColGroupNode: microColGroupNode,
+      tableMinWidth: '',
+      tableWidth: isSizingReady ? state.scrollerClientWidths[sectionI] : '',
+      tableHeight: isSizingReady ? state.scrollerClientHeights[sectionI] : '', // TODO: subtract 1 for IE?????
+      isSizingReady
+    })
+
     // TODO: cleaner solution
     // in browsers other than Chrome, the height of the inner table was taking precedence over scroller's liquid height,
-    // making it so there's never be scrollbars
+    // making it so there's never be scrollbars (thus the position:relative div)
     if (vGrow) {
       return (
         <td class={getChunkClassNames(sectionConfig, chunkConfig, this.context)} ref={chunkConfig.elRef}>
@@ -132,37 +156,55 @@ export default class SimpleScrollGrid extends BaseComponent<SimpleScrollGridProp
 
 
   componentDidMount() {
-    this.handleSizing()
-    this.context.addResizeHandler(this.handleSizing)
+    if (!this.props.forPrint) {
+      this.adjustSizing()
+    }
+
+    this.context.addResizeHandler(this.handleResize)
   }
 
 
   componentDidUpdate(prevProps: SimpleScrollGridProps, prevState: SimpleScrollGridState) {
-    if (componentNeedsResize(prevProps, this.props, prevState, this.state, STATE_IS_SIZING)) {
-      this.handleSizing()
+    if (!this.props.forPrint) { // repeat code
+      this.adjustSizing()
     }
   }
 
 
   componentWillUnmount() {
-    this.context.removeResizeHandler(this.handleSizing)
+    this.context.removeResizeHandler(this.handleResize)
   }
 
 
-  handleSizing = () => {
+  adjustSizing() {
+    let { state } = this
+
+    if (state.shrinkWidth == null) {
+      this.setState({
+        shrinkWidth:
+          hasShrinkWidth(this.props.cols) ? // TODO: do this optimization for ScrollGrid
+            computeShrinkWidth(this.scrollerElRefs.getAll())
+            : 0
+      })
+
+    } else if (state.forceYScrollbars == null) {
+      this.setState({
+        forceYScrollbars: computeForceScrollbars(this.scrollerRefs.getAll(), 'Y')
+      })
 
-    if (
-      !this.props.forPrint && // temporary? do in ScrollGrid?
-      hasShrinkWidth(this.props.cols)
-    ) {
+    } else if (!state.scrollerClientWidths) {
       this.setState({
-        shrinkWidth: computeShrinkWidth(this.scrollerElRefs.getAll())
+        scrollerClientWidths: computeScrollerClientWidths(this.scrollerElRefs),
+        scrollerClientHeights: computeScrollerClientHeights(this.scrollerElRefs)
       })
     }
+  }
 
-    this.setState({
-      forceYScrollbars: computeForceScrollbars(this.scrollerRefs.getAll(), 'Y')
-    })
+
+  handleResize = () => {
+    if (!this.props.forPrint) {
+      this.forceUpdate() // getDerivedStateFromProps will clear the sizing state
+    }
   }
 
 

+ 43 - 23
packages/core/src/scrollgrid/util.tsx

@@ -2,6 +2,8 @@ import { VNode, h, Ref } from '../vdom'
 import { findElements } from '../util/dom-manip'
 import ComponentContext from '../component/ComponentContext'
 import { computeSmallestCellWidth } from '../util/misc'
+import { mapHash } from '../util/object'
+import RefMap from '../util/RefMap'
 
 
 export type CssDimValue = string | number
@@ -27,18 +29,17 @@ export interface ChunkConfig {
   content?: (contentProps: ChunkContentCallbackArgs) => VNode
   rowContent?: VNode
   vGrowRows?: boolean
-  needsSizing?: boolean
   scrollerElRef?: Ref<HTMLDivElement>
   elRef?: Ref<HTMLTableCellElement>
   className?: string // on the <td>
 }
 
 export interface ChunkContentCallbackArgs {
-  colGroupNode: VNode
-  rowsGrow: boolean
-  type: string
+  tableColGroupNode: VNode
+  tableMinWidth: CssDimValue
+  tableWidth: CssDimValue
+  tableHeight: CssDimValue
   isSizingReady: boolean
-  minWidth: CssDimValue
 }
 
 
@@ -88,30 +89,31 @@ export function getNeedsYScrolling(props: { vGrow?: boolean }, sectionConfig: Se
 }
 
 
-export function renderChunkContent(
-  sectionConfig: SectionConfig,
-  chunkConfig: ChunkConfig,
-  microColGroupNode: VNode,
-  chunkTableMinWidth: CssDimValue,
+export function renderChunkContent(sectionConfig: SectionConfig, chunkConfig: ChunkConfig, arg: {
+  tableColGroupNode: VNode,
+  tableMinWidth: CssDimValue,
+  tableWidth: CssDimValue,
+  tableHeight: CssDimValue,
   isSizingReady: boolean
-) {
-  let vGrowRows = sectionConfig.vGrowRows || chunkConfig.vGrowRows
+}) {
+  let tableHeight = (sectionConfig.vGrowRows || chunkConfig.vGrowRows) ? arg.tableHeight : ''
 
   let content: VNode = typeof chunkConfig.content === 'function' ?
     chunkConfig.content({
-      colGroupNode: microColGroupNode,
-      rowsGrow: vGrowRows,
-      type: sectionConfig.type,
-      isSizingReady,
-      minWidth: chunkTableMinWidth
+      tableColGroupNode: arg.tableColGroupNode,
+      tableMinWidth: arg.tableMinWidth,
+      tableWidth: arg.tableWidth,
+      tableHeight,
+      isSizingReady: arg.isSizingReady,
     }) :
     h('table', {
-      class: (vGrowRows ? 'vgrow' : ''),
       style: {
-        minWidth: chunkTableMinWidth // because colMinWidths arent enough
+        minWidth: arg.tableMinWidth, // because colMinWidths arent enough
+        width: arg.tableWidth,
+        height: tableHeight // css `height` on a <table> serves as a min-height
       }
     }, [
-      microColGroupNode,
+      arg.tableColGroupNode,
       h('tbody', {}, chunkConfig.rowContent)
     ])
 
@@ -126,9 +128,7 @@ export function renderMicroColGroup(cols: ColProps[], shrinkWidth?: number) { //
         <col
           span={colProps.span || 1}
           style={{
-            /* why 4? if we do 0, it will kill any border, which are needed for computeSmallestCellWidth
-            4 accounts for 2 2-pixel borders. TODO: better solution? */
-            width: colProps.width === 'shrink' ? (shrinkWidth || 4) : (colProps.width || ''),
+            width: colProps.width === 'shrink' ? sanitizeShrinkWidth(shrinkWidth) : (colProps.width || ''),
             minWidth: colProps.minWidth || ''
           }}
         />
@@ -138,6 +138,13 @@ export function renderMicroColGroup(cols: ColProps[], shrinkWidth?: number) { //
 }
 
 
+export function sanitizeShrinkWidth(shrinkWidth?: number) {
+  /* why 4? if we do 0, it will kill any border, which are needed for computeSmallestCellWidth
+  4 accounts for 2 2-pixel borders. TODO: better solution? */
+  return shrinkWidth == null ? 4 : shrinkWidth
+}
+
+
 export function hasShrinkWidth(cols: ColProps[]) {
   for (let col of cols) {
     if (col.width === 'shrink') {
@@ -182,3 +189,16 @@ export function getSectionClassNames(sectionConfig: SectionConfig, wholeTableVGr
 export function getChunkClassNames(sectionConfig: SectionConfig, chunkConfig: ChunkConfig, context: ComponentContext) {
   return chunkConfig.className
 }
+
+
+// IE sometimes reports a certain clientHeight, but when inner content is set to that height,
+// some sort of rounding error causes it to spill out and create unnecessary scrollbars. Compensate.
+const CLIENT_HEIGHT_WIGGLE = /Trident/.test(navigator.userAgent) ? 1 : 0
+
+export function computeScrollerClientWidths(scrollerElRefs: RefMap<HTMLElement, any>) {
+  return mapHash(scrollerElRefs.currentMap, (scrollerEl) => scrollerEl.clientWidth)
+}
+
+export function computeScrollerClientHeights(scrollerElRefs: RefMap<HTMLElement, any>) {
+  return mapHash(scrollerElRefs.currentMap, (scrollerEl) => scrollerEl.clientHeight - CLIENT_HEIGHT_WIGGLE)
+}

+ 1 - 0
packages/core/src/vdom-util.tsx

@@ -331,6 +331,7 @@ export function renderVNodes(children: ComponentChildren, context: ComponentCont
 }
 
 
+// TODO: kill this at some pt???
 export function componentNeedsResize<P, S>(prevProps: P, props: P, prevState: S, state: S, stateIsSizing: { [K in keyof S]?: boolean }) {
   if (!isPropsEqual(prevProps, props)) {
     return true

+ 2 - 2
packages/daygrid/src/DayTableView.tsx

@@ -14,7 +14,7 @@ import TableView, { isEventLimitAuto } from './TableView'
 import DayTable from './DayTable'
 
 
-export default class DayTableView extends TableView {
+export default class DayTableView extends TableView { // TODO: use clientWidth/clientHeight
 
   private buildDayTableModel = memoize(buildDayTableModel)
   private headerRef = createRef<DayHeader>()
@@ -50,7 +50,7 @@ export default class DayTableView extends TableView {
           eventResize={props.eventResize}
           isRigid={isEventLimitAuto(context.options) && !props.isHeightAuto}
           nextDayThreshold={context.nextDayThreshold}
-          colGroupNode={contentArg.colGroupNode}
+          colGroupNode={contentArg.tableColGroupNode}
           renderNumberIntro={this.renderNumberIntro}
           renderBgIntro={this.renderBgIntro}
           renderIntro={this.renderIntro}

+ 10 - 3
packages/timegrid/src/DayTimeCols.tsx

@@ -15,7 +15,8 @@ import {
   Hit,
   ComponentContext,
   subrenderer,
-  NowTimer
+  NowTimer,
+  CssDimValue
 } from '@fullcalendar/core'
 import TimeCols, { TimeColsSeg, TIME_COLS_NOW_INDICATOR_UNIT } from './TimeCols'
 
@@ -30,10 +31,13 @@ export interface DayTimeColsProps {
   eventSelection: string
   eventDrag: EventInteractionState | null
   eventResize: EventInteractionState | null
-  colGroupNode: VNode
+  tableColGroupNode: VNode
+  tableWidth: CssDimValue
+  tableHeight: CssDimValue
   renderBgIntro: () => VNode[]
   renderIntro: () => VNode[]
   forPrint: boolean
+  allowSizing: boolean
 }
 
 interface DayTimeColsState {
@@ -65,12 +69,15 @@ export default class DayTimeCols extends DateComponent<DayTimeColsProps, DayTime
         {...this.slicer.sliceProps(props, dateProfile, null, context.calendar, dayRanges)}
         dateProfile={dateProfile}
         cells={dayTableModel.cells[0]}
-        colGroupNode={props.colGroupNode}
+        tableColGroupNode={props.tableColGroupNode}
+        tableWidth={props.tableWidth}
+        tableHeight={props.tableHeight}
         renderBgIntro={props.renderBgIntro}
         renderIntro={props.renderIntro}
         nowIndicatorDate={state.nowIndicatorDate}
         nowIndicatorSegs={state.nowIndicatorDate && this.slicer.sliceNowDate(state.nowIndicatorDate, this.context.calendar, this.dayRanges)}
         forPrint={props.forPrint}
+        allowSizing={props.allowSizing}
       />
     )
   }

+ 5 - 2
packages/timegrid/src/DayTimeColsView.tsx

@@ -42,7 +42,7 @@ export default class DayTimeColsView extends TimeColsView {
           dateProfile={dateProfile}
           dayTableModel={dayTableModel}
           nextDayThreshold={nextDayThreshold}
-          colGroupNode={contentArg.colGroupNode}
+          colGroupNode={contentArg.tableColGroupNode}
           isRigid={false}
           renderNumberIntro={this.renderTableIntro}
           renderBgIntro={this.renderTableBgIntro}
@@ -59,10 +59,13 @@ export default class DayTimeColsView extends TimeColsView {
           {...splitProps['timed']}
           dateProfile={dateProfile}
           dayTableModel={dayTableModel}
-          colGroupNode={contentArg.colGroupNode}
           renderBgIntro={this.renderTimeColsBgIntro}
           renderIntro={this.renderTimeColsIntro}
           forPrint={props.forPrint}
+          tableColGroupNode={contentArg.tableColGroupNode}
+          tableWidth={contentArg.tableWidth}
+          tableHeight={contentArg.tableHeight}
+          allowSizing={contentArg.isSizingReady}
         />
       )
     )

+ 28 - 33
packages/timegrid/src/TimeCols.tsx

@@ -19,7 +19,8 @@ import {
   sortEventSegs,
   memoize,
   subrenderer,
-  setRef
+  setRef,
+  CssDimValue
 } from '@fullcalendar/core'
 import { DayBgRow, DayBgCellModel } from '@fullcalendar/daygrid'
 import TimeColsEvents from './TimeColsEvents'
@@ -47,12 +48,15 @@ export interface TimeColsProps {
   eventDrag: EventSegUiInteractionState | null
   eventResize: EventSegUiInteractionState | null
   rootElRef?: Ref<HTMLDivElement>
-  colGroupNode: VNode
+  tableColGroupNode: VNode
+  tableWidth: CssDimValue
+  tableHeight: CssDimValue
   renderBgIntro: () => VNode[]
   renderIntro: () => VNode[]
   nowIndicatorDate: DateMarker
   nowIndicatorSegs: TimeColsSeg[]
   forPrint: boolean
+  allowSizing: boolean
 }
 
 export const TIME_COLS_NOW_INDICATOR_UNIT = 'minute'
@@ -89,8 +93,6 @@ export default class TimeCols extends BaseComponent<TimeColsProps> {
 
   private colPositions: PositionCache
   private slatPositions: PositionCache
-  private isSlatSizesDirty: boolean = false
-  private isColSizesDirty: boolean = false
   private segRenderers: (TimeColsEvents | TimeColsFills | null)[]
 
 
@@ -107,7 +109,7 @@ export default class TimeCols extends BaseComponent<TimeColsProps> {
       <div class='fc-time-grid' ref={this.handleRootEl}>
         <div class='fc-bg'>
           <table class={theme.getClass('table')}>
-            {props.colGroupNode}
+            {props.tableColGroupNode}
             <DayBgRow
               dateProfile={props.dateProfile}
               cells={props.cells}
@@ -117,8 +119,15 @@ export default class TimeCols extends BaseComponent<TimeColsProps> {
           </table>
         </div>
         <div class='fc-slats'>
-          <table class={theme.getClass('table')}>
-            {props.colGroupNode /* relies on there only being a single <col> for the axis */}
+          <table
+            class={theme.getClass('table') + ' vgrow' /* why not use rowsGrow like resource view? */}
+            style={{
+              // TODO: add minWidth
+              width: props.tableWidth,
+              height: props.tableHeight
+            }}
+          >
+            {props.tableColGroupNode /* relies on there only being a single <col> for the axis */}
             <TimeColsSlats
               dateProfile={props.dateProfile}
               slotDuration={this.slotDuration}
@@ -128,7 +137,7 @@ export default class TimeCols extends BaseComponent<TimeColsProps> {
         </div>
         <div class='fc-content-skeleton'>
           <table>
-            {props.colGroupNode}
+            {props.tableColGroupNode}
             <TimeColsContentSkeleton
               colCnt={props.cells.length}
               renderIntro={props.renderIntro}
@@ -172,7 +181,6 @@ export default class TimeCols extends BaseComponent<TimeColsProps> {
   handleBgCellEls = (colEls: HTMLElement[] | null) => {
     if (colEls) {
       this.colEls = colEls
-      this.isColSizesDirty = true
       this.colPositions = new PositionCache(
         colEls[0].parentNode as HTMLElement,
         colEls,
@@ -187,7 +195,6 @@ export default class TimeCols extends BaseComponent<TimeColsProps> {
     if (slatEls) {
       let slatRootEl = this.slatRootEl = slatEls[0].parentNode as HTMLElement
       this.slatEls = slatEls
-      this.isSlatSizesDirty = true
       this.slatPositions = new PositionCache(
         slatRootEl,
         slatEls,
@@ -209,14 +216,14 @@ export default class TimeCols extends BaseComponent<TimeColsProps> {
 
   componentDidMount() {
     this.subrender()
-    this.handleSizing(false)
+    this.handleSizing()
     this.context.addResizeHandler(this.handleSizing)
   }
 
 
   componentDidUpdate() {
     this.subrender()
-    this.handleSizing(false)
+    this.handleSizing()
   }
 
 
@@ -301,28 +308,26 @@ export default class TimeCols extends BaseComponent<TimeColsProps> {
   }
 
 
-  handleSizing = (forced: boolean) => {
+  handleSizing = () => {
     let { segRenderers } = this
 
-    if (forced || this.isSlatSizesDirty) {
-      this.buildSlatPositions()
-      this.isSlatSizesDirty = false
-    }
-
-    if (forced || this.isColSizesDirty) {
-      this.buildColPositions()
-      this.isColSizesDirty = false
+    if (this.props.allowSizing) {
+      this.slatPositions.build()
+      this.colPositions.build()
+    } else {
+      this.slatPositions.buildZeros()
+      this.colPositions.buildZeros()
     }
 
     for (let segRenderer of segRenderers) {
       if (segRenderer) {
-        segRenderer.computeSizes(forced, this)
+        segRenderer.computeSizes(true, this) // TODO: always forced!?
       }
     }
 
     for (let segRenderer of segRenderers) {
       if (segRenderer) {
-        segRenderer.assignSizes(forced, this)
+        segRenderer.assignSizes(true, this) // TODO: always forced!?
       }
     }
 
@@ -469,17 +474,7 @@ export default class TimeCols extends BaseComponent<TimeColsProps> {
 
 
   buildPositionCaches() {
-    this.buildColPositions()
-    this.buildSlatPositions()
-  }
-
-
-  buildColPositions() {
     this.colPositions.build()
-  }
-
-
-  buildSlatPositions() {
     this.slatPositions.build()
   }
 

+ 3 - 3
packages/timegrid/src/TimeColsView.tsx

@@ -51,7 +51,7 @@ export default abstract class TimeColsView extends View {
     allDayContent: ((contentArg: ChunkContentCallbackArgs) => VNode) | null,
     timeContent: ((contentArg: ChunkContentCallbackArgs) => VNode) | null
   ) {
-    let { props } = this
+    let { context, props } = this
     let classNames = getViewClassNames(props.viewSpec).concat('fc-timeGrid-view')
     let sections: SimpleScrollGridSection[] = []
 
@@ -76,8 +76,7 @@ export default abstract class TimeColsView extends View {
           <tr>
             <td
               ref={this.dividerElRef}
-              class={'fc-divider ' + this.context.theme.getClass('tableCellShaded')}
-              colSpan={0}
+              class={'fc-divider ' + context.theme.getClass('tableCellShaded')}
             />
           </tr>
         )
@@ -87,6 +86,7 @@ export default abstract class TimeColsView extends View {
     sections.push({
       type: 'body',
       vGrow: true,
+      vGrowRows: Boolean(context.options.expandRows),
       chunk: {
         scrollerElRef: this.scrollerElRef,
         content: timeContent

+ 1 - 0
packages/timegrid/src/styles/_time-grid.scss

@@ -2,6 +2,7 @@
 .fc-time-grid {
   position: relative; // for housing skeletons
   z-index: 1; // because containers z-index'd children
+  min-height: 100%; // fill height always ... even when slat table doesn't grow
 }