@@ -38,6 +38,8 @@ export class TimelineComponent extends BaseComponentDirective implements OnInit,
3838 SelectedDateFieldVariable3 ;
3939 binSizes = [ 'Day' , 'Week' , 'Month' , 'Quarter' , 'Year' ]
4040 tickInterval ;
41+ labelSize = 12 ;
42+ legendLabelSize = 15 ;
4143
4244 graphTypes = [ 'Single Date Field' , 'Multi: Side by Side' , 'Multi: Overlay' ]
4345 selectedGraphType = 'Single Date Field' ;
@@ -157,7 +159,7 @@ export class TimelineComponent extends BaseComponentDirective implements OnInit,
157159
158160 // colors
159161 if ( this . widgets [ 'epiCurve-colors' ] == undefined ) {
160- this . widgets [ 'epiCurve-colors' ] = [ '#f5d742' , '#f20fff ', '#20ff1a ' ] ;
162+ this . widgets [ 'epiCurve-colors' ] = [ '#C6D8EB' , '#B79ECC ', '#F3BF79 ' ] ;
161163 }
162164
163165 // stackColorBy field
@@ -185,13 +187,6 @@ export class TimelineComponent extends BaseComponentDirective implements OnInit,
185187
186188 // this.initializeD3Chart();
187189 this . setupEventListeners ( ) ;
188-
189- if ( this . widgets [ 'epiCurve-legendPosition' ] == 'Bottom' ) {
190- this . margin . bottom = 100 ;
191- } else {
192- this . margin . bottom = 50 ;
193- }
194-
195190 this . refresh ( ) ;
196191 }
197192
@@ -236,7 +231,7 @@ public refresh(): void {
236231
237232 const epiCurve = this . svg . append ( "g" )
238233 . classed ( "epiCurve-epi-curve" , true )
239- . attr ( "transform" , `translate(${ this . margin . left } , 5 )` ) ;
234+ . attr ( "transform" , `translate(${ this . margin . left } , ${ this . margin . top } )` ) ;
240235
241236 let bins = this . histogram ( this . vnodes ) ;
242237
@@ -267,6 +262,7 @@ public refresh(): void {
267262 . attr ( "width" , d => this . x ( d . x1 ) - this . x ( d . x0 ) )
268263 . attr ( "height" , d => this . height - this . y ( d . length2 [ ind ] ) )
269264 . attr ( "fill" , this . localColorMap ( value ) )
265+ . attr ( "stroke" , "black" )
270266
271267 nodeColors . push ( this . localColorMap ( value ) ) ;
272268 } )
@@ -280,7 +276,8 @@ public refresh(): void {
280276 . attr ( "transform" , d => `translate(${ this . x ( d . x0 ) } , ${ this . y ( d . length ) } )` )
281277 . attr ( "width" , d => this . x ( d . x1 ) - this . x ( d . x0 ) )
282278 . attr ( "height" , d => this . height - this . y ( d . length ) )
283- . attr ( "fill" , color ) ;
279+ . attr ( "fill" , color )
280+ . attr ( "stroke" , "black" ) ;
284281
285282 this . generateLegend ( epiCurve , [ color ] , [ this . SelectedDateFieldVariable ] )
286283 }
@@ -336,13 +333,12 @@ private refreshMulti(): void {
336333
337334 this . svg = d3 . select ( this . epiCurveSVGElement . nativeElement )
338335 . attr ( "width" , this . width + this . margin . left + this . margin . right )
339- . attr ( "height" , this . height + this . margin . top + this . margin . bottom )
340- . attr ( "transform" , `translate(0, ${ this . margin . top } )` ) ;
336+ . attr ( "height" , this . height + this . margin . top + this . margin . bottom ) ;
341337
342338
343339 const epiCurve = this . svg . append ( "g" )
344340 . classed ( "epiCurve-epi-curve" , true )
345- . attr ( "transform" , `translate(${ this . margin . left } , 5 )` ) ;
341+ . attr ( "transform" , `translate(${ this . margin . left } , ${ this . margin . top } )` ) ;
346342
347343 let maxCount = 0 ;
348344 let bins = [ ] ;
@@ -405,7 +401,8 @@ private refreshMulti(): void {
405401 . attr ( "transform" , d => `translate(${ xOffset [ ind ] ( d , that ) } , ${ this . y ( d . length ) } )` )
406402 . attr ( "width" , width )
407403 . attr ( "height" , d => this . height - this . y ( d . length ) )
408- . attr ( "fill" , colors [ ind ] ) ;
404+ . attr ( "fill" , colors [ ind ] )
405+ . attr ( "stroke" , "black" ) ;
409406 } ) ;
410407 }
411408
@@ -430,6 +427,7 @@ updateLocalColorMap() {
430427}
431428
432429updateSizes ( ) {
430+ this . updateBottomMargin ( ) ;
433431 const wrapper = $ ( this . epiCurveElement . nativeElement ) . parent ( ) ;
434432 $ ( '#epiCurve' ) . height ( wrapper . height ( ) - 50 ) ;
435433 this . width = wrapper . width ( ) - this . margin . left - this . margin . right ;
@@ -438,6 +436,16 @@ updateSizes() {
438436 this . middle = this . height / 2 ;
439437}
440438
439+ private updateBottomMargin ( ) {
440+ const baseBottomMargin = this . widgets [ 'epiCurve-legendPosition' ] == 'Bottom' ? 100 : 50 ;
441+ const labelSizePadding = Math . max ( 0 , this . labelSize - 12 ) * 2 ;
442+ const legendSizePadding = this . widgets [ 'epiCurve-legendPosition' ] == 'Bottom' ? Math . max ( 0 , this . legendLabelSize - 15 ) * 2 : 0 ;
443+ this . margin . bottom = baseBottomMargin + labelSizePadding + legendSizePadding ;
444+ this . margin . top = Math . max ( 8 , Math . round ( this . labelSize * 0.75 ) ) ;
445+ this . margin . left = Math . max ( 45 , Math . round ( this . labelSize * 3.2 ) ) ;
446+ this . margin . right = Math . max ( 20 , Math . round ( this . labelSize * 2 ) ) + 10 ;
447+ }
448+
441449getTimes ( fields ) {
442450 let times = [ ] ;
443451 this . vnodes = JSON . parse ( JSON . stringify ( this . commonService . session . data . nodes ) ) ;
@@ -458,38 +466,56 @@ getTimes(fields) {
458466 }
459467 return times ;
460468}
469+ onLabelSizeChange ( ) {
470+ this . refresh ( ) ;
471+ }
461472
462473updateAxes ( ) {
463- let [ xAxis , xLabelOffset ] = this . configureXAxisSettings ( ) ;
474+ const xAxis = this . configureXAxisSettings ( ) ;
475+ const yDomainMax = Math . max ( 0 , Math . ceil ( this . y . domain ( ) [ 1 ] as number ) ) ;
476+ const yTickStep = Math . max ( 1 , Math . ceil ( yDomainMax / 10 ) ) ;
477+ const yTickValues = d3 . range ( 0 , yDomainMax + 1 , yTickStep ) ;
478+ if ( yTickValues [ yTickValues . length - 1 ] !== yDomainMax ) {
479+ yTickValues . push ( yDomainMax ) ;
480+ }
481+ const yAxis = d3 . axisLeft ( this . y )
482+ . tickValues ( yTickValues )
483+ . tickFormat ( ( d : number ) => `${ Math . round ( d ) } ` ) ;
464484
465- this . svg . append ( "g" )
485+ const xLabelY = this . height + this . margin . top + Math . max ( 40 , Math . round ( this . labelSize * 2.3 ) ) ;
486+ const yTickOffset = - Math . max ( 9 , Math . round ( this . labelSize * 0.9 ) ) ;
487+
488+ const xAxisGroup = this . svg . append ( "g" )
466489 . attr ( "class" , "axis axis--x" )
467- . attr ( "transform" , `translate(${ this . margin . left } , ${ this . height + 5 } )` )
490+ . attr ( "transform" , `translate(${ this . margin . left } , ${ this . height + this . margin . top } )` )
468491 . call ( xAxis )
469- . attr ( "text-anchor" , "center " )
470- . selectAll ( "text" )
471- . attr ( "x " , xLabelOffset ) ;
492+ . attr ( "text-anchor" , "middle " )
493+ . attr ( "font-size" , this . labelSize ) ;
494+ xAxisGroup . selectAll ( "text" ) . attr ( "text-anchor " , "middle" ) ;
472495
473496 this . svg . append ( "g" )
474497 . attr ( "class" , "axis axis--y" )
475- . attr ( "transform" , `translate(${ this . margin . left } , 5 )` )
476- . call ( d3 . axisLeft ( this . y ) )
477- . attr ( "text-anchor " , null )
498+ . attr ( "transform" , `translate(${ this . margin . left } , ${ this . margin . top } )` )
499+ . call ( yAxis )
500+ . attr ( "font-size " , this . labelSize )
478501 . selectAll ( "text" )
479- . attr ( "x" , - 20 ) ;
502+ . attr ( "text-anchor" , "end" )
503+ . attr ( "x" , yTickOffset ) ;
480504
481505 this . svg . append ( "text" )
482506 . attr ( "class" , "x label" )
483- . attr ( "text-anchor" , "center" )
484- . attr ( "x" , this . width / 2 )
485- . attr ( "y" , this . height + 40 )
507+ . attr ( "text-anchor" , "middle" )
508+ . attr ( "font-size" , this . labelSize )
509+ . attr ( "x" , this . margin . left + this . width / 2 )
510+ . attr ( "y" , xLabelY )
486511 . text ( `Date (${ this . widgets [ 'epiCurve-binSize' ] == 'Day' ? 'Dai' : this . widgets [ 'epiCurve-binSize' ] } ly Bins)` ) ;
487512
488513 this . svg . append ( "text" )
489514 . attr ( "class" , "y label" )
490- . attr ( "text-anchor" , "center" )
491- . attr ( "y" , 15 )
492- . attr ( "x" , - this . middle - this . margin . top - this . margin . bottom )
515+ . attr ( "text-anchor" , "middle" )
516+ . attr ( "font-size" , this . labelSize )
517+ . attr ( "y" , Math . max ( 14 , Math . round ( this . labelSize * 0.95 ) ) )
518+ . attr ( "x" , - ( this . margin . top + this . height / 2 ) )
493519 . attr ( "transform" , "rotate(-90)" )
494520 . text ( "Number of Cases" ) ;
495521
@@ -523,54 +549,62 @@ updateAxes() {
523549}
524550
525551generateLegend ( epiCurve , colors , fieldNames ) {
552+ const legendFontSize = Math . max ( 6 , Number ( this . legendLabelSize || 15 ) ) ;
553+ const legendFontSizePx = `${ legendFontSize } px` ;
554+ const markerRadius = Math . max ( 4 , Math . round ( legendFontSize * 0.35 ) ) ;
555+ const markerTextGap = Math . max ( 8 , Math . round ( legendFontSize * 0.65 ) ) ;
556+ const legendRowHeight = Math . max ( 22 , Math . round ( legendFontSize * 1.9 ) ) ;
557+ const legendCharWidth = Math . max ( 5.5 , legendFontSize * 0.55 ) ;
558+ const legendItemGap = Math . max ( 24 , Math . round ( legendFontSize * 1.8 ) ) ;
559+
526560 let xOffset = 50 ; // default for left position
527561 if ( this . widgets [ 'epiCurve-legendPosition' ] == 'Hide' ) {
528562 return ;
529563 } else if ( this . widgets [ 'epiCurve-legendPosition' ] == 'Bottom' ) {
530564 let prevLength = 0 ;
531565 let rowCount = 0 ;
532- let y = this . height + 50 ;
566+ let y = this . height + this . margin . bottom - ( Math . max ( legendRowHeight , Math . round ( this . labelSize * 1.5 ) ) + 14 ) ;
533567 if ( this . selectedGraphType == 'Single Date Field' && this . widgets [ 'epiCurve-stackColorBy' ] == 'Node Color' && this . commonService . session . style . widgets [ 'node-color-variable' ] != 'None' ) {
534568 let field = this . commonService . capitalize ( this . commonService . session . style . widgets [ 'node-color-variable' ] ) ;
535- epiCurve . append ( "text" ) . attr ( "x" , 70 ) . attr ( "y" , y ) . text ( field + ': ' ) . style ( "font-size" , "15px" ) . attr ( "alignment-baseline" , "middle" )
569+ epiCurve . append ( "text" ) . attr ( "x" , 70 ) . attr ( "y" , y ) . text ( field + ': ' ) . style ( "font-size" , legendFontSizePx ) . attr ( "alignment-baseline" , "middle" )
536570 prevLength += field . length + 3 ;
537571 } else if ( this . selectedGraphType == 'Single Date Field' && this . widgets [ 'epiCurve-stackColorBy' ] != 'Node Color' && this . widgets [ 'epiCurve-stackColorBy' ] != 'None' ) {
538- epiCurve . append ( "text" ) . attr ( "x" , 70 ) . attr ( "y" , y ) . text ( this . widgets [ 'epiCurve-stackColorBy' ] + ': ' ) . style ( "font-size" , "15px" ) . attr ( "alignment-baseline" , "middle" )
572+ epiCurve . append ( "text" ) . attr ( "x" , 70 ) . attr ( "y" , y ) . text ( this . widgets [ 'epiCurve-stackColorBy' ] + ': ' ) . style ( "font-size" , legendFontSizePx ) . attr ( "alignment-baseline" , "middle" )
539573 prevLength += this . widgets [ 'epiCurve-stackColorBy' ] . length + 3 ;
540574 }
541575 fieldNames . forEach ( ( name , i ) => {
542576 // this first section calculates the location for each item/name in the legend
543577 let nLength = name == null ? 7 : name . toString ( ) . length
544- let baseX = 70 + 40 * ( rowCount ) + prevLength * 7.5 ;
545- if ( baseX + 24 + nLength * 3 > this . width - 70 ) {
578+ let baseX = 70 + legendItemGap * rowCount + prevLength * legendCharWidth ;
579+ if ( baseX + markerRadius * 2 + markerTextGap + nLength * legendCharWidth > this . width - 70 ) {
546580 rowCount = 0 ;
547- y += 30 ;
581+ y -= legendRowHeight ;
548582 prevLength = 0 ;
549583 baseX = 70 ;
550584 }
551585
552- epiCurve . append ( "circle" ) . attr ( "cx" , baseX ) . attr ( "cy" , y ) . attr ( "r" , 6 ) . style ( "fill" , colors [ i ] )
553- epiCurve . append ( "text" ) . attr ( "x" , baseX + 10 ) . attr ( "y" , y ) . text ( this . commonService . capitalize ( name == null ? '(Empty)' : name . toString ( ) ) ) . style ( "font-size" , "15px" ) . attr ( "alignment-baseline" , "middle" )
586+ epiCurve . append ( "circle" ) . attr ( "cx" , baseX ) . attr ( "cy" , y ) . attr ( "r" , markerRadius ) . style ( "fill" , colors [ i ] )
587+ epiCurve . append ( "text" ) . attr ( "x" , baseX + markerRadius + markerTextGap ) . attr ( "y" , y ) . text ( this . commonService . capitalize ( name == null ? '(Empty)' : name . toString ( ) ) ) . style ( "font-size" , legendFontSizePx ) . attr ( "alignment-baseline" , "middle" )
554588
555589 prevLength += nLength ;
556590 rowCount += 1 ;
557591 } )
558592 return ;
559593 } else if ( this . widgets [ 'epiCurve-legendPosition' ] == 'Right' ) {
560- xOffset = this . width - 120 ;
594+ xOffset = this . width - Math . max ( 120 , Math . round ( legendFontSize * 8 ) ) ;
561595 }
562596 let count = 0 ;
563597 if ( this . selectedGraphType == 'Single Date Field' && this . widgets [ 'epiCurve-stackColorBy' ] == 'Node Color' && this . commonService . session . style . widgets [ 'node-color-variable' ] != 'None' ) {
564598 let field = this . commonService . capitalize ( this . commonService . session . style . widgets [ 'node-color-variable' ] ) ;
565- epiCurve . append ( "text" ) . attr ( "x" , xOffset ) . attr ( "y" , 30 ) . text ( field + ': ' ) . style ( "font-size" , "15px" ) . attr ( "alignment-baseline" , "middle" )
599+ epiCurve . append ( "text" ) . attr ( "x" , xOffset ) . attr ( "y" , legendRowHeight ) . text ( field + ': ' ) . style ( "font-size" , legendFontSizePx ) . attr ( "alignment-baseline" , "middle" )
566600 count += 1 ;
567601 } else if ( this . selectedGraphType == 'Single Date Field' && this . widgets [ 'epiCurve-stackColorBy' ] != 'Node Color' && this . widgets [ 'epiCurve-stackColorBy' ] != 'None' ) {
568- epiCurve . append ( "text" ) . attr ( "x" , xOffset ) . attr ( "y" , 30 ) . text ( this . widgets [ 'epiCurve-stackColorBy' ] + ': ' ) . style ( "font-size" , "15px" ) . attr ( "alignment-baseline" , "middle" )
602+ epiCurve . append ( "text" ) . attr ( "x" , xOffset ) . attr ( "y" , legendRowHeight ) . text ( this . widgets [ 'epiCurve-stackColorBy' ] + ': ' ) . style ( "font-size" , legendFontSizePx ) . attr ( "alignment-baseline" , "middle" )
569603 count += 1 ;
570604 }
571605 fieldNames . forEach ( ( name , i ) => {
572- epiCurve . append ( "circle" ) . attr ( "cx" , xOffset ) . attr ( "cy" , 30 * ( count + 1 ) ) . attr ( "r" , 6 ) . style ( "fill" , colors [ i ] )
573- epiCurve . append ( "text" ) . attr ( "x" , xOffset + 20 ) . attr ( "y" , 30 * ( count + 1 ) ) . text ( this . commonService . capitalize ( name == null ? '(Empty)' : name . toString ( ) ) ) . style ( "font-size" , "15px" ) . attr ( "alignment-baseline" , "middle" )
606+ epiCurve . append ( "circle" ) . attr ( "cx" , xOffset ) . attr ( "cy" , legendRowHeight * ( count + 1 ) ) . attr ( "r" , markerRadius ) . style ( "fill" , colors [ i ] )
607+ epiCurve . append ( "text" ) . attr ( "x" , xOffset + markerRadius + markerTextGap ) . attr ( "y" , legendRowHeight * ( count + 1 ) ) . text ( this . commonService . capitalize ( name == null ? '(Empty)' : name . toString ( ) ) ) . style ( "font-size" , legendFontSizePx ) . attr ( "alignment-baseline" , "middle" )
574608 count += 1 ;
575609 } )
576610}
@@ -719,17 +753,15 @@ updateBins(bins, colorVariable='None', nodeColorKeys=undefined) {
719753
720754/**
721755 *
722- * @return xAxis which is used to determine the interval and label for xAxis ticks and xLabelOffet which determine how much to shift each label
756+ * @return xAxis which is used to determine the interval and label for xAxis ticks
723757 */
724758configureXAxisSettings ( ) {
725759 let xAxis ;
726760 let numberOfDays = d3 . timeDay . count ( this . timeDomainStart , this . timeDomainEnd ) ;
727- let xLabelOffset = - 10 ;
728761 if ( this . widgets [ 'epiCurve-binSize' ] == 'Year' ) {
729762 xAxis = d3 . axisBottom ( this . x ) . ticks ( d3 . timeYear ) . tickFormat ( d3 . timeFormat ( "%Y" ) ) ;
730763 } else if ( numberOfDays < 366 ) {
731764 xAxis = d3 . axisBottom ( this . x ) . ticks ( d3 . timeMonth . every ( this . tickInterval ) ) . tickFormat ( d3 . timeFormat ( "%b %Y" ) )
732- xLabelOffset = - 25 ;
733765 } else if ( this . widgets [ 'epiCurve-binSize' ] == 'Quarter' ) {
734766 xAxis = d3 . axisBottom ( this . x )
735767 . ticks ( d3 . timeMonth . every ( this . tickInterval < 3 ? this . tickInterval * 3 : 12 ) )
@@ -739,7 +771,7 @@ configureXAxisSettings() {
739771 . ticks ( d3 . timeMonth . every ( this . tickInterval ) )
740772 . tickFormat ( ( d : Date ) => d <= d3 . timeYear ( d ) ? d . getFullYear ( ) . toString ( ) : null ) ;
741773 }
742- return [ xAxis , xLabelOffset ]
774+ return xAxis ;
743775}
744776
745777goldenLayoutComponentResize ( ) {
@@ -817,11 +849,11 @@ onNodeColorChanged() {
817849}
818850
819851onLegendPositionChange ( ) {
820- if ( this . widgets [ 'epiCurve-legendPosition' ] == 'Bottom' ) {
821- this . margin . bottom = 100 ;
822- } else {
823- this . margin . bottom = 50 ;
824- }
852+ this . refresh ( ) ;
853+ }
854+
855+ onLegendLabelSizeChange ( ) {
856+ this . legendLabelSize = Number ( this . legendLabelSize ) ;
825857 this . refresh ( ) ;
826858}
827859
@@ -1092,4 +1124,4 @@ onFilterDataChange() {
10921124
10931125export namespace TimelineComponent {
10941126 export const componentTypeName = 'Epi Curve' ;
1095- }
1127+ }
0 commit comments