import { TimelineEvent , TimelineTick , TimelineTrackSpecification , TimelineTrackType , } from '../types' ; import { intersect } from './intersect' ; import _ from 'lodash' ; export const TIMELINE_TRACK_HEIGHT = 20 ; export const TIMELINE_LINE_CHART_TRACK_HEIGHT = 100 ; export const REMOVE_FOR_DOWNLOAD_CLASSNAME = 'tl-remove-for-download' ; export function getTrackHeight ( track ) { switch ( track . trackType ) { case TimelineTrackType . LINE_CHART : return TIMELINE_LINE_CHART_TRACK_HEIGHT ; case TimelineTrackType . DEFAULT : case undefined : default : return TIMELINE_TRACK_HEIGHT ; } } export function getAttributeValue ( name , event ) { const att = _ . at ( event as any , [ 'event.attributes' ] ) ; const attObj = att [ 0 ] ? att [ 0 ] . find ( ( a ) => { if ( name instanceof RegExp ) { return name . test ( a . key ) ; } else { return a . key === name ; } } ) : undefined ; return attObj ? attObj . value : undefined ; } const TRIM_TICK_THRESHHOLD = 4 ; const TICK_OFFSET = 30 ; export function getTrimmedTicks ( ticks , expandedTrims ) { let tickCache = [ ] ; let offset = 0 ; return ticks . reduce ( ( aggr , tick ) => { if ( tick . events ! . length === 0 ) { tick . offset = offset ; tickCache . push ( tick ) ; } else { const isTrim = ! expandedTrims && tickCache . length >= TRIM_TICK_THRESHHOLD ; if ( isTrim ) { aggr . push ( tickCache [ 0 ] ) ; const trimmedTicks = tickCache . slice ( 1 , - 1 ) ; aggr . push ( { isTrim : true , start : trimmedTicks [ 0 ] . start , end : trimmedTicks [ 0 ] . end , realEnd : trimmedTicks [ trimmedTicks . length - 1 ] . end , offset : offset , } as TimelineTick ) ; const lastTick = tickCache [ tickCache . length - 1 ] ; offset += _ . sumBy ( trimmedTicks , t => t . end - t . start + 1 ) ; lastTick . offset = offset ; aggr . push ( lastTick ) ; tickCache = [ ] ; } else { aggr . push ( ... tickCache ) ; } aggr . push ( tick ) ; tickCache = [ ] ; } tick . offset = offset ; return aggr ; } , [ ] ) ; } export function getFullTicks ( events , tickInterval ) { const ticks = [ ] ; const lowerBound = Math . min ( ... events . map ( e => e . start ) ) ; const upperBound = Math . max ( ... events . map ( e => e . end ) ) ; const floor = Math . floor ( lowerBound / tickInterval ) * tickInterval ; let place = floor ; const ceiling = Math . ceil ( upperBound / tickInterval ) * tickInterval ; let i = 0 ; do { const start = place ; const end = place + tickInterval - 1 ; ticks . push ( { start , end , events : events . filter ( e => { return intersect ( e . start , e . end , start , end ) ; } ) , } ) ; place += tickInterval ; i ++ ; } while ( place < ceiling ) ; const BUFFER = 20 ; let diff = Math . abs ( ticks [ 0 ] . start ) - Math . abs ( lowerBound ) ; if ( diff < BUFFER ) { ticks . unshift ( { start : ticks [ 0 ] . start - BUFFER , end : ticks [ 0 ] . start - 1 , events : [ ] , } ) ; } else { ticks [ 0 ] . start = lowerBound - BUFFER ; } ticks [ ticks . length - 1 ] . end = upperBound + BUFFER ; return ticks ; } export function getPerc ( n , l ) { return ( n / l ) * 100 ; } export function getPointInTrimmedSpace ( x , regions ) { const region = _ . find ( regions , r => { return r . start <= x && ( r . end >= x || r . realEnd ! >= x ) ; } ) ; if ( region ) { if ( region . isTrim ) { return region . start - ( region . offset || 0 ) ; } else { return x - ( region . offset || 0 ) ; } } else { return undefined ; } } export function getPointInTrimmedSpaceFromScreenRead ( val , ticks ) { let rem = val ; let result = 0 ; _ . forEach ( ticks , tick => { if ( ! tick . isTrim ) { if ( rem >= tick . end - tick . start ) { rem = rem - ( tick . end - tick . start ) ; return true ; } else { result = tick . start + rem ; return false ; } } else { return true ; } } ) ; return result ; } export function sortNestedTracks ( tracks ) { return _ . sortBy ( tracks , t => t . items && t . items . length ? t . items [ 0 ] . start : 0 ) ; } export function formatDate ( dayCount ) { let negative = dayCount < 0 ; dayCount = Math . abs ( dayCount ) ; let years , months , days ; years = Math . floor ( dayCount / 365 ) ; months = Math . floor ( ( dayCount - years * 365 ) / 30 ) ; days = Math . floor ( dayCount - years * 365 - months * 30 ) ; let arr = [ ] ; if ( years > 0 ) arr . push ( `${ years } year${ years === 1 ? '' : 's' }` ) ; if ( months > 0 ) arr . push ( `${ months } month${ months === 1 ? '' : 's' }` ) ; if ( dayCount === 0 || days > 0 ) arr . push ( `${ days } day${ days === 1 ? '' : 's' }` ) ; const formattedDate = arr . join ( ', ' ) ; return `${ negative ? '-' : '' }${ formattedDate }` ; } function getAllDescendantData ( track , accumulator ) { accumulator . push ( ... track . items ) ; if ( track . tracks ) { for ( const nestedTrack of track . tracks ) { getAllDescendantData ( nestedTrack , accumulator ) ; } } return accumulator ; } function flattenTrack ( track , indent , isTrackCollapsed ) { const ret = [ { track , indent , height : getTrackHeight ( track ) } ] ; if ( track . tracks ) { if ( ! isTrackCollapsed ( track . uid ) ) { const sortedNestedTracks = sortNestedTracks ( track . tracks ) ; ret . push ( ... _ . flatMap ( sortedNestedTracks , t => flattenTrack ( t , indent + 17 , isTrackCollapsed ) ) ) ; } else { ret [ 0 ] . track = { ... track , items : getAllDescendantData ( track , [ ] ) , } ; } } return ret ; } export function flattenTracks ( tracks , isTrackCollapsed ) { return _ . flatMap ( tracks , t => flattenTrack ( t , 5 , isTrackCollapsed ) ) ; } export function isTrackVisible ( track , visibleTracks ) { return visibleTracks . includes ( track . type ) ; } export function getTrackEventColorGetter ( track ) { return track . eventColorGetter || track . timelineConfig ?. eventColorGetter ; }