@@ -16,86 +16,97 @@ const ZERO_WIDTH_ZERO: string = "\u200b";
1616const ZERO_WIDTH_ONE : string = "\u200c" ;
1717const ZERO_WIDTH_SENTINEL : string = "\u200d" ;
1818
19- export const calculateLabelStep = ( count : number ) : number => {
20- if ( count <= 2 ) {
21- return 1 ;
19+ export class Renderer {
20+ public static calculateLabelStep ( count : number ) : number {
21+ if ( count <= 2 ) {
22+ return 1 ;
23+ }
24+
25+ const totalGapWidth : number = CHART_WIDTH_PX - TICK_WIDTH_PX * count ;
26+ if ( totalGapWidth <= 0 ) {
27+ return count ;
28+ }
29+
30+ const numerator : number = REQUIRED_GAP_PX * ( count - 1 ) ;
31+ return Math . max ( 1 , Math . ceil ( numerator / totalGapWidth ) ) ;
2232 }
2333
24- const totalGapWidth : number = CHART_WIDTH_PX - TICK_WIDTH_PX * count ;
25- if ( totalGapWidth <= 0 ) {
26- return count ;
34+ private static encodeHiddenLabel ( index : number ) : string {
35+ const binary : string = index . toString ( 2 ) ;
36+ return (
37+ ZERO_WIDTH_SENTINEL +
38+ binary
39+ . split ( "" )
40+ . map ( ( digit : string ) : string =>
41+ digit === "0" ? ZERO_WIDTH_ZERO : ZERO_WIDTH_ONE ,
42+ )
43+ . join ( "" )
44+ ) ;
2745 }
2846
29- const numerator : number = REQUIRED_GAP_PX * ( count - 1 ) ;
30- return Math . max ( 1 , Math . ceil ( numerator / totalGapWidth ) ) ;
31- } ;
32-
33- const encodeHiddenLabel = ( index : number ) : string => {
34- const binary : string = index . toString ( 2 ) ;
35- return (
36- ZERO_WIDTH_SENTINEL +
37- binary
38- . split ( "" )
39- . map ( ( digit : string ) : string =>
40- digit === "0" ? ZERO_WIDTH_ZERO : ZERO_WIDTH_ONE ,
41- )
42- . join ( "" )
43- ) ;
44- } ;
47+ private static formatTimeLabels (
48+ times : z . TypeOf < typeof timesSchema > ,
49+ ) : string [ ] {
50+ if ( times . length === 0 ) {
51+ return [ ] ;
52+ }
4553
46- const formatTimeLabels = ( times : z . TypeOf < typeof timesSchema > ) : string [ ] => {
47- if ( times . length === 0 ) {
48- return [ ] ;
49- }
54+ const formattedTimes : string [ ] = times . map ( ( d : Date ) : string =>
55+ d . toLocaleTimeString ( "en-GB" , { hour12 : false } ) ,
56+ ) ;
5057
51- const formattedTimes : string [ ] = times . map ( ( d : Date ) : string =>
52- d . toLocaleTimeString ( "en-GB" , { hour12 : false } ) ,
53- ) ;
58+ const labelStep : number = Renderer . calculateLabelStep (
59+ formattedTimes . length ,
60+ ) ;
61+ if ( labelStep <= 1 || formattedTimes . length <= 2 ) {
62+ return formattedTimes ;
63+ }
5464
55- const labelStep : number = calculateLabelStep ( formattedTimes . length ) ;
56- if ( labelStep <= 1 || formattedTimes . length <= 2 ) {
57- return formattedTimes ;
58- }
65+ const lastIndex : number = formattedTimes . length - 1 ;
66+ const totalInteriorPositions : number = Math . max (
67+ formattedTimes . length - 2 ,
68+ 0 ,
69+ ) ;
70+ const estimatedInteriorLabels : number = Math . max (
71+ Math . floor ( lastIndex / labelStep ) - 1 ,
72+ 0 ,
73+ ) ;
74+ const usableInteriorLabels : number = Math . min (
75+ totalInteriorPositions ,
76+ estimatedInteriorLabels ,
77+ ) ;
5978
60- const lastIndex : number = formattedTimes . length - 1 ;
61- const totalInteriorPositions : number = Math . max ( formattedTimes . length - 2 , 0 ) ;
62- const estimatedInteriorLabels : number = Math . max (
63- Math . floor ( lastIndex / labelStep ) - 1 ,
64- 0 ,
65- ) ;
66- const usableInteriorLabels : number = Math . min (
67- totalInteriorPositions ,
68- estimatedInteriorLabels ,
69- ) ;
70-
71- const visibleInteriorIndices : Set < number > = new Set < number > ( ) ;
72- if ( usableInteriorLabels > 0 ) {
73- const spacing : number = totalInteriorPositions / ( usableInteriorLabels + 1 ) ;
74- for ( let slot : number = 1 ; slot <= usableInteriorLabels ; slot += 1 ) {
75- const targetIndex : number = 1 + Math . round ( slot * spacing ) ;
76- let clamped : number = Math . min ( lastIndex - 1 , Math . max ( 1 , targetIndex ) ) ;
77- while ( visibleInteriorIndices . has ( clamped ) && clamped < lastIndex - 1 ) {
78- clamped += 1 ;
79+ const visibleInteriorIndices : Set < number > = new Set < number > ( ) ;
80+ if ( usableInteriorLabels > 0 ) {
81+ const spacing : number =
82+ totalInteriorPositions / ( usableInteriorLabels + 1 ) ;
83+ for ( let slot : number = 1 ; slot <= usableInteriorLabels ; slot += 1 ) {
84+ const targetIndex : number = 1 + Math . round ( slot * spacing ) ;
85+ let clamped : number = Math . min ( lastIndex - 1 , Math . max ( 1 , targetIndex ) ) ;
86+ while (
87+ visibleInteriorIndices . has ( clamped ) &&
88+ clamped < lastIndex - 1
89+ ) {
90+ clamped += 1 ;
91+ }
92+ visibleInteriorIndices . add ( clamped ) ;
7993 }
80- visibleInteriorIndices . add ( clamped ) ;
8194 }
82- }
8395
84- return formattedTimes . map (
85- ( label : string , index : number , array : string [ ] ) : string => {
86- if (
87- index === 0 ||
88- index === array . length - 1 ||
89- visibleInteriorIndices . has ( index )
90- ) {
91- return label ;
92- }
93- return encodeHiddenLabel ( index ) ;
94- } ,
95- ) ;
96- } ;
96+ return formattedTimes . map (
97+ ( label : string , index : number , array : string [ ] ) : string => {
98+ if (
99+ index === 0 ||
100+ index === array . length - 1 ||
101+ visibleInteriorIndices . has ( index )
102+ ) {
103+ return label ;
104+ }
105+ return Renderer . encodeHiddenLabel ( index ) ;
106+ } ,
107+ ) ;
108+ }
97109
98- export class Renderer {
99110 render (
100111 renderParamsList : z . TypeOf < typeof renderParamsListSchema > ,
101112 metricsID : string ,
@@ -133,7 +144,7 @@ ${charts}`;
133144 }
134145
135146 private formatTimes ( times : z . TypeOf < typeof timesSchema > ) : string {
136- return JSON . stringify ( formatTimeLabels ( times ) ) ;
147+ return JSON . stringify ( Renderer . formatTimeLabels ( times ) ) ;
137148 }
138149
139150 private formatYAxisRange ( range ?: string ) : string {
0 commit comments