Skip to content

Commit

Permalink
feat: add monthPlacement props. (#114)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaywcjlove committed Oct 7, 2023
1 parent 081253d commit 8c51eb0
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 79 deletions.
1 change: 1 addition & 0 deletions core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ export default Demo
| startDate | Start date | Date | `new Date()` |
| endDate | End date | Date | - |
| space | Interval between grid sizes | number | `2`
| monthPlacement | position of month labels | `'top' | 'bottom'` | `top`
| rectProps | Grid node attribute settings | `React.SVGProps<SVGRectElement>` | `2` |
| weekLabels | Week display | string[] | `['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']`
| monthLabels | Month display | string[] | `['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']`
Expand Down
76 changes: 76 additions & 0 deletions core/src/Day.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { FC, PropsWithChildren, useMemo } from "react"
import { Rect, RectProps } from './Rect';
import { formatData, getDateToString, existColor, numberSort, oneDayTime } from './utils';
import { SVGProps } from './SVG';

type DayProps = {
transform?: string;
gridNum?: number;
initStartDate: Date;
endDate?: Date;
rectProps?: RectProps;
rectSize?: number;
space?: number;
startY?: number;
rectRender?: SVGProps['rectRender'];
panelColors?: SVGProps['panelColors'];
value?: SVGProps['value'];
}

export const Day:FC<PropsWithChildren<DayProps>> = (props) => {
const { transform, gridNum = 0, startY = 0, panelColors = {}, initStartDate, space = 2, value = [], rectSize = 11, endDate, rectProps, rectRender } = props;
const data = useMemo(() => formatData(value), [value]);
const nums = useMemo(() => numberSort(Object.keys(panelColors).map((item) => parseInt(item, 10))), [panelColors]);
return (
<g transform={transform}>
{gridNum > 0 &&
[...Array(gridNum)].map((_, idx) => {
return (
<g key={idx} data-column={idx}>
{[...Array(7)].map((_, cidx) => {
const currentDate = new Date(initStartDate.getTime() + oneDayTime * (idx * 7 + cidx));
const date = getDateToString(currentDate);
const dataProps: RectProps['value'] = {
...data[date],
date: date,
row: cidx,
column: idx,
index: idx * 7 + cidx,
};
const dayProps: RectProps = {
...rectProps,
key: cidx,
fill: 'var(--rhm-rect, #EBEDF0)',
width: rectSize,
height: rectSize,
x: idx * (rectSize + space),
y: (rectSize + space) * cidx,
render: rectRender,
value: dataProps
};

if (endDate instanceof Date && currentDate.getTime() > endDate.getTime()) {
return null;
}
if (date && data[date] && panelColors && Object.keys(panelColors).length > 0) {
dayProps.fill = existColor(data[date].count || 0, nums, panelColors);
} else if (panelColors && panelColors[0]) {
dayProps.fill = panelColors[0];
}
return (
<Rect
{...dayProps}
value={dataProps}
data-date={date}
data-index={dataProps.index}
data-row={dataProps.row}
data-column={dataProps.column}
/>
);
})}
</g>
);
})}
</g>
)
}
41 changes: 20 additions & 21 deletions core/src/LabelsMonth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface LablesMonthProps extends React.SVGProps<SVGTextElement> {
space: SVGProps['space'];
leftPad: number;
colNum: number;
rectY?: number;
startDate: SVGProps['startDate'];
}

Expand All @@ -18,6 +19,7 @@ export const LabelsMonth = ({
space = 0,
leftPad = 0,
colNum = 0,
rectY = 15,
startDate,
}: LablesMonthProps) => {
const data = useMemo(() => {
Expand All @@ -35,26 +37,23 @@ export const LabelsMonth = ({
.filter((item, idx, list) => list[idx - 1] && list[idx - 1]!.month !== item!.month);
}, [colNum, monthLabels, startDate]);

return useMemo(
() => (
<Fragment>
{[...data].map((item, idx) => {
return (
<text
key={idx}
data-size={rectSize}
x={leftPad + space + space}
y={15}
dx={item!.col * (rectSize + space)}
textAnchor='start'
style={textStyle}
>
{item!.monthStr}
</text>
);
})}
</Fragment>
),
[data, leftPad, rectSize, space],
return (
<Fragment>
{[...data].map((item, idx) => {
return (
<text
key={idx}
data-size={rectSize}
x={leftPad + space + space}
y={rectY}
dx={item!.col * (rectSize + space)}
textAnchor='start'
style={textStyle}
>
{item!.monthStr}
</text>
);
})}
</Fragment>
);
};
7 changes: 5 additions & 2 deletions core/src/Legend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface LegendProps extends RectProps {
panelColors: SVGProps['panelColors'];
rectSize: SVGProps['rectSize'];
leftPad: number;
rectY: number;
legendCellSize: number;
legendRender?: (props: RectProps) => React.ReactElement;
topPad: number;
Expand All @@ -15,6 +16,7 @@ export default function Legend({
panelColors,
leftPad = 0,
topPad = 0,
rectY = 15,
space = 0,
rectSize = 0,
legendCellSize = 0,
Expand All @@ -30,7 +32,8 @@ export default function Legend({
...props,
key,
x: (size + 1) * key + leftPad,
y: topPad + rectSize * 8 + 6,
y: rectY,
// y: topPad + rectSize * 8 + 6,
fill: panelColors![Number(num)],
width: size,
height: size,
Expand All @@ -40,6 +43,6 @@ export default function Legend({
})}
</Fragment>
),
[panelColors, props, size, leftPad, topPad, rectSize, legendRender],
[panelColors, props, size, rectY, leftPad, rectSize, legendRender],
);
}
82 changes: 26 additions & 56 deletions core/src/SVG.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React, { CSSProperties, useEffect, useMemo, useState } from 'react';
import { LabelsWeek } from './LabelsWeek';
import { LabelsMonth } from './LabelsMonth';
import { Rect, RectProps } from './Rect';
import { formatData, getDateToString, existColor, numberSort, isValidDate, oneDayTime } from './utils';
import { RectProps } from './Rect';
import { isValidDate, oneDayTime } from './utils';
import Legend, { LegendProps } from './Legend';
import { Day } from './Day';

export type HeatMapValue = {
date: string;
Expand All @@ -30,6 +31,8 @@ export interface SVGProps extends React.SVGProps<SVGSVGElement> {
value?: Array<HeatMapValue>;
weekLabels?: string[] | false;
monthLabels?: string[] | false;
/** position of month labels @default `top` */
monthPlacement?: 'top' | 'bottom';
panelColors?: Record<number, string>;
}

Expand All @@ -38,6 +41,7 @@ export default function SVG(props: SVGProps) {
rectSize = 11,
legendCellSize = 11,
space = 2,
monthPlacement = 'top',
startDate = new Date(),
endDate,
rectProps,
Expand All @@ -52,10 +56,10 @@ export default function SVG(props: SVGProps) {
} = props || {};
const [gridNum, setGridNum] = useState(0);
const [leftPad, setLeftPad] = useState(!!weekLabels ? 28 : 5);
const [topPad, setTopPad] = useState(!!monthLabels ? 20 : 5);

const defaultTopPad = monthPlacement === 'top' ? 20 : 5;
const [topPad, setTopPad] = useState(!!monthLabels ? defaultTopPad : 5);
const svgRef = React.createRef<SVGSVGElement>();
const nums = useMemo(() => numberSort(Object.keys(panelColors).map((item) => parseInt(item, 10))), [panelColors]);
const data = useMemo(() => formatData(value), [value]);
useEffect(() => setLeftPad(!!weekLabels ? 28 : 5), [weekLabels]);
useEffect(() => {
if (svgRef.current) {
Expand All @@ -65,7 +69,7 @@ export default function SVG(props: SVGProps) {
}, [rectSize, svgRef, space, leftPad]);

useEffect(() => {
setTopPad(!!monthLabels ? 20 : 5);
setTopPad(!!monthLabels ? defaultTopPad : 5);
}, [monthLabels]);

const initStartDate = useMemo(() => {
Expand All @@ -84,13 +88,16 @@ export default function SVG(props: SVGProps) {
fontSize: 10,
} as CSSProperties;

const monthRectY = monthPlacement === 'top' ? 15 : 15 * 7 + space
const legendTopPad = monthPlacement === 'top' ? topPad + rectSize * 8 + 6 : (!!monthLabels ? (topPad + rectSize + space) : topPad) + rectSize * 8 + 6;
return (
<svg ref={svgRef} style={{ ...styl, ...style }} {...other}>
{legendCellSize !== 0 && (
<Legend
legendRender={legendRender}
panelColors={panelColors}
rectSize={rectSize}
rectY={legendTopPad}
legendCellSize={legendCellSize}
leftPad={leftPad}
topPad={topPad}
Expand All @@ -104,58 +111,21 @@ export default function SVG(props: SVGProps) {
space={space}
leftPad={leftPad}
colNum={gridNum}
rectY={monthRectY}
startDate={initStartDate}
/>
<g transform={`translate(${leftPad}, ${topPad})`}>
{gridNum > 0 &&
[...Array(gridNum)].map((_, idx) => {
return (
<g key={idx} data-column={idx}>
{[...Array(7)].map((_, cidx) => {
const currentDate = new Date(initStartDate.getTime() + oneDayTime * (idx * 7 + cidx));
const date = getDateToString(currentDate);
const dataProps: RectProps['value'] = {
...data[date],
date: date,
row: cidx,
column: idx,
index: idx * 7 + cidx,
};
const dayProps: RectProps = {
...rectProps,
key: cidx,
fill: 'var(--rhm-rect, #EBEDF0)',
width: rectSize,
height: rectSize,
x: idx * (rectSize + space),
y: (rectSize + space) * cidx,
render: rectRender,
value: dataProps
};

if (endDate instanceof Date && currentDate.getTime() > endDate.getTime()) {
return null;
}
if (date && data[date] && panelColors && Object.keys(panelColors).length > 0) {
dayProps.fill = existColor(data[date].count || 0, nums, panelColors);
} else if (panelColors && panelColors[0]) {
dayProps.fill = panelColors[0];
}
return (
<Rect
{...dayProps}
value={dataProps}
data-date={date}
data-index={dataProps.index}
data-row={dataProps.row}
data-column={dataProps.column}
/>
);
})}
</g>
);
})}
</g>
<Day
transform={`translate(${leftPad}, ${topPad})`}
gridNum={gridNum}
initStartDate={initStartDate}
endDate={endDate}
rectProps={rectProps}
rectSize={rectSize}
rectRender={rectRender}
panelColors={panelColors}
value={value}
space={space}
/>
</svg>
);
}
8 changes: 8 additions & 0 deletions www/src/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export default function Example() {
const [enableDark, setEnableDark] = useState(false);
const [enableCircle, setEnableCircle] = useState(false);
const [rectSize, setRectSize] = useState(11);
const [monthPlacement, setMonthPlacement] = useState<'top' | 'bottom'>('top');
const [legendCellSize, setLegendCellSize] = useState<number | undefined>();
const [enableWeekLabels, setEnableWeekLabels] = useState<false | undefined | string[]>(undefined);
const [enableMonthLabels, setEnableMonthLabels] = useState<false | undefined | string[]>(undefined);
Expand All @@ -122,6 +123,7 @@ export default function Example() {
monthLabels={enableMonthLabels}
startDate={new Date('2016/01/01')}
endDate={enableEndDate ? new Date('2016/6/01') : undefined}
monthPlacement={monthPlacement}
value={value}
rectProps={{
rx: !enableCircle ? 0 : 5,
Expand Down Expand Up @@ -258,6 +260,12 @@ export default function Example() {
</div>

<div style={{ display: 'flex', marginTop: 16 }}>
<label>
<select value={monthPlacement} onChange={(evn) => setMonthPlacement(evn.target.value as any)}>
<option value="top">monthPlacement = top</option>
<option value="bottom">monthPlacement = bottom</option>
</select>
</label>
<label>
<input
type="number"
Expand Down

0 comments on commit 8c51eb0

Please sign in to comment.