import { createElement , createRef , diffDays , SimpleScrollGridSection , VNode , SimpleScrollGrid , ChunkContentCallbackArgs , ScrollGridSectionConfig , buildNavLinkAttrs , ViewRoot , WeekNumberRoot , RenderHook , DateComponent , ViewProps , RefObject , renderScrollShim , getStickyHeaderDates , getStickyFooterScrollbar , createFormatter , AllDayContentArg , CssDimValue , NowTimer , DateMarker , NowIndicatorRoot , } from '@fullcalendar/common' ; import { AllDaySplitter } from './AllDaySplitter' ; import { TimeSlatMeta } from './time-slat-meta' ; import { TimeColsSlatsCoords } from './TimeColsSlatsCoords' ; import { TimeBodyAxis } from './TimeBodyAxis' ; const DEFAULT_WEEK_NUM_FORMAT = createFormatter ( { week : 'short' } ) ; const AUTO_ALL_DAY_MAX_EVENT_ROWS = 5 ; interface TimeColsViewState { slatCoords : TimeColsSlatsCoords | null ; } export abstract class TimeColsView extends DateComponent < ViewProps , TimeColsViewState > { protected allDaySplitter = new AllDaySplitter ( ) ; protected headerElRef : RefObject < HTMLTableCellElement > = createRef < HTMLTableCellElement > ( ) ; private rootElRef : RefObject < HTMLDivElement > = createRef < HTMLDivElement > ( ) ; private scrollerElRef : RefObject < HTMLDivElement > = createRef < HTMLDivElement > ( ) ; state = { slatCoords : null , } ; renderSimpleLayout ( headerRowContent , allDayContent , timeContent ) { let { context , props } = this ; let sections = [ ] ; let stickyHeaderDates = getStickyHeaderDates ( context . options ) ; if ( headerRowContent ) { sections . push ( { type : 'header' , key : 'header' , isSticky : stickyHeaderDates , chunk : { elRef : this . headerElRef , tableClassName : 'fc-col-header' , rowContent : headerRowContent , } , } ) ; } if ( allDayContent ) { sections . push ( { type : 'body' , key : 'all-day' , chunk : { content : allDayContent } , } ) ; sections . push ( { type : 'body' , key : 'all-day-divider' , outerContent : ( < tr role = "presentation" className = "fc-scrollgrid-section" > < td className = { 'fc-timegrid-divider ' + context . theme . getClass ( 'tableCellShaded' ) } / > < /tr>), } ) ; } sections . push ( { type : 'body' , key : 'body' , liquid : true , expandRows : Boolean ( context . options . expandRows ) , chunk : { scrollerElRef : this . scrollerElRef , content : timeContent , } , } ) ; return ( < ViewRoot viewSpec = { context . viewSpec } elRef = { this . rootElRef } > { ( rootElRef , classNames ) => ( < div className = { [ 'fc-timegrid' ] . concat ( classNames ) . join ( ' ' ) } ref = { rootElRef } > < SimpleScrollGrid liquid = { ! props . isHeightAuto && ! props . forPrint } collapsibleWidth = { props . forPrint } cols = { [ { width : 'shrink' } ] } sections = { sections } / > < /div>)} < /ViewRoot>); } renderHScrollLayout ( headerRowContent , allDayContent , timeContent , colCnt , dayMinWidth , slatMetas , slatCoords ) { let ScrollGrid = this . context . pluginHooks . scrollGridImpl ; if ( ! ScrollGrid ) { throw new Error ( 'No ScrollGrid implementation' ) ; } let { context , props } = this ; let stickyHeaderDates = ! props . forPrint && getStickyHeaderDates ( context . options ) ; let stickyFooterScrollbar = ! props . forPrint && getStickyFooterScrollbar ( context . options ) ; let sections = [ ] ; if ( headerRowContent ) { sections . push ( { type : 'header' , key : 'header' , isSticky : stickyHeaderDates , syncRowHeights : true , chunks : [ { key : 'axis' , rowContent : ( arg ) => ( < tr role = "presentation" > { this . renderHeadAxis ( 'day' , arg . rowSyncHeights [ 0 ] ) } < /tr>), } , { key : 'cols' , elRef : this . headerElRef , tableClassName : 'fc-col-header' , rowContent : headerRowContent , } , ] , } ) ; } if ( allDayContent ) { sections . push ( { type : 'body' , key : 'all-day' , syncRowHeights : true , chunks : [ { key : 'axis' , rowContent : ( contentArg ) => ( < tr role = "presentation" > { this . renderTableRowAxis ( contentArg . rowSyncHeights [ 0 ] ) } < /tr>), } , { key : 'cols' , content : allDayContent , } , ] , } ) ; sections . push ( { key : 'all-day-divider' , type : 'body' , outerContent : ( < tr role = "presentation" className = "fc-scrollgrid-section" > < td colSpan = { 2 } className = { 'fc-timegrid-divider ' + context . theme . getClass ( 'tableCellShaded' ) } / > < /tr>), } ) ; } let isNowIndicator = context . options . nowIndicator ; sections . push ( { type : 'body' , key : 'body' , liquid : true , expandRows : Boolean ( context . options . expandRows ) , chunks : [ { key : 'axis' , content : ( arg ) => ( < div className = "fc-timegrid-axis-chunk" > < table aria - hidden style = { { height : arg . expandRows ? arg . clientHeight : '' } } > { arg . tableColGroupNode } < tbody > < TimeBodyAxis slatMetas = { slatMetas } / > < /tbody> < /table> < div className = "fc-timegrid-now-indicator-container" > < NowTimer unit = { isNowIndicator ? 'minute' : 'day' } > { ( nowDate ) => { let nowIndicatorTop = isNowIndicator && slatCoords && slatCoords . safeComputeTop ( nowDate ) ; if ( typeof nowIndicatorTop === 'number' ) { return ( < NowIndicatorRoot isAxis date = { nowDate } > { ( rootElRef , classNames , innerElRef , innerContent ) => ( < div ref = { rootElRef } className = { [ 'fc-timegrid-now-indicator-arrow' ] . concat ( classNames ) . join ( ' ' ) } style = { { top : nowIndicatorTop } } > { innerContent } < /div>)} < /NowIndicatorRoot>); } return null ; } } < /NowTimer> < /div> < /div>), } , { key : 'cols' , scrollerElRef : this . scrollerElRef , content : timeContent , } , ] , } ) ; if ( stickyFooterScrollbar ) { sections . push ( { key : 'footer' , type : 'footer' , isSticky : true , chunks : [ { key : 'axis' , content : renderScrollShim , } , { key : 'cols' , content : renderScrollShim , } , ] , } ) ; } return ( < ViewRoot viewSpec = { context . viewSpec } elRef = { this . rootElRef } > { ( rootElRef , classNames ) => ( < div className = { [ 'fc-timegrid' ] . concat ( classNames ) . join ( ' ' ) } ref = { rootElRef } > < ScrollGrid liquid = { ! props . isHeightAuto && ! props . forPrint } collapsibleWidth = { false } colGroups = { [ { width : 'shrink' , cols : [ { width : 'shrink' } ] } , { cols : [ { span : colCnt , minWidth : dayMinWidth } ] } , ] } sections = { sections } / > < /div>)} < /ViewRoot>); } handleScrollTopRequest = ( scrollTop ) => { let scrollerEl = this . scrollerElRef . current ; if ( scrollerEl ) { scrollerEl . scrollTop = scrollTop ; } } ; getAllDayMaxEventProps ( ) { let { dayMaxEvents , dayMaxEventRows } = this . context . options ; if ( dayMaxEvents === true || dayMaxEventRows === true ) { dayMaxEvents = undefined ; dayMaxEventRows = AUTO_ALL_DAY_MAX_EVENT_ROWS ; } return { dayMaxEvents , dayMaxEventRows } ; } renderHeadAxis = ( rowKey , frameHeight = '' ) => { let { options } = this . context ; let { dateProfile } = this . props ; let range = dateProfile . renderRange ; let dayCnt = diffDays ( range . start , range . end ) ; let navLinkAttrs = ( dayCnt === 1 ) ? buildNavLinkAttrs ( this . context , range . start , 'week' ) : { } ; if ( options . weekNumbers && rowKey === 'day' ) { return ( < WeekNumberRoot date = { range . start } defaultFormat = { DEFAULT_WEEK_NUM_FORMAT } > { ( rootElRef , classNames , innerElRef , innerContent ) => ( < th ref = { rootElRef } aria - hidden className = { [ 'fc-timegrid-axis' , 'fc-scrollgrid-shrink' , ] . concat ( classNames ) . join ( ' ' ) } > < div className = "fc-timegrid-axis-frame fc-scrollgrid-shrink-frame fc-timegrid-axis-frame-liquid" style = { { height : frameHeight } } > < a ref = { innerElRef } className = "fc-timegrid-axis-cushion fc-scrollgrid-shrink-cushion fc-scrollgrid-sync-inner" { ... navLinkAttrs } > { innerContent } < /a> < /div> < /th>)} < /WeekNumberRoot>); } return ( < th aria - hidden className = "fc-timegrid-axis" > < div className = "fc-timegrid-axis-frame" style = { { height : frameHeight } } /> < /th>); } ; renderTableRowAxis = ( rowHeight ? ) => { let { options , viewApi } = this . context ; let hookProps = { text : options . allDayText , view : viewApi , } ; return ( < RenderHook < AllDayContentArg > hookProps = { hookProps } classNames = { options . allDayClassNames } content = { options . allDayContent } defaultContent = { renderAllDayInner } didMount = { options . allDayDidMount } willUnmount = { options . allDayWillUnmount } > { ( rootElRef , classNames , innerElRef , innerContent ) => ( < td ref = { rootElRef } aria - hidden className = { [ 'fc-timegrid-axis' , 'fc-scrollgrid-shrink' , ] . concat ( classNames ) . join ( ' ' ) } > < div className = { 'fc-timegrid-axis-frame fc-scrollgrid-shrink-frame' + ( rowHeight == null ? ' fc-timegrid-axis-frame-liquid' : '' ) } style = { { height : rowHeight } } > < span className = "fc-timegrid-axis-cushion fc-scrollgrid-shrink-cushion fc-scrollgrid-sync-inner" ref = { innerElRef } > { innerContent } < /span> < /div> < /td>)} < /RenderHook>); } ; handleSlatCoords = ( slatCoords ) => { this . setState ( { slatCoords } ) ; } ; } function renderAllDayInner ( hookProps ) { return hookProps . text ; }