1
1
'use client' ;
2
2
3
3
import chroma from 'chroma-js' ;
4
- import type { Day as WeekDay } from 'date-fns' ;
5
4
import { getYear , parseISO } from 'date-fns' ;
6
5
import {
7
6
type CSSProperties ,
@@ -19,20 +18,16 @@ import type {
19
18
Activity ,
20
19
BlockElement ,
21
20
Color ,
21
+ DayIndex ,
22
+ DayName ,
22
23
EventHandlerMap ,
23
24
Labels ,
24
25
ReactEvent ,
25
26
SVGRectEventHandler ,
26
27
ThemeInput ,
27
- Week ,
28
28
} from '../types' ;
29
- import {
30
- generateEmptyData ,
31
- getClassName ,
32
- getMonthLabels ,
33
- groupByWeeks ,
34
- maxWeekdayLabelLength ,
35
- } from '../utils/calendar' ;
29
+ import { generateEmptyData , getClassName , groupByWeeks , range } from '../utils/calendar' ;
30
+ import { getMonthLabels , initWeekdayLabels , maxWeekdayLabelWidth } from '../utils/label' ;
36
31
import { createTheme } from '../utils/theme' ;
37
32
38
33
export interface Props {
@@ -127,8 +122,10 @@ export interface Props {
127
122
renderColorLegend ?: ( block : BlockElement , level : number ) => ReactElement ;
128
123
/**
129
124
* Toggle to show weekday labels left to the calendar.
125
+ * Alternatively, pass a list of ISO 8601 weekday names to show.
126
+ * For example `['mon', 'wed', 'fri']`.
130
127
*/
131
- showWeekdayLabels ?: boolean ;
128
+ showWeekdayLabels ?: boolean | Array < DayName > ;
132
129
/**
133
130
* Style object to pass to component container.
134
131
*/
@@ -164,7 +161,7 @@ export interface Props {
164
161
/**
165
162
* Index of day to be used as start of week. 0 represents Sunday.
166
163
*/
167
- weekStart ?: WeekDay ;
164
+ weekStart ?: DayIndex ;
168
165
}
169
166
170
167
const ActivityCalendar = forwardRef < HTMLElement , Props > (
@@ -212,13 +209,13 @@ const ActivityCalendar = forwardRef<HTMLElement, Props>(
212
209
const firstActivity = activities [ 0 ] as Activity ;
213
210
const year = getYear ( parseISO ( firstActivity . date ) ) ;
214
211
const weeks = groupByWeeks ( activities , weekStart ) ;
215
- const firstWeek = weeks [ 0 ] as Week ;
216
212
217
213
const labels = Object . assign ( { } , DEFAULT_LABELS , labelsProp ) ;
218
214
const labelHeight = hideMonthLabels ? 0 : fontSize + LABEL_MARGIN ;
219
215
220
- const weekdayLabelOffset = showWeekdayLabels
221
- ? maxWeekdayLabelLength ( firstWeek , weekStart , labels . weekdays , fontSize ) + LABEL_MARGIN
216
+ const weekdayLabels = initWeekdayLabels ( showWeekdayLabels , weekStart ) ;
217
+ const weekdayLabelOffset = weekdayLabels . shouldShow
218
+ ? maxWeekdayLabelWidth ( labels . weekdays , weekdayLabels , fontSize ) + LABEL_MARGIN
222
219
: undefined ;
223
220
224
221
function getDimensions ( ) {
@@ -323,74 +320,75 @@ const ActivityCalendar = forwardRef<HTMLElement, Props>(
323
320
{ ! loading && ! hideColorLegend && (
324
321
< div className = { getClassName ( 'legend-colors' , styles . legendColors ) } >
325
322
< span style = { { marginRight : '0.4em' } } > { labels . legend . less } </ span >
326
- { Array ( maxLevel + 1 )
327
- . fill ( undefined )
328
- . map ( ( _ , level ) => {
329
- const block = (
330
- < svg width = { blockSize } height = { blockSize } key = { level } >
331
- < rect
332
- width = { blockSize }
333
- height = { blockSize }
334
- fill = { colorScale [ level ] }
335
- rx = { blockRadius }
336
- ry = { blockRadius }
337
- />
338
- </ svg >
339
- ) ;
340
-
341
- return renderColorLegend ? renderColorLegend ( block , level ) : block ;
342
- } ) }
323
+ { range ( maxLevel + 1 ) . map ( level => {
324
+ const block = (
325
+ < svg width = { blockSize } height = { blockSize } key = { level } >
326
+ < rect
327
+ width = { blockSize }
328
+ height = { blockSize }
329
+ fill = { colorScale [ level ] }
330
+ rx = { blockRadius }
331
+ ry = { blockRadius }
332
+ />
333
+ </ svg >
334
+ ) ;
335
+
336
+ return renderColorLegend ? renderColorLegend ( block , level ) : block ;
337
+ } ) }
343
338
< span style = { { marginLeft : '0.4em' } } > { labels . legend . more } </ span >
344
339
</ div >
345
340
) }
346
341
</ footer >
347
342
) ;
348
343
}
349
344
350
- function renderLabels ( ) {
351
- if ( ! showWeekdayLabels && hideMonthLabels ) {
345
+ function renderWeekdayLabels ( ) {
346
+ if ( ! weekdayLabels . shouldShow ) {
352
347
return null ;
353
348
}
354
349
355
350
return (
356
- < >
357
- { showWeekdayLabels && weeks [ 0 ] && (
358
- < g className = { getClassName ( 'legend-weekday' ) } >
359
- { weeks [ 0 ] . map ( ( _ , index ) => {
360
- if ( index % 2 === 0 ) {
361
- return null ;
362
- }
363
-
364
- const dayIndex = ( index + weekStart ) % 7 ;
365
-
366
- return (
367
- < text
368
- x = { - LABEL_MARGIN }
369
- y = { labelHeight + ( blockSize + blockMargin ) * index + blockSize / 2 }
370
- dominantBaseline = "central"
371
- textAnchor = "end"
372
- key = { index }
373
- >
374
- { labels . weekdays [ dayIndex ] }
375
- </ text >
376
- ) ;
377
- } ) }
378
- </ g >
379
- ) }
380
- { ! hideMonthLabels && (
381
- < g className = { getClassName ( 'legend-month' ) } >
382
- { getMonthLabels ( weeks , labels . months ) . map ( ( { label, weekIndex } ) => (
383
- < text
384
- x = { ( blockSize + blockMargin ) * weekIndex }
385
- dominantBaseline = "hanging"
386
- key = { weekIndex }
387
- >
388
- { label }
389
- </ text >
390
- ) ) }
391
- </ g >
392
- ) }
393
- </ >
351
+ < g className = { getClassName ( 'legend-weekday' ) } >
352
+ { range ( 7 ) . map ( index => {
353
+ const dayIndex = ( ( index + weekStart ) % 7 ) as DayIndex ;
354
+
355
+ if ( ! weekdayLabels . byDayIndex ( dayIndex ) ) {
356
+ return null ;
357
+ }
358
+
359
+ return (
360
+ < text
361
+ x = { - LABEL_MARGIN }
362
+ y = { labelHeight + ( blockSize + blockMargin ) * index + blockSize / 2 }
363
+ dominantBaseline = "central"
364
+ textAnchor = "end"
365
+ key = { index }
366
+ >
367
+ { labels . weekdays [ dayIndex ] }
368
+ </ text >
369
+ ) ;
370
+ } ) }
371
+ </ g >
372
+ ) ;
373
+ }
374
+
375
+ function renderMonthLabels ( ) {
376
+ if ( hideMonthLabels ) {
377
+ return null ;
378
+ }
379
+
380
+ return (
381
+ < g className = { getClassName ( 'legend-month' ) } >
382
+ { getMonthLabels ( weeks , labels . months ) . map ( ( { label, weekIndex } ) => (
383
+ < text
384
+ x = { ( blockSize + blockMargin ) * weekIndex }
385
+ dominantBaseline = "hanging"
386
+ key = { weekIndex }
387
+ >
388
+ { label }
389
+ </ text >
390
+ ) ) }
391
+ </ g >
394
392
) ;
395
393
}
396
394
@@ -422,7 +420,8 @@ const ActivityCalendar = forwardRef<HTMLElement, Props>(
422
420
className = { getClassName ( 'calendar' , styles . calendar ) }
423
421
style = { { marginLeft : weekdayLabelOffset } }
424
422
>
425
- { ! loading && renderLabels ( ) }
423
+ { ! loading && renderWeekdayLabels ( ) }
424
+ { ! loading && renderMonthLabels ( ) }
426
425
{ renderCalendar ( ) }
427
426
</ svg >
428
427
</ div >
0 commit comments