diff --git a/src/app/calendarOverviewCard.tsx b/src/app/calendarOverviewCard.tsx index ab68d7e..a778221 100644 --- a/src/app/calendarOverviewCard.tsx +++ b/src/app/calendarOverviewCard.tsx @@ -5,11 +5,12 @@ import useSWR from "swr"; import {fetcher} from "@/app/utils"; import * as R from 'ramda'; import * as dFns from 'date-fns'; -// import CalendarHeatmap from 'react-calendar-heatmap'; -import CalendarHeatmap from '../heatmap'; +import CalendarHeatmap from 'react-calendar-heatmap'; +// import CalendarHeatmap from '../heatmap'; import 'react-calendar-heatmap/dist/styles.css'; import './calendar-styles.css' import { Tooltip } from 'react-tooltip'; +import {MyHeatMap} from "@/heatmap/b"; const initialDate = dFns.parseISO('2023-12-15T00:00:00.000Z') const endDate = dFns.parseISO('2024-01-25T00:00:00.000Z') diff --git a/src/heatmap/b.tsx b/src/heatmap/b.tsx deleted file mode 100644 index 7218ec6..0000000 --- a/src/heatmap/b.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import * as dFns from 'date-fns'; -import {CSS_PSEDUO_NAMESPACE} from "@/heatmap/index"; - -export function MonthLabels({ - startDate, - endDate, - getMonthLabelCoordinates - }: { - startDate: Date; - endDate: Date; - getMonthLabelCoordinates: (month: Date) => { - x: number; - y: number - }; -}) { - // We render a label for each month in the interval if its first week is in the interval - const firstWeeks = dFns.eachWeekOfInterval({ - start: startDate, - end: endDate, - }).filter(date => date.getDate() <= 7); - - return ( - <> - {firstWeeks.map((month, i) => { - const {x, y} = getMonthLabelCoordinates(month); - - return ( - {dFns.format(month, 'MMM')} - ); - })} - - ); -} diff --git a/src/heatmap/constants.ts b/src/heatmap/constants.ts deleted file mode 100644 index a70d6ca..0000000 --- a/src/heatmap/constants.ts +++ /dev/null @@ -1,20 +0,0 @@ -export const MILLISECONDS_IN_ONE_DAY = 24 * 60 * 60 * 1000; - -export const DAYS_IN_WEEK = 7; - -export const MONTH_LABELS = [ - 'Jan', - 'Feb', - 'Mar', - 'Apr', - 'May', - 'Jun', - 'Jul', - 'Aug', - 'Sep', - 'Oct', - 'Nov', - 'Dec', -]; - -export const DAY_LABELS = ['', 'Mon', '', 'Wed', '', 'Fri', '']; diff --git a/src/heatmap/helpers.test.ts b/src/heatmap/helpers.test.ts deleted file mode 100644 index 911b363..0000000 --- a/src/heatmap/helpers.test.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { - convertToDate, - dateNDaysAgo, - getBeginningTimeForDate, - getRange, - shiftDate, -} from './helpers'; - -import {describe, it, expect} from "@jest/globals"; - -describe('shiftDate', () => { - it('adds a day to the first day of a month', () => { - const startingDate = new Date(2017, 0, 1); - const expectedDate = new Date(2017, 0, 2); - - expect(shiftDate(startingDate, 1).getTime()).toBe(expectedDate.getTime()); - }); - - it('adds multiple days to the first day of a month', () => { - const startingDate = new Date(2017, 0, 1); - const expectedDate = new Date(2017, 0, 3); - - expect(shiftDate(startingDate, 2).getTime()).toBe(expectedDate.getTime()); - }); - - it('subtracts a day from the first day of a month', () => { - const startingDate = new Date(2017, 0, 1); - const expectedDate = new Date(2016, 11, 31); - - expect(shiftDate(startingDate, -1).getTime()).toBe(expectedDate.getTime()); - }); - - it('subtracts multiple days from the first day of a month', () => { - const startingDate = new Date(2017, 0, 1); - const expectedDate = new Date(2016, 11, 30); - - expect(shiftDate(startingDate, -2).getTime()).toBe(expectedDate.getTime()); - }); - - it('adds a day to a non-first day of a month', () => { - const startingDate = new Date(2017, 0, 2); - const expectedDate = new Date(2017, 0, 3); - - expect(shiftDate(startingDate, 1).getTime()).toBe(expectedDate.getTime()); - }); - - it('adds multiple days to a non-first day of a month', () => { - const startingDate = new Date(2017, 0, 2); - const expectedDate = new Date(2017, 0, 4); - - expect(shiftDate(startingDate, 2).getTime()).toBe(expectedDate.getTime()); - }); - - it('subtracts a day from a non-first day of a month', () => { - const startingDate = new Date(2017, 0, 2); - const expectedDate = new Date(2017, 0, 1); - - expect(shiftDate(startingDate, -1).getTime()).toBe(expectedDate.getTime()); - }); - - it('subtracts multiple days from a non-first day of a month', () => { - const startingDate = new Date(2017, 0, 2); - const expectedDate = new Date(2016, 11, 31); - - expect(shiftDate(startingDate, -2).getTime()).toBe(expectedDate.getTime()); - }); -}); - -describe('getBeginningTimeForDate', () => { - it('gets midnight (in the local timezone) on the date passed in', () => { - const inputDate = new Date(2017, 11, 25, 21, 30, 59, 750); - const expectedDate = new Date(2017, 11, 25, 0, 0, 0, 0); - - expect(getBeginningTimeForDate(inputDate).getTime()).toBe(expectedDate.getTime()); - }); -}); - -describe('convertToDate', () => { - it('interprets an "ISO-8601 date-only" string as UTC and converts it into a Date object representing the first millisecond on that date', () => { - const iso8601DateOnlyString = '2017-07-14'; - const expectedDate = new Date(Date.UTC(2017, 6, 14, 0, 0, 0, 0)); - - expect(convertToDate(iso8601DateOnlyString).getTime()).toBe(expectedDate.getTime()); - }); - - it('interprets a millisecond timestamp integer as UTC and converts it into a Date object representing that same millisecond', () => { - const msTimestamp = 1500000001234; // Friday, July 14, 2017 2:40:01.234 AM, according to https://epochconverter.com - const expectedDate = new Date(Date.UTC(2017, 6, 14, 2, 40, 1, 234)); - - expect(convertToDate(msTimestamp).getTime()).toBe(expectedDate.getTime()); - }); - - it('returns the same Date object it receives', () => { - const inputDate = new Date(2017, 11, 25, 21, 30, 59, 750); - const originalTimestamp = inputDate.getTime(); - - expect(convertToDate(inputDate)).toBe(inputDate); - expect(convertToDate(inputDate).getTime()).toBe(originalTimestamp); - }); -}); - -describe('dateNDaysAgo', () => { - it('crosses month boundaries in the negative direction', () => { - const numDays = 32; - const startingDate = new Date(); - const expectedDate = new Date(startingDate.getTime()); - expectedDate.setDate(startingDate.getDate() - numDays); - - const expectedYear = expectedDate.getFullYear(); - const expectedMonth = expectedDate.getMonth(); - const expectedDay = expectedDate.getDate(); - - expect(dateNDaysAgo(numDays).getFullYear()).toBe(expectedYear); - expect(dateNDaysAgo(numDays).getMonth()).toBe(expectedMonth); - expect(dateNDaysAgo(numDays).getDate()).toBe(expectedDay); - }); - - it('crosses month boundaries in the positive direction', () => { - const numDays = -32; - const startingDate = new Date(); - const expectedDate = new Date(startingDate.getTime()); - expectedDate.setDate(startingDate.getDate() - numDays); - - const expectedYear = expectedDate.getFullYear(); - const expectedMonth = expectedDate.getMonth(); - const expectedDay = expectedDate.getDate(); - - expect(dateNDaysAgo(numDays).getFullYear()).toBe(expectedYear); - expect(dateNDaysAgo(numDays).getMonth()).toBe(expectedMonth); - expect(dateNDaysAgo(numDays).getDate()).toBe(expectedDay); - }); - - it('crosses year boundaries in the negative direction', () => { - const numDays = 366; - const startingDate = new Date(); - const expectedDate = new Date(startingDate.getTime()); - expectedDate.setDate(startingDate.getDate() - numDays); - - const expectedYear = expectedDate.getFullYear(); - const expectedMonth = expectedDate.getMonth(); - const expectedDay = expectedDate.getDate(); - - expect(dateNDaysAgo(numDays).getFullYear()).toBe(expectedYear); - expect(dateNDaysAgo(numDays).getMonth()).toBe(expectedMonth); - expect(dateNDaysAgo(numDays).getDate()).toBe(expectedDay); - }); - - it('crosses year boundaries in the positive direction', () => { - const numDays = -366; - const startingDate = new Date(); - const expectedDate = new Date(startingDate.getTime()); - expectedDate.setDate(startingDate.getDate() - numDays); - - const expectedYear = expectedDate.getFullYear(); - const expectedMonth = expectedDate.getMonth(); - const expectedDay = expectedDate.getDate(); - - expect(dateNDaysAgo(numDays).getFullYear()).toBe(expectedYear); - expect(dateNDaysAgo(numDays).getMonth()).toBe(expectedMonth); - expect(dateNDaysAgo(numDays).getDate()).toBe(expectedDay); - }); -}); - -describe('getRange', () => { - it('generates an empty array', () => { - expect(getRange(0)).toEqual([]); - }); - - it('generates an array containing one integer', () => { - expect(getRange(1)).toEqual([0]); - }); - - it('generates an array containing multiple integers', () => { - expect(getRange(5)).toEqual([0, 1, 2, 3, 4]); - }); -}); diff --git a/src/heatmap/helpers.ts b/src/heatmap/helpers.ts deleted file mode 100644 index 5c21bc9..0000000 --- a/src/heatmap/helpers.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as dFns from 'date-fns'; - -// returns a new date shifted a certain number of days (can be negative) -export function shiftDate(date: Date, numDays: number): Date { - return dFns.addDays(date, numDays); -} - -export function getBeginningTimeForDate(date: Date): Date { - return dFns.startOfDay(date); -} - -// obj can be a parseable string, a millisecond timestamp, or a Date object -export function convertToDate(obj: Date | string | number): Date { - return obj instanceof Date ? obj : new Date(obj); -} - -export function dateNDaysAgo(numDaysAgo: number): Date { - return shiftDate(new Date(), -numDaysAgo); -} - -export function getRange(count: number): number[] { - const arr = []; - for (let idx = 0; idx < count; idx += 1) { - arr.push(idx); - } - return arr; -} diff --git a/src/heatmap/index.test.js b/src/heatmap/index.test.js deleted file mode 100644 index 0f8c346..0000000 --- a/src/heatmap/index.test.js +++ /dev/null @@ -1,286 +0,0 @@ -import React from 'react'; -import Enzyme, { shallow } from 'enzyme'; -import Adapter from 'enzyme-adapter-react-16'; - -import CalendarHeatmap from './index'; -import { dateNDaysAgo, shiftDate } from './helpers'; - -Enzyme.configure({ adapter: new Adapter() }); - -const getWrapper = (overrideProps, renderMethod = 'shallow') => { - const defaultProps = { - values: [], - }; - return Enzyme[renderMethod](); -}; - -describe('CalendarHeatmap', () => { - const values = [ - { date: new Date('2017-06-01') }, - { date: new Date('2017-06-02') }, - { date: new Date('2018-06-01') }, - { date: new Date('2018-06-02') }, - { date: new Date('2018-06-03') }, - ]; - - it('should render as an svg', () => { - const wrapper = shallow(); - - expect(wrapper.find('svg')).toHaveLength(1); - }); - - it('should not throw exceptions in base case', () => { - expect(() => ).not.toThrow(); - }); - - it('shows values within its original date range', () => { - const wrapper = shallow( - , - ); - - expect(wrapper.find('.color-filled').length).toBe(2); - }); - - it('should handle string formatted date range', () => { - const wrapper = shallow( - , - ); - - expect(wrapper.find('.color-filled').length).toBe(2); - }); - - it('shows values within an updated date range', () => { - const wrapper = shallow( - , - ); - - wrapper.setProps({ - endDate: new Date('2018-12-31'), - startDate: new Date('2018-01-01'), - }); - - expect(wrapper.find('.color-filled').length).toBe(3); - }); -}); - -describe('CalendarHeatmap props', () => { - it('values', () => { - const values = [ - { date: '2016-01-01' }, - { date: new Date('2016-01-02').getTime() }, - { date: new Date('2016-01-03') }, - ]; - const wrapper = shallow( - , - ); - - // 'values should handle Date/string/number formats' - expect(wrapper.find('.color-filled')).toHaveLength(values.length); - }); - - it('horizontal', () => { - const horizontal = shallow( - , - ); - const [, , horWidth, horHeight] = horizontal.prop('viewBox').split(' '); - // 'horizontal orientation width should be greater than height' - expect(Number(horWidth)).toBeGreaterThan(Number(horHeight)); - - const vertical = shallow( - , - ); - const [, , vertWidth, vertHeight] = vertical.prop('viewBox').split(' '); - // 'vertical orientation width should be less than height' - expect(Number(vertWidth)).toBeLessThan(Number(vertHeight)); - }); - - it('startDate', () => { - const today = new Date(); - const wrapper = shallow(); - - expect( - today.getDate() === - wrapper - .instance() - .getEndDate() - .getDate() && - today.getMonth() === - wrapper - .instance() - .getEndDate() - .getMonth(), - ).toBe(true); - }); - - it('endDate', () => { - const today = new Date(); - const wrapper = shallow( - , - ); - - expect( - today.getDate() === - wrapper - .instance() - .getEndDate() - .getDate() && - today.getMonth() === - wrapper - .instance() - .getEndDate() - .getMonth(), - ).toBe(true); - }); - - it('classForValue', () => { - const today = new Date(); - const numDays = 10; - const expectedStartDate = shiftDate(today, -numDays + 1); - const wrapper = shallow( - (value ? value.count : null)} - classForValue={(value) => { - if (!value) { - return null; - } - return value.count > 0 ? 'red' : 'white'; - }} - />, - ); - - expect(wrapper.find('.white')).toHaveLength(1); - expect(wrapper.find('.red')).toHaveLength(1); - - // TODO these attr selectors might be broken with react 15 - // assert(wrapper.first('rect[title=0]').hasClass('white')); - // assert(wrapper.first('rect[title=1]').hasClass('red')); - }); - - it('showMonthLabels', () => { - const visible = shallow( - , - ); - - expect(visible.find('text').length).toBeGreaterThan(0); - - const hidden = shallow(); - - expect(hidden.find('text')).toHaveLength(0); - }); - - it('showWeekdayLabels', () => { - const visible = shallow( - , - ); - - expect(visible.find('text').length).toBeGreaterThan(2); - - const hidden = shallow( - , - ); - - expect(hidden.find('text')).toHaveLength(0); - - // should display text with .small-text class - // in case if horizontal prop value is false - const vertical = shallow(); - - expect(vertical.find('text.react-calendar-heatmap-small-text')).toHaveLength(3); - }); - - it('transformDayElement', () => { - const transform = (rect) => React.cloneElement(rect, { 'data-test': 'ok' }); - const today = new Date(); - const expectedStartDate = shiftDate(today, -1); - const wrapper = shallow( - , - ); - - expect(wrapper.find('[data-test="ok"]')).toHaveLength(1); - }); - - describe('tooltipDataAttrs', () => { - it('allows a function to be passed', () => { - const today = new Date(); - const numDays = 10; - const expectedStartDate = shiftDate(today, -numDays + 1); - const wrapper = shallow( - ({ - 'data-tooltip': `Count: ${count}`, - })} - />, - ); - - expect(wrapper.find('[data-tooltip="Count: 1"]')).toHaveLength(1); - }); - }); - - describe('event handlers', () => { - const count = 999; - const startDate = '2018-06-01'; - const endDate = '2018-06-03'; - const values = [{ date: '2018-06-02', count }]; - const props = { - values, - startDate, - endDate, - }; - const expectedValue = values[0]; - - it('calls props.onClick with the correct value', () => { - const onClick = jest.fn(); - const wrapper = getWrapper({ ...props, onClick }); - - const rect = wrapper.find('rect').at(0); - rect.simulate('click'); - - expect(onClick).toHaveBeenCalledWith(expectedValue); - }); - - it('calls props.onMouseOver with the correct value', () => { - const onMouseOver = jest.fn(); - const wrapper = getWrapper({ ...props, onMouseOver }); - const fakeEvent = { preventDefault: jest.fn() }; - - const rect = wrapper.find('rect').at(0); - rect.simulate('mouseOver', fakeEvent); - - expect(onMouseOver).toHaveBeenCalledWith(fakeEvent, expectedValue); - }); - - it('calls props.onMouseLeave with the correct value', () => { - const onMouseLeave = jest.fn(); - const wrapper = getWrapper({ ...props, onMouseLeave }); - const fakeEvent = { preventDefault: jest.fn() }; - - const rect = wrapper.find('rect').at(0); - rect.simulate('mouseLeave', fakeEvent); - - expect(onMouseLeave).toHaveBeenCalledWith(fakeEvent, expectedValue); - }); - }); -}); diff --git a/src/heatmap/index.tsx b/src/heatmap/index.tsx deleted file mode 100644 index 62185f2..0000000 --- a/src/heatmap/index.tsx +++ /dev/null @@ -1,386 +0,0 @@ -// @ts-nocheck - -import React, {Fragment} from 'react'; -import PropTypes from 'prop-types'; -import memoizeOne from 'memoize-one'; -import {DAYS_IN_WEEK, MILLISECONDS_IN_ONE_DAY, DAY_LABELS, MONTH_LABELS} from './constants'; -import { - dateNDaysAgo, - shiftDate, - getBeginningTimeForDate, - convertToDate, - getRange, -} from './helpers'; -import * as dFns from 'date-fns'; -import {MonthLabels} from "@/heatmap/b"; - -const SQUARE_SIZE = 10; -const MONTH_LABEL_GUTTER_SIZE = 4; -export const CSS_PSEDUO_NAMESPACE = 'react-calendar-heatmap-'; - -class CalendarHeatmap extends React.Component { - getDateDifferenceInDays() { - const { - startDate, - endDate - } = this.props; - return dFns.differenceInCalendarDays(endDate, startDate); - } - - getSquareSizeWithGutter() { - return SQUARE_SIZE + this.props.gutterSize; - } - - getMonthLabelSize() { - if (!this.props.showMonthLabels) { - return 0; - } - if (this.props.horizontal) { - return SQUARE_SIZE + MONTH_LABEL_GUTTER_SIZE; - } - return 2 * (SQUARE_SIZE + MONTH_LABEL_GUTTER_SIZE); - } - - getWeekdayLabelSize() { - if (!this.props.showWeekdayLabels) { - return 0; - } - if (this.props.horizontal) { - return 30; - } - return SQUARE_SIZE * 1.5; - } - - getStartDate() { - return shiftDate(this.getEndDate(), -this.getDateDifferenceInDays() + 1); // +1 because endDate is inclusive - } - - getEndDate() { - return getBeginningTimeForDate(convertToDate(this.props.endDate)); - } - - getStartDateWithEmptyDays() { - return shiftDate(this.getStartDate(), -this.getNumEmptyDaysAtStart()); - } - - getNumEmptyDaysAtStart() { - return this.getStartDate().getDay(); - } - - getNumEmptyDaysAtEnd() { - return DAYS_IN_WEEK - 1 - this.getEndDate().getDay(); - } - - getWeekCount() { - const numDaysRoundedToWeek = - this.getDateDifferenceInDays() + this.getNumEmptyDaysAtStart() + this.getNumEmptyDaysAtEnd(); - return Math.ceil(numDaysRoundedToWeek / DAYS_IN_WEEK); - } - - getWeekWidth() { - return DAYS_IN_WEEK * this.getSquareSizeWithGutter(); - } - - getWidth() { - return ( - this.getWeekCount() * this.getSquareSizeWithGutter() - - (this.props.gutterSize - this.getWeekdayLabelSize()) - ); - } - - getHeight() { - return ( - this.getWeekWidth() + - (this.getMonthLabelSize() - this.props.gutterSize) + - this.getWeekdayLabelSize() - ); - } - - getValueCache = memoizeOne((props) => - props.values.reduce((memo, value) => { - const date = convertToDate(value.date); - const index = Math.floor((date - this.getStartDateWithEmptyDays()) / MILLISECONDS_IN_ONE_DAY); - // eslint-disable-next-line no-param-reassign - memo[index] = { - value, - className: this.props.classForValue(value), - title: this.props.titleForValue ? this.props.titleForValue(value) : null, - tooltipDataAttrs: this.getTooltipDataAttrsForValue(value), - }; - return memo; - }, {}), - ); - - getValueForIndex(index) { - if (this.valueCache[index]) { - return this.valueCache[index].value; - } - return null; - } - - getClassNameForIndex(index) { - if (this.valueCache[index]) { - return this.valueCache[index].className; - } - return this.props.classForValue(null); - } - - getTitleForIndex(index) { - if (this.valueCache[index]) { - return this.valueCache[index].title; - } - return this.props.titleForValue ? this.props.titleForValue(null) : null; - } - - getTooltipDataAttrsForIndex(index) { - if (this.valueCache[index]) { - return this.valueCache[index].tooltipDataAttrs; - } - return this.getTooltipDataAttrsForValue({ - date: null, - count: null - }); - } - - getTooltipDataAttrsForValue(value) { - const {tooltipDataAttrs} = this.props; - - if (typeof tooltipDataAttrs === 'function') { - return tooltipDataAttrs(value); - } - return tooltipDataAttrs; - } - - getTransformForWeek(weekIndex) { - if (this.props.horizontal) { - return `translate(${weekIndex * this.getSquareSizeWithGutter()}, 0)`; - } - return `translate(0, ${weekIndex * this.getSquareSizeWithGutter()})`; - } - - getTransformForWeekdayLabels() { - if (this.props.horizontal) { - return `translate(${SQUARE_SIZE}, ${this.getMonthLabelSize()})`; - } - return null; - } - - getTransformForMonthLabels() { - if (this.props.horizontal) { - return `translate(${this.getWeekdayLabelSize()}, 0)`; - } - return `translate(${this.getWeekWidth() + - MONTH_LABEL_GUTTER_SIZE}, ${this.getWeekdayLabelSize()})`; - } - - getTransformForAllWeeks() { - if (this.props.horizontal) { - return `translate(${this.getWeekdayLabelSize()}, ${this.getMonthLabelSize()})`; - } - return `translate(0, ${this.getWeekdayLabelSize()})`; - } - - getViewBox() { - if (this.props.horizontal) { - return `0 0 ${this.getWidth()} ${this.getHeight()}`; - } - return `0 0 ${this.getHeight()} ${this.getWidth()}`; - } - - getSquareCoordinates(dayIndex) { - if (this.props.horizontal) { - return [0, dayIndex * this.getSquareSizeWithGutter()]; - } - return [dayIndex * this.getSquareSizeWithGutter(), 0]; - } - - getWeekdayLabelCoordinates(dayIndex) { - if (this.props.horizontal) { - return [0, (dayIndex + 1) * SQUARE_SIZE + dayIndex * this.props.gutterSize]; - } - return [dayIndex * SQUARE_SIZE + dayIndex * this.props.gutterSize, SQUARE_SIZE]; - } - - getMonthLabelCoordinates(weekIndex) { - if (this.props.horizontal) { - return [ - weekIndex * this.getSquareSizeWithGutter(), - this.getMonthLabelSize() - MONTH_LABEL_GUTTER_SIZE, - ]; - } - const verticalOffset = -2; - return [0, (weekIndex + 1) * this.getSquareSizeWithGutter() + verticalOffset]; - } - - handleClick(value) { - if (this.props.onClick) { - this.props.onClick(value); - } - } - - handleMouseOver(e, value) { - if (this.props.onMouseOver) { - this.props.onMouseOver(e, value); - } - } - - handleMouseLeave(e, value) { - if (this.props.onMouseLeave) { - this.props.onMouseLeave(e, value); - } - } - - renderSquare(dayIndex, index) { - const indexOutOfRange = - index < this.getNumEmptyDaysAtStart() || - index >= this.getNumEmptyDaysAtStart() + this.getDateDifferenceInDays(); - if (indexOutOfRange && !this.props.showOutOfRangeDays) { - return null; - } - const [x, y] = this.getSquareCoordinates(dayIndex); - const value = this.getValueForIndex(index); - const rect = ( - // eslint-disable-next-line jsx-a11y/mouse-events-have-key-events - this.handleClick(value)} - onMouseOver={(e) => this.handleMouseOver(e, value)} - onMouseLeave={(e) => this.handleMouseLeave(e, value)} - {...this.getTooltipDataAttrsForIndex(index)} - > - {this.getTitleForIndex(index)} - - ); - const {transformDayElement} = this.props; - return transformDayElement ? transformDayElement(rect, value, index) : rect; - } - - renderWeek(weekIndex) { - return ( - - {getRange(DAYS_IN_WEEK).map((dayIndex) => - this.renderSquare(dayIndex, weekIndex * DAYS_IN_WEEK + dayIndex), - )} - - ); - } - - renderWeekdayLabels() { - if (!this.props.showWeekdayLabels) { - return null; - } - return this.props.weekdayLabels.map((weekdayLabel, dayIndex) => { - const [x, y] = this.getWeekdayLabelCoordinates(dayIndex); - const cssClasses = `${ - this.props.horizontal ? '' : `${CSS_PSEDUO_NAMESPACE}small-text` - } ${CSS_PSEDUO_NAMESPACE}weekday-label`; - // eslint-disable-next-line no-bitwise - return dayIndex & 1 ? ( - - {weekdayLabel} - - ) : null; - }); - } - - render() { - this.valueCache = this.getValueCache(this.props); - - const monthPositionAdapter = (date) => { - const [x, y] = this.getMonthLabelCoordinates(dFns.differenceInWeeks(date, this.props.startDate)); - return { - x, - y - } - }; - - const weeks = dFns.eachWeekOfInterval({ - start: this.props.startDate, - end: this.props.endDate, - }); - - return ( - - - {this.props.showMonthLabels && } - - - {weeks.map((_, index) => this.renderWeek(index))} - - - {this.renderWeekdayLabels()} - - - ); - } -} - -CalendarHeatmap.propTypes = { - values: PropTypes.arrayOf( - PropTypes.shape({ - date: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.instanceOf(Date)]) - .isRequired, - }).isRequired, - ).isRequired, // array of objects with date and arbitrary metadata - numDays: PropTypes.number, // number of days back from endDate to show - startDate: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.instanceOf(Date)]), // start of date range - endDate: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.instanceOf(Date)]), // end of date range - gutterSize: PropTypes.number, // size of space between squares - horizontal: PropTypes.bool, // whether to orient horizontally or vertically - showMonthLabels: PropTypes.bool, // whether to show month labels - showWeekdayLabels: PropTypes.bool, // whether to show weekday labels - showOutOfRangeDays: PropTypes.bool, // whether to render squares for extra days in week after endDate, and before start date - tooltipDataAttrs: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), // data attributes to add to square for setting 3rd party tooltips, e.g. { 'data-toggle': 'tooltip' } for bootstrap tooltips - titleForValue: PropTypes.func, // function which returns title text for value - classForValue: PropTypes.func, // function which returns html class for value - monthLabels: PropTypes.arrayOf(PropTypes.string), // An array with 12 strings representing the text from janurary to december - weekdayLabels: PropTypes.arrayOf(PropTypes.string), // An array with 7 strings representing the text from Sun to Sat - onClick: PropTypes.func, // callback function when a square is clicked - onMouseOver: PropTypes.func, // callback function when mouse pointer is over a square - onMouseLeave: PropTypes.func, // callback function when mouse pointer is left a square - transformDayElement: PropTypes.func, // function to further transform the svg element for a single day -}; - -CalendarHeatmap.defaultProps = { - numDays: null, - startDate: dateNDaysAgo(200), - endDate: new Date(), - gutterSize: 1, - horizontal: true, - showMonthLabels: true, - showWeekdayLabels: false, - showOutOfRangeDays: false, - tooltipDataAttrs: null, - titleForValue: null, - classForValue: (value) => (value ? 'color-filled' : 'color-empty'), - monthLabels: MONTH_LABELS, - weekdayLabels: DAY_LABELS, - onClick: null, - onMouseOver: null, - onMouseLeave: null, - transformDayElement: null, -}; - -export default CalendarHeatmap; diff --git a/src/heatmap/styles.css b/src/heatmap/styles.css deleted file mode 100644 index 2961147..0000000 --- a/src/heatmap/styles.css +++ /dev/null @@ -1,72 +0,0 @@ -/* - * react-calendar-heatmap styles - * - * All of the styles in this file are optional and configurable! - * The github and gitlab color scales are provided for reference. - */ - -.react-calendar-heatmap text { - font-size: 10px; - fill: #aaa; -} - -.react-calendar-heatmap .react-calendar-heatmap-small-text { - font-size: 5px; -} - -.react-calendar-heatmap rect:hover { - stroke: #555; - stroke-width: 1px; -} - -/* - * Default color scale - */ - -.react-calendar-heatmap .color-empty { - fill: #eeeeee; -} - -.react-calendar-heatmap .color-filled { - fill: #8cc665; -} - -/* - * Github color scale - */ - -.react-calendar-heatmap .color-github-0 { - fill: #eeeeee; -} -.react-calendar-heatmap .color-github-1 { - fill: #d6e685; -} -.react-calendar-heatmap .color-github-2 { - fill: #8cc665; -} -.react-calendar-heatmap .color-github-3 { - fill: #44a340; -} -.react-calendar-heatmap .color-github-4 { - fill: #1e6823; -} - -/* - * Gitlab color scale - */ - -.react-calendar-heatmap .color-gitlab-0 { - fill: #ededed; -} -.react-calendar-heatmap .color-gitlab-1 { - fill: #acd5f2; -} -.react-calendar-heatmap .color-gitlab-2 { - fill: #7fa8d1; -} -.react-calendar-heatmap .color-gitlab-3 { - fill: #49729b; -} -.react-calendar-heatmap .color-gitlab-4 { - fill: #254e77; -}