@@ -3,6 +3,10 @@ interface ChartData {
33 datasets : { label : string ; data : ( number | null ) [ ] } [ ] ;
44}
55
6+ const isSmallScreen = window . matchMedia (
7+ "(pointer: coarse) and (max-width: 768px)" ,
8+ ) . matches ;
9+
610const colors = [
711 "#08c" ,
812 "#dc3545" ,
@@ -45,6 +49,112 @@ function getTooltipElement(chart: {
4549 return el ;
4650}
4751
52+ function showTooltip ( el : HTMLDivElement , html : string , x : number , y : number ) {
53+ el . innerHTML = html ;
54+ el . style . opacity = "1" ;
55+ el . style . left = `${ x } px` ;
56+ el . style . top = `${ y } px` ;
57+ }
58+
59+ function barTooltipHandler ( {
60+ chart,
61+ tooltip,
62+ } : {
63+ chart : { canvas : HTMLCanvasElement } ;
64+ tooltip : {
65+ opacity : number ;
66+ dataPoints : { raw : unknown } [ ] ;
67+ caretX : number ;
68+ caretY : number ;
69+ } ;
70+ } ) {
71+ const el = getTooltipElement ( chart ) ;
72+ if ( tooltip . opacity === 0 ) {
73+ el . style . opacity = "0" ;
74+ return ;
75+ }
76+ const value = ( tooltip . dataPoints [ 0 ] . raw as number ) . toFixed ( 2 ) ;
77+ showTooltip ( el , value , tooltip . caretX , tooltip . caretY ) ;
78+ }
79+
80+ function lineTooltipHandler ( {
81+ chart,
82+ tooltip,
83+ } : {
84+ chart : { canvas : HTMLCanvasElement } ;
85+ tooltip : {
86+ opacity : number ;
87+ title : string [ ] ;
88+ dataPoints : {
89+ raw : unknown ;
90+ datasetIndex : number ;
91+ dataset : { label ?: string } ;
92+ } [ ] ;
93+ caretX : number ;
94+ caretY : number ;
95+ } ;
96+ } ) {
97+ const el = getTooltipElement ( chart ) ;
98+ if ( tooltip . opacity === 0 ) {
99+ el . style . opacity = "0" ;
100+ return ;
101+ }
102+
103+ const rows = tooltip . dataPoints
104+ . map ( ( item ) => {
105+ const color = colors [ item . datasetIndex % colors . length ] ;
106+ return `<tr>
107+ <td style="color:${ color } ">●</td>
108+ <td>${ item . dataset . label } </td>
109+ <td>${ ( item . raw as number ) . toFixed ( 2 ) } </td>
110+ </tr>` ;
111+ } )
112+ . join ( "" ) ;
113+
114+ showTooltip (
115+ el ,
116+ `<div class="chart-tooltip-title">${ renderYearMonth ( tooltip . title [ 0 ] ) } </div><table>${ rows } </table>` ,
117+ tooltip . caretX ,
118+ tooltip . caretY ,
119+ ) ;
120+ }
121+
122+ function generateLegendLabels ( textColor : string , gridColor : string ) {
123+ return ( chart : {
124+ data : { datasets : { label ?: string } [ ] } ;
125+ isDatasetVisible ( index : number ) : boolean ;
126+ } ) => {
127+ return chart . data . datasets . map ( ( ds , i ) => {
128+ const hidden = ! chart . isDatasetVisible ( i ) ;
129+ const color = colors [ i % colors . length ] ;
130+ return {
131+ text : ds . label ?? "" ,
132+ fontColor : hidden ? gridColor : textColor ,
133+ fillStyle : hidden ? "transparent" : color ,
134+ strokeStyle : hidden ? gridColor : color ,
135+ lineWidth : 1 ,
136+ hidden : false ,
137+ datasetIndex : i ,
138+ } ;
139+ } ) ;
140+ } ;
141+ }
142+
143+ const legendPaddingPlugin = {
144+ id : "legendPadding" ,
145+ beforeInit ( chart : { legend ?: { fit ( ) : void ; height : number } } ) {
146+ const legend = chart . legend ;
147+ if ( ! legend ) {
148+ return ;
149+ }
150+ const originalFit = legend . fit . bind ( legend ) ;
151+ legend . fit = function ( ) {
152+ originalFit ( ) ;
153+ this . height += 16 ;
154+ } ;
155+ } ,
156+ } ;
157+
48158class PopularityChart extends HTMLElement {
49159 connectedCallback ( ) {
50160 const script = this . querySelector ( 'script[type="application/json"]' ) ;
@@ -127,51 +237,24 @@ class PopularityChart extends HTMLElement {
127237 animation : false ,
128238 maintainAspectRatio : false ,
129239 plugins : {
130- legend : {
131- display : false ,
132- } ,
240+ legend : { display : false } ,
133241 tooltip : {
134242 enabled : false ,
135- external : ( { chart, tooltip } ) => {
136- const el = getTooltipElement ( chart ) ;
137-
138- if ( tooltip . opacity === 0 ) {
139- el . style . opacity = "0" ;
140- return ;
141- }
142-
143- const item = tooltip . dataPoints [ 0 ] ;
144- el . innerHTML = ( item . raw as number ) . toFixed ( 2 ) ;
145- el . style . opacity = "1" ;
146- el . style . left = `${ tooltip . caretX } px` ;
147- el . style . top = `${ tooltip . caretY } px` ;
148- } ,
243+ external : isSmallScreen ? undefined : barTooltipHandler ,
149244 } ,
150245 } ,
151246 scales : {
152247 x : {
153248 min : 0 ,
154249 max : 100 ,
155- border : {
156- color : gridColor ,
157- } ,
158- grid : {
159- color : gridColor ,
160- } ,
161- ticks : {
162- color : textColor ,
163- } ,
250+ border : { color : gridColor } ,
251+ grid : { color : gridColor } ,
252+ ticks : { color : textColor } ,
164253 } ,
165254 y : {
166- border : {
167- color : gridColor ,
168- } ,
169- grid : {
170- display : false ,
171- } ,
172- ticks : {
173- color : textColor ,
174- } ,
255+ border : { color : gridColor } ,
256+ grid : { display : false } ,
257+ ticks : { color : textColor } ,
175258 } ,
176259 } ,
177260 } ,
@@ -208,58 +291,41 @@ class PopularityChart extends HTMLElement {
208291 new Chart ( canvas , {
209292 type : "line" ,
210293 data,
294+ plugins : [ legendPaddingPlugin ] ,
211295 options : {
212296 animation : false ,
213297 maintainAspectRatio : false ,
214- interaction : {
215- mode : "index" ,
216- intersect : false ,
217- } ,
298+ interaction : isSmallScreen
299+ ? undefined
300+ : { mode : "index" as const , intersect : false } ,
218301 plugins : {
219302 tooltip : {
220303 enabled : false ,
221- itemSort : ( a , b ) =>
222- ( b . raw as number ) - ( a . raw as number ) ,
223- external : ( { chart, tooltip } ) => {
224- const el = getTooltipElement ( chart ) ;
225-
226- if ( tooltip . opacity === 0 ) {
227- el . style . opacity = "0" ;
228- return ;
229- }
230-
231- const rows = tooltip . dataPoints
232- . map ( ( item ) => {
233- const color =
234- colors [
235- item . datasetIndex % colors . length
236- ] ;
237- return `<tr>
238- <td style="color:${ color } ">●</td>
239- <td>${ item . dataset . label } </td>
240- <td>${ ( item . raw as number ) . toFixed ( 2 ) } </td>
241- </tr>` ;
242- } )
243- . join ( "" ) ;
244-
245- el . innerHTML = `<div class="chart-tooltip-title">${ renderYearMonth ( tooltip . title [ 0 ] ) } </div><table>${ rows } </table>` ;
246- el . style . opacity = "1" ;
247- el . style . left = `${ tooltip . caretX } px` ;
248- el . style . top = `${ tooltip . caretY } px` ;
249- } ,
304+ itemSort : isSmallScreen
305+ ? undefined
306+ : ( a , b ) => ( b . raw as number ) - ( a . raw as number ) ,
307+ external : isSmallScreen
308+ ? undefined
309+ : lineTooltipHandler ,
250310 } ,
251311 legend : {
312+ align : isSmallScreen ? "start" : "center" ,
252313 labels : {
253314 color : textColor ,
315+ boxWidth : 11 ,
316+ boxHeight : 11 ,
317+ font : isSmallScreen ? { size : 11 } : undefined ,
318+ generateLabels : generateLegendLabels (
319+ textColor ,
320+ gridColor ,
321+ ) ,
254322 } ,
255323 } ,
256324 } ,
257325 normalized : true ,
258326 scales : {
259327 x : {
260- border : {
261- color : gridColor ,
262- } ,
328+ border : { color : gridColor } ,
263329 ticks : {
264330 callback ( val ) {
265331 return renderYearMonth (
@@ -269,32 +335,24 @@ class PopularityChart extends HTMLElement {
269335 color : textColor ,
270336 autoSkipPadding : 30 ,
271337 } ,
272- grid : {
273- display : false ,
274- color : gridColor ,
275- } ,
338+ grid : { display : false , color : gridColor } ,
276339 } ,
277340 y : {
278341 type : "linear" ,
279342 min : 0 ,
280- border : {
281- color : gridColor ,
282- } ,
283- grid : {
284- color : gridColor ,
285- } ,
286- ticks : {
287- color : textColor ,
288- } ,
343+ border : { color : gridColor } ,
344+ grid : { color : gridColor } ,
345+ ticks : { color : textColor } ,
289346 } ,
290347 } ,
291348 elements : {
292349 line : {
293350 borderColor : colors ,
351+ borderWidth : isSmallScreen ? 1.5 : 3 ,
294352 } ,
295353 point : {
296354 radius : 0 ,
297- hoverRadius : 4 ,
355+ hoverRadius : isSmallScreen ? 0 : 4 ,
298356 hoverBackgroundColor : textColor ,
299357 } ,
300358 } ,
0 commit comments