import { createElement , VNode , addDurations , multiplyDuration , wholeDivideDurations , DateMarker , EventSegUiInteractionState , memoize , CssDimValue , PositionCache , ScrollResponder , ScrollRequest , DateRange , Duration , DateProfile , DayTableCell , Hit , DateComponent , } from '@fullcalendar/common' ; import { TimeColsSlats } from './TimeColsSlats' ; import { TimeSlatMeta } from './time-slat-meta' ; import { TimeColsContent } from './TimeColsContent' ; import { TimeColsSlatsCoords } from './TimeColsSlatsCoords' ; import { TimeColsSeg } from './TimeColsSeg' ; export interface TimeColsProps { cells : DayTableCell [ ] ; dateProfile : DateProfile ; slotDuration : Duration ; nowDate : DateMarker ; todayRange : DateRange ; businessHourSegs : TimeColsSeg [ ] ; bgEventSegs : TimeColsSeg [ ] ; fgEventSegs : TimeColsSeg [ ] ; dateSelectionSegs : TimeColsSeg [ ] ; eventSelection : string ; eventDrag : EventSegUiInteractionState | null ; eventResize : EventSegUiInteractionState | null ; tableColGroupNode : VNode ; tableMinWidth : CssDimValue ; clientWidth : number | null ; clientHeight : number | null ; expandRows : boolean ; nowIndicatorSegs : TimeColsSeg [ ] ; onScrollTopRequest ? : ( scrollTop : number ) => void ; forPrint : boolean ; axis : boolean ; slatMetas : TimeSlatMeta [ ] ; onSlatCoords ? : ( slatCoords : TimeColsSlatsCoords ) => void ; isHitComboAllowed ? : ( hit0 : Hit , hit1 : Hit ) => boolean ; } interface TimeColsState { slatCoords : TimeColsSlatsCoords | null ; } export class TimeCols extends DateComponent < TimeColsProps , TimeColsState > { private processSlotOptions = memoize ( processSlotOptions ) ; private scrollResponder : ScrollResponder ; private colCoords : PositionCache ; state = { slatCoords : null , } ; render ( ) { let { props , state } = this ; return ( < div className = "fc-timegrid-body" ref = { this . handleRootEl } style = { { width : props . clientWidth , minWidth : props . tableMinWidth , } } > < TimeColsSlats axis = { props . axis } dateProfile = { props . dateProfile } slatMetas = { props . slatMetas } clientWidth = { props . clientWidth } minHeight = { props . expandRows ? props . clientHeight : '' } tableMinWidth = { props . tableMinWidth } tableColGroupNode = { props . axis ? props . tableColGroupNode : null } onCoords = { this . handleSlatCoords } / > < TimeColsContent cells = { props . cells } axis = { props . axis } dateProfile = { props . dateProfile } businessHourSegs = { props . businessHourSegs } bgEventSegs = { props . bgEventSegs } fgEventSegs = { props . fgEventSegs } dateSelectionSegs = { props . dateSelectionSegs } eventSelection = { props . eventSelection } eventDrag = { props . eventDrag } eventResize = { props . eventResize } todayRange = { props . todayRange } nowDate = { props . nowDate } nowIndicatorSegs = { props . nowIndicatorSegs } clientWidth = { props . clientWidth } tableMinWidth = { props . tableMinWidth } tableColGroupNode = { props . tableColGroupNode } slatCoords = { state . slatCoords } onColCoords = { this . handleColCoords } forPrint = { props . forPrint } / > < /div>); } handleRootEl = ( el ) => { if ( el ) { this . context . registerInteractiveComponent ( this , { el , isHitComboAllowed : this . props . isHitComboAllowed , } ) ; } else { this . context . unregisterInteractiveComponent ( this ) ; } } ; componentDidMount ( ) { this . scrollResponder = this . context . createScrollResponder ( this . handleScrollRequest ) ; } componentDidUpdate ( prevProps ) { this . scrollResponder . update ( prevProps . dateProfile !== this . props . dateProfile ) ; } componentWillUnmount ( ) { this . scrollResponder . detach ( ) ; } handleScrollRequest = ( request ) => { let { onScrollTopRequest } = this . props ; let { slatCoords } = this . state ; if ( onScrollTopRequest && slatCoords ) { if ( request . time ) { let top = slatCoords . computeTimeTop ( request . time ) ; top = Math . ceil ( top ) ; if ( top ) { top += 1 ; } onScrollTopRequest ( top ) ; } return true ; } return false ; } ; handleColCoords = ( colCoords ) => { this . colCoords = colCoords ; } ; handleSlatCoords = ( slatCoords ) => { this . setState ( { slatCoords } ) ; if ( this . props . onSlatCoords ) { this . props . onSlatCoords ( slatCoords ) ; } } ; queryHit ( positionLeft , positionTop ) { let { dateEnv , options } = this . context ; let { colCoords } = this ; let { dateProfile } = this . props ; let { slatCoords } = this . state ; let { snapDuration , snapsPerSlot } = this . processSlotOptions ( this . props . slotDuration , options . snapDuration ) ; let colIndex = colCoords . leftToIndex ( positionLeft ) ; let slatIndex = slatCoords . positions . topToIndex ( positionTop ) ; if ( colIndex != null && slatIndex != null ) { let cell = this . props . cells [ colIndex ] ; let slatTop = slatCoords . positions . tops [ slatIndex ] ; let slatHeight = slatCoords . positions . getHeight ( slatIndex ) ; let partial = ( positionTop - slatTop ) / slatHeight ; let localSnapIndex = Math . floor ( partial * snapsPerSlot ) ; let snapIndex = slatIndex * snapsPerSlot + localSnapIndex ; let dayDate = this . props . cells [ colIndex ] . date ; let time = addDurations ( dateProfile . slotMinTime , multiplyDuration ( snapDuration , snapIndex ) ) ; let start = dateEnv . add ( dayDate , time ) ; let end = dateEnv . add ( start , snapDuration ) ; return { dateProfile , dateSpan : { range : { start , end } , allDay : false , ... cell . extraDateSpan , } , dayEl : colCoords . els [ colIndex ] , rect : { left : colCoords . lefts [ colIndex ] , right : colCoords . rights [ colIndex ] , top : slatTop , bottom : slatTop + slatHeight , } , layer : 0 , } ; } return null ; } } function processSlotOptions ( slotDuration , snapDurationOverride ) { let snapDuration = snapDurationOverride || slotDuration ; let snapsPerSlot = wholeDivideDurations ( slotDuration , snapDuration ) ; if ( snapsPerSlot === null ) { snapDuration = slotDuration ; snapsPerSlot = 1 ; } return { snapDuration , snapsPerSlot } ; }