@lobehub/charts 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/es/Heatmaps/Calendar.d.ts +18 -0
- package/es/Heatmaps/Calendar.js +67 -0
- package/es/Heatmaps/ChartLabels.d.ts +15 -0
- package/es/Heatmaps/ChartLabels.js +50 -0
- package/es/Heatmaps/Footer.d.ts +22 -0
- package/es/Heatmaps/Footer.js +60 -0
- package/es/Heatmaps/index.d.ts +28 -0
- package/es/Heatmaps/index.js +181 -0
- package/es/Heatmaps/styles.d.ts +7 -0
- package/es/Heatmaps/styles.js +14 -0
- package/es/hooks/useOnWindowResize.js +2 -1
- package/es/hooks/usePrefersReducedMotion.d.ts +1 -0
- package/es/hooks/usePrefersReducedMotion.js +23 -0
- package/es/index.d.ts +1 -0
- package/es/index.js +1 -0
- package/es/types.d.ts +25 -0
- package/es/utils/calendar.d.ts +29 -0
- package/es/utils/calendar.js +194 -0
- package/es/utils/index.d.ts +1 -0
- package/es/utils/index.js +2 -1
- package/es/utils/theme.d.ts +2 -0
- package/es/utils/theme.js +49 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -156,6 +156,7 @@ Every bit counts and your one-time donation sparkles in our galaxy of support! Y
|
|
|
156
156
|
|
|
157
157
|
- **recharts** - <https://github.com/recharts/recharts>
|
|
158
158
|
- **tremor** - <https://github.com/tremorlabs/tremor>
|
|
159
|
+
- **react-activity-calendar** - <https://github.com/grubersjoe/react-activity-calendar>
|
|
159
160
|
|
|
160
161
|
<div align="right">
|
|
161
162
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import type { Activity, Labels, Week } from "../types";
|
|
3
|
+
interface CalendarProps {
|
|
4
|
+
blockMargin: number;
|
|
5
|
+
blockRadius: number;
|
|
6
|
+
blockSize: number;
|
|
7
|
+
colorScale: string[];
|
|
8
|
+
customTooltip?: (activity: Activity) => ReactNode;
|
|
9
|
+
enableAnimation: boolean;
|
|
10
|
+
labelHeight: number;
|
|
11
|
+
labels: Labels;
|
|
12
|
+
maxLevel: number;
|
|
13
|
+
onValueChange?: (value: Activity) => void;
|
|
14
|
+
showTooltip: boolean;
|
|
15
|
+
weeks: Week[];
|
|
16
|
+
}
|
|
17
|
+
declare const Calendar: import("react").NamedExoticComponent<CalendarProps>;
|
|
18
|
+
export default Calendar;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
|
+
import _taggedTemplateLiteral from "@babel/runtime/helpers/esm/taggedTemplateLiteral";
|
|
3
|
+
var _templateObject;
|
|
4
|
+
import { Tooltip } from '@lobehub/ui';
|
|
5
|
+
import { keyframes } from 'antd-style';
|
|
6
|
+
import { Fragment, memo } from 'react';
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
9
|
+
var Calendar = /*#__PURE__*/memo(function (_ref) {
|
|
10
|
+
var weeks = _ref.weeks,
|
|
11
|
+
maxLevel = _ref.maxLevel,
|
|
12
|
+
enableAnimation = _ref.enableAnimation,
|
|
13
|
+
colorScale = _ref.colorScale,
|
|
14
|
+
blockSize = _ref.blockSize,
|
|
15
|
+
blockRadius = _ref.blockRadius,
|
|
16
|
+
blockMargin = _ref.blockMargin,
|
|
17
|
+
labelHeight = _ref.labelHeight,
|
|
18
|
+
showTooltip = _ref.showTooltip,
|
|
19
|
+
customTooltip = _ref.customTooltip,
|
|
20
|
+
labels = _ref.labels,
|
|
21
|
+
onValueChange = _ref.onValueChange;
|
|
22
|
+
return /*#__PURE__*/_jsx(_Fragment, {
|
|
23
|
+
children: weeks.map(function (week, weekIndex) {
|
|
24
|
+
return week.map(function (activity, dayIndex) {
|
|
25
|
+
if (!activity) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
if (activity.level < 0 || activity.level > maxLevel) {
|
|
29
|
+
throw new RangeError("Provided activity level ".concat(activity.level, " for ").concat(activity.date, " is out of range. It must be between 0 and ").concat(maxLevel, "."));
|
|
30
|
+
}
|
|
31
|
+
var style = enableAnimation ? {
|
|
32
|
+
animation: "".concat(keyframes(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n 0% {\n fill: var(--lobe-heatmaps-loading);\n }\n 50% {\n fill: var(--lobe-heatmaps-loading-active);\n }\n 100% {\n fill: var(--lobe-heatmaps-loading);\n }\n "]))), " 1.75s ease-in-out infinite"),
|
|
33
|
+
animationDelay: "".concat(weekIndex * 20 + dayIndex * 20, "ms")
|
|
34
|
+
} : undefined;
|
|
35
|
+
var block = /*#__PURE__*/_jsx("rect", {
|
|
36
|
+
"data-date": activity.date,
|
|
37
|
+
"data-level": activity.level,
|
|
38
|
+
fill: colorScale[activity.level],
|
|
39
|
+
height: blockSize,
|
|
40
|
+
onClick: function onClick() {
|
|
41
|
+
return onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(activity);
|
|
42
|
+
},
|
|
43
|
+
rx: blockRadius,
|
|
44
|
+
ry: blockRadius,
|
|
45
|
+
style: _objectSpread({
|
|
46
|
+
cursor: onValueChange ? 'pointer' : undefined
|
|
47
|
+
}, style),
|
|
48
|
+
width: blockSize,
|
|
49
|
+
x: 0,
|
|
50
|
+
y: labelHeight + (blockSize + blockMargin) * dayIndex
|
|
51
|
+
});
|
|
52
|
+
return /*#__PURE__*/_jsx(Fragment, {
|
|
53
|
+
children: showTooltip ? /*#__PURE__*/_jsx(Tooltip, {
|
|
54
|
+
title: customTooltip ? customTooltip(activity) : labels.tooltip ? labels.tooltip.replace('{{count}}', String(activity.count)).replace('{{date}}', String(activity.date)) : "".concat(activity.count, " activities on ").concat(activity.date),
|
|
55
|
+
children: block
|
|
56
|
+
}) : block
|
|
57
|
+
}, activity.date);
|
|
58
|
+
});
|
|
59
|
+
}).map(function (week, x) {
|
|
60
|
+
return /*#__PURE__*/_jsx("g", {
|
|
61
|
+
transform: "translate(".concat((blockSize + blockMargin) * x, ", 0)"),
|
|
62
|
+
children: week
|
|
63
|
+
}, x);
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
export default Calendar;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import type { Labels, Week } from "../types";
|
|
3
|
+
interface ChartLabelsProps {
|
|
4
|
+
blockMargin: number;
|
|
5
|
+
blockSize: number;
|
|
6
|
+
hideMonthLabels: boolean;
|
|
7
|
+
labelHeight: number;
|
|
8
|
+
labelMargin: number;
|
|
9
|
+
labels: Labels;
|
|
10
|
+
showWeekdayLabels: boolean;
|
|
11
|
+
weekStart: number;
|
|
12
|
+
weeks: Week[];
|
|
13
|
+
}
|
|
14
|
+
declare const ChartLabels: import("react").NamedExoticComponent<ChartLabelsProps>;
|
|
15
|
+
export default ChartLabels;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { getMonthLabels } from "../utils/calendar";
|
|
3
|
+
import { useStyles } from "./styles";
|
|
4
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
5
|
+
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
6
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
+
var ChartLabels = /*#__PURE__*/memo(function (_ref) {
|
|
8
|
+
var labels = _ref.labels,
|
|
9
|
+
blockSize = _ref.blockSize,
|
|
10
|
+
labelHeight = _ref.labelHeight,
|
|
11
|
+
blockMargin = _ref.blockMargin,
|
|
12
|
+
labelMargin = _ref.labelMargin,
|
|
13
|
+
showWeekdayLabels = _ref.showWeekdayLabels,
|
|
14
|
+
hideMonthLabels = _ref.hideMonthLabels,
|
|
15
|
+
weeks = _ref.weeks,
|
|
16
|
+
weekStart = _ref.weekStart;
|
|
17
|
+
var _useStyles = useStyles(),
|
|
18
|
+
cx = _useStyles.cx;
|
|
19
|
+
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
20
|
+
children: [showWeekdayLabels && weeks[0] && /*#__PURE__*/_jsx("g", {
|
|
21
|
+
className: cx('legend-weekday'),
|
|
22
|
+
children: weeks[0].map(function (_, index) {
|
|
23
|
+
var _labels$weekdays;
|
|
24
|
+
if (index % 2 === 0) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
var dayIndex = (index + weekStart) % 7;
|
|
28
|
+
return /*#__PURE__*/_jsx("text", {
|
|
29
|
+
dominantBaseline: "middle",
|
|
30
|
+
textAnchor: "end",
|
|
31
|
+
x: -labelMargin,
|
|
32
|
+
y: labelHeight + (blockSize + blockMargin) * index + blockSize / 2,
|
|
33
|
+
children: labels === null || labels === void 0 || (_labels$weekdays = labels.weekdays) === null || _labels$weekdays === void 0 ? void 0 : _labels$weekdays[dayIndex]
|
|
34
|
+
}, index);
|
|
35
|
+
})
|
|
36
|
+
}), !hideMonthLabels && /*#__PURE__*/_jsx("g", {
|
|
37
|
+
className: cx('legend-month'),
|
|
38
|
+
children: getMonthLabels(weeks, labels.months).map(function (_ref2) {
|
|
39
|
+
var label = _ref2.label,
|
|
40
|
+
weekIndex = _ref2.weekIndex;
|
|
41
|
+
return /*#__PURE__*/_jsx("text", {
|
|
42
|
+
dominantBaseline: "hanging",
|
|
43
|
+
x: (blockSize + blockMargin) * weekIndex,
|
|
44
|
+
children: label
|
|
45
|
+
}, weekIndex);
|
|
46
|
+
})
|
|
47
|
+
})]
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
export default ChartLabels;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
interface FooterProps {
|
|
3
|
+
blockRadius: number;
|
|
4
|
+
blockSize: number;
|
|
5
|
+
colorScale: string[];
|
|
6
|
+
hideColorLegend?: boolean;
|
|
7
|
+
hideTotalCount?: boolean;
|
|
8
|
+
labels: {
|
|
9
|
+
legend: {
|
|
10
|
+
less: string;
|
|
11
|
+
more: string;
|
|
12
|
+
};
|
|
13
|
+
totalCount?: string;
|
|
14
|
+
};
|
|
15
|
+
loading: boolean;
|
|
16
|
+
maxLevel: number;
|
|
17
|
+
totalCount: number;
|
|
18
|
+
weekdayLabelOffset?: number;
|
|
19
|
+
year: number;
|
|
20
|
+
}
|
|
21
|
+
declare const Footer: import("react").NamedExoticComponent<FooterProps>;
|
|
22
|
+
export default Footer;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { useStyles } from "./styles";
|
|
3
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
4
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
5
|
+
var Footer = /*#__PURE__*/memo(function (_ref) {
|
|
6
|
+
var hideTotalCount = _ref.hideTotalCount,
|
|
7
|
+
hideColorLegend = _ref.hideColorLegend,
|
|
8
|
+
weekdayLabelOffset = _ref.weekdayLabelOffset,
|
|
9
|
+
loading = _ref.loading,
|
|
10
|
+
labels = _ref.labels,
|
|
11
|
+
year = _ref.year,
|
|
12
|
+
maxLevel = _ref.maxLevel,
|
|
13
|
+
blockSize = _ref.blockSize,
|
|
14
|
+
colorScale = _ref.colorScale,
|
|
15
|
+
blockRadius = _ref.blockRadius,
|
|
16
|
+
totalCount = _ref.totalCount;
|
|
17
|
+
var _useStyles = useStyles(),
|
|
18
|
+
cx = _useStyles.cx,
|
|
19
|
+
styles = _useStyles.styles;
|
|
20
|
+
return /*#__PURE__*/_jsxs("footer", {
|
|
21
|
+
className: cx('footer', styles.footer),
|
|
22
|
+
style: {
|
|
23
|
+
marginLeft: weekdayLabelOffset
|
|
24
|
+
},
|
|
25
|
+
children: [loading && /*#__PURE__*/_jsx("div", {
|
|
26
|
+
children: "\xA0"
|
|
27
|
+
}), !loading && !hideTotalCount && /*#__PURE__*/_jsx("div", {
|
|
28
|
+
className: cx('count'),
|
|
29
|
+
children: labels.totalCount ? labels.totalCount.replace('{{count}}', String(totalCount)).replace('{{year}}', String(year)) : "".concat(totalCount, " activities in ").concat(year)
|
|
30
|
+
}), !loading && !hideColorLegend && /*#__PURE__*/_jsxs("div", {
|
|
31
|
+
className: cx('legend-colors', styles.legendColors),
|
|
32
|
+
children: [/*#__PURE__*/_jsx("span", {
|
|
33
|
+
style: {
|
|
34
|
+
marginRight: '0.4em'
|
|
35
|
+
},
|
|
36
|
+
children: labels.legend.less
|
|
37
|
+
}), Array.from({
|
|
38
|
+
length: maxLevel + 1
|
|
39
|
+
}).fill(null).map(function (_, level) {
|
|
40
|
+
return /*#__PURE__*/_jsx("svg", {
|
|
41
|
+
height: blockSize,
|
|
42
|
+
width: blockSize,
|
|
43
|
+
children: /*#__PURE__*/_jsx("rect", {
|
|
44
|
+
fill: colorScale[level],
|
|
45
|
+
height: blockSize,
|
|
46
|
+
rx: blockRadius,
|
|
47
|
+
ry: blockRadius,
|
|
48
|
+
width: blockSize
|
|
49
|
+
})
|
|
50
|
+
}, level);
|
|
51
|
+
}), /*#__PURE__*/_jsx("span", {
|
|
52
|
+
style: {
|
|
53
|
+
marginLeft: '0.4em'
|
|
54
|
+
},
|
|
55
|
+
children: labels.legend.more
|
|
56
|
+
})]
|
|
57
|
+
})]
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
export default Footer;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Day as WeekDay } from 'date-fns';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { FlexboxProps } from 'react-layout-kit';
|
|
4
|
+
import { NoDataProps } from "../common/NoData";
|
|
5
|
+
import type { Activity, Labels } from "../types";
|
|
6
|
+
export interface HeatmapsProps extends FlexboxProps {
|
|
7
|
+
blockMargin?: number;
|
|
8
|
+
blockRadius?: number;
|
|
9
|
+
blockSize?: number;
|
|
10
|
+
colors?: string[];
|
|
11
|
+
customTooltip?: (activity: Activity) => ReactNode;
|
|
12
|
+
data: Array<Activity>;
|
|
13
|
+
fontSize?: number;
|
|
14
|
+
hideColorLegend?: boolean;
|
|
15
|
+
hideMonthLabels?: boolean;
|
|
16
|
+
hideTotalCount?: boolean;
|
|
17
|
+
labels?: Labels;
|
|
18
|
+
loading?: boolean;
|
|
19
|
+
maxLevel?: number;
|
|
20
|
+
noDataText?: NoDataProps['noDataText'];
|
|
21
|
+
onValueChange?: (value: Activity) => void;
|
|
22
|
+
showTooltip?: boolean;
|
|
23
|
+
showWeekdayLabels?: boolean;
|
|
24
|
+
totalCount?: number;
|
|
25
|
+
weekStart?: WeekDay;
|
|
26
|
+
}
|
|
27
|
+
declare const Heatmaps: import("react").ForwardRefExoticComponent<HeatmapsProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
28
|
+
export default Heatmaps;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
|
4
|
+
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
5
|
+
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
|
|
6
|
+
var _excluded = ["data", "blockMargin", "blockRadius", "blockSize", "onValueChange", "fontSize", "hideColorLegend", "hideMonthLabels", "hideTotalCount", "labels", "maxLevel", "loading", "showTooltip", "customTooltip", "showWeekdayLabels", "style", "colors", "totalCount", "weekStart", "className", "noDataText"];
|
|
7
|
+
import { useThemeMode } from 'antd-style';
|
|
8
|
+
import { getYear, parseISO } from 'date-fns';
|
|
9
|
+
import { forwardRef, useMemo } from 'react';
|
|
10
|
+
import { Flexbox } from 'react-layout-kit';
|
|
11
|
+
import NoData from "../common/NoData";
|
|
12
|
+
import { usePrefersReducedMotion } from "../hooks/usePrefersReducedMotion";
|
|
13
|
+
import { isOnSeverSide } from "../utils";
|
|
14
|
+
import { DEFAULT_LABELS, generateEmptyData, groupByWeeks, maxWeekdayLabelLength } from "../utils/calendar";
|
|
15
|
+
import { createTheme } from "../utils/theme";
|
|
16
|
+
import Calendar from "./Calendar";
|
|
17
|
+
import ChartLabels from "./ChartLabels";
|
|
18
|
+
import Footer from "./Footer";
|
|
19
|
+
import { useStyles } from "./styles";
|
|
20
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
21
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
22
|
+
var LABEL_MARGIN = 8;
|
|
23
|
+
var Heatmaps = /*#__PURE__*/forwardRef(function (props, ref) {
|
|
24
|
+
var data = props.data,
|
|
25
|
+
_props$blockMargin = props.blockMargin,
|
|
26
|
+
blockMargin = _props$blockMargin === void 0 ? 4 : _props$blockMargin,
|
|
27
|
+
_props$blockRadius = props.blockRadius,
|
|
28
|
+
blockRadius = _props$blockRadius === void 0 ? 2 : _props$blockRadius,
|
|
29
|
+
_props$blockSize = props.blockSize,
|
|
30
|
+
blockSize = _props$blockSize === void 0 ? 12 : _props$blockSize,
|
|
31
|
+
onValueChange = props.onValueChange,
|
|
32
|
+
_props$fontSize = props.fontSize,
|
|
33
|
+
fontSize = _props$fontSize === void 0 ? 14 : _props$fontSize,
|
|
34
|
+
_props$hideColorLegen = props.hideColorLegend,
|
|
35
|
+
hideColorLegend = _props$hideColorLegen === void 0 ? false : _props$hideColorLegen,
|
|
36
|
+
_props$hideMonthLabel = props.hideMonthLabels,
|
|
37
|
+
hideMonthLabels = _props$hideMonthLabel === void 0 ? false : _props$hideMonthLabel,
|
|
38
|
+
_props$hideTotalCount = props.hideTotalCount,
|
|
39
|
+
hideTotalCount = _props$hideTotalCount === void 0 ? false : _props$hideTotalCount,
|
|
40
|
+
labelsProp = props.labels,
|
|
41
|
+
_props$maxLevel = props.maxLevel,
|
|
42
|
+
maxLevel = _props$maxLevel === void 0 ? Math.max(1, props.maxLevel || 4) : _props$maxLevel,
|
|
43
|
+
_props$loading = props.loading,
|
|
44
|
+
loading = _props$loading === void 0 ? false : _props$loading,
|
|
45
|
+
_props$showTooltip = props.showTooltip,
|
|
46
|
+
showTooltip = _props$showTooltip === void 0 ? true : _props$showTooltip,
|
|
47
|
+
customTooltip = props.customTooltip,
|
|
48
|
+
_props$showWeekdayLab = props.showWeekdayLabels,
|
|
49
|
+
showWeekdayLabels = _props$showWeekdayLab === void 0 ? false : _props$showWeekdayLab,
|
|
50
|
+
_props$style = props.style,
|
|
51
|
+
style = _props$style === void 0 ? {} : _props$style,
|
|
52
|
+
colors = props.colors,
|
|
53
|
+
totalCountProp = props.totalCount,
|
|
54
|
+
_props$weekStart = props.weekStart,
|
|
55
|
+
weekStart = _props$weekStart === void 0 ? 0 : _props$weekStart,
|
|
56
|
+
className = props.className,
|
|
57
|
+
noDataText = props.noDataText,
|
|
58
|
+
rest = _objectWithoutProperties(props, _excluded);
|
|
59
|
+
var activities = useMemo(function () {
|
|
60
|
+
if (loading) return generateEmptyData();
|
|
61
|
+
return data.map(function (item) {
|
|
62
|
+
return _objectSpread(_objectSpread({}, item), {}, {
|
|
63
|
+
level: item.level > maxLevel ? maxLevel : item.level
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}, [data, maxLevel, loading]);
|
|
67
|
+
var _useThemeMode = useThemeMode(),
|
|
68
|
+
isDarkMode = _useThemeMode.isDarkMode;
|
|
69
|
+
var _useStyles = useStyles(),
|
|
70
|
+
cx = _useStyles.cx,
|
|
71
|
+
styles = _useStyles.styles,
|
|
72
|
+
theme = _useStyles.theme;
|
|
73
|
+
var defaultColors = useMemo(function () {
|
|
74
|
+
switch (maxLevel) {
|
|
75
|
+
case 1:
|
|
76
|
+
{
|
|
77
|
+
return [theme.colorFillSecondary, isDarkMode ? theme.lime8 : theme.green8];
|
|
78
|
+
}
|
|
79
|
+
case 2:
|
|
80
|
+
{
|
|
81
|
+
return [theme.colorFillSecondary, isDarkMode ? theme.lime4 : theme.green4, isDarkMode ? theme.lime8 : theme.green8];
|
|
82
|
+
}
|
|
83
|
+
case 3:
|
|
84
|
+
{
|
|
85
|
+
return [theme.colorFillSecondary, isDarkMode ? theme.lime3 : theme.green3, isDarkMode ? theme.lime6 : theme.green6, isDarkMode ? theme.lime9 : theme.green9];
|
|
86
|
+
}
|
|
87
|
+
case 4:
|
|
88
|
+
{
|
|
89
|
+
return [theme.colorFillSecondary, isDarkMode ? theme.lime4 : theme.green4, isDarkMode ? theme.lime6 : theme.green6, isDarkMode ? theme.lime8 : theme.green8, isDarkMode ? theme.lime10 : theme.green10];
|
|
90
|
+
}
|
|
91
|
+
default:
|
|
92
|
+
{
|
|
93
|
+
return [theme.colorFillSecondary, theme.colorSuccess];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}, [theme, isDarkMode, maxLevel]);
|
|
97
|
+
var useAnimation = !usePrefersReducedMotion();
|
|
98
|
+
if (activities.length === 0) return /*#__PURE__*/_jsx(NoData, {
|
|
99
|
+
noDataText: noDataText
|
|
100
|
+
});
|
|
101
|
+
if (showWeekdayLabels && isOnSeverSide) return null;
|
|
102
|
+
var colorScale = createTheme(colors || defaultColors, maxLevel + 1);
|
|
103
|
+
var firstActivity = activities[0];
|
|
104
|
+
var year = getYear(parseISO(firstActivity.date));
|
|
105
|
+
var weeks = groupByWeeks(activities, weekStart);
|
|
106
|
+
var firstWeek = weeks[0];
|
|
107
|
+
var labels = Object.assign({}, DEFAULT_LABELS, labelsProp);
|
|
108
|
+
var labelHeight = hideMonthLabels ? 0 : fontSize + LABEL_MARGIN;
|
|
109
|
+
var weekdayLabelOffset = showWeekdayLabels ? maxWeekdayLabelLength(firstWeek, weekStart, labels.weekdays, fontSize) + LABEL_MARGIN : undefined;
|
|
110
|
+
var getDimensions = function getDimensions() {
|
|
111
|
+
return {
|
|
112
|
+
height: labelHeight + (blockSize + blockMargin) * 7 - blockMargin,
|
|
113
|
+
width: weeks.length * (blockSize + blockMargin) - blockMargin
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
var _getDimensions = getDimensions(),
|
|
117
|
+
width = _getDimensions.width,
|
|
118
|
+
height = _getDimensions.height;
|
|
119
|
+
var zeroColor = colorScale[0];
|
|
120
|
+
var containerStyles = _objectSpread({
|
|
121
|
+
fontSize: fontSize
|
|
122
|
+
}, useAnimation && _defineProperty(_defineProperty({}, "--lobe-heatmaps-loading", zeroColor), "--lobe-heatmaps-loading-active", theme.colorFill));
|
|
123
|
+
return /*#__PURE__*/_jsxs(Flexbox, _objectSpread(_objectSpread({
|
|
124
|
+
className: cx(styles.container, className),
|
|
125
|
+
ref: ref,
|
|
126
|
+
style: _objectSpread(_objectSpread({}, style), containerStyles)
|
|
127
|
+
}, rest), {}, {
|
|
128
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
129
|
+
className: styles.scrollContainer,
|
|
130
|
+
children: /*#__PURE__*/_jsxs("svg", {
|
|
131
|
+
className: styles.calendar,
|
|
132
|
+
height: height,
|
|
133
|
+
style: {
|
|
134
|
+
marginLeft: weekdayLabelOffset
|
|
135
|
+
},
|
|
136
|
+
viewBox: "0 0 ".concat(width, " ").concat(height),
|
|
137
|
+
width: width,
|
|
138
|
+
children: [!loading && !showWeekdayLabels && hideMonthLabels ? undefined : /*#__PURE__*/_jsx(ChartLabels, {
|
|
139
|
+
blockMargin: blockMargin,
|
|
140
|
+
blockSize: blockSize,
|
|
141
|
+
hideMonthLabels: hideMonthLabels,
|
|
142
|
+
labelHeight: labelHeight,
|
|
143
|
+
labelMargin: LABEL_MARGIN,
|
|
144
|
+
labels: labels,
|
|
145
|
+
showWeekdayLabels: showWeekdayLabels,
|
|
146
|
+
weekStart: weekStart,
|
|
147
|
+
weeks: weeks
|
|
148
|
+
}), /*#__PURE__*/_jsx(Calendar, {
|
|
149
|
+
blockMargin: blockMargin,
|
|
150
|
+
blockRadius: blockRadius,
|
|
151
|
+
blockSize: blockSize,
|
|
152
|
+
colorScale: colorScale,
|
|
153
|
+
customTooltip: customTooltip,
|
|
154
|
+
enableAnimation: loading && useAnimation,
|
|
155
|
+
labelHeight: labelHeight,
|
|
156
|
+
labels: labels,
|
|
157
|
+
maxLevel: maxLevel,
|
|
158
|
+
onValueChange: onValueChange,
|
|
159
|
+
showTooltip: showTooltip,
|
|
160
|
+
weeks: weeks
|
|
161
|
+
})]
|
|
162
|
+
})
|
|
163
|
+
}), hideTotalCount && hideColorLegend ? undefined : /*#__PURE__*/_jsx(Footer, {
|
|
164
|
+
blockRadius: blockRadius,
|
|
165
|
+
blockSize: blockSize,
|
|
166
|
+
colorScale: colorScale,
|
|
167
|
+
hideColorLegend: hideColorLegend,
|
|
168
|
+
hideTotalCount: hideTotalCount,
|
|
169
|
+
labels: labels,
|
|
170
|
+
loading: loading,
|
|
171
|
+
maxLevel: maxLevel,
|
|
172
|
+
totalCount: typeof totalCountProp === 'number' ? totalCountProp : activities.reduce(function (sum, activity) {
|
|
173
|
+
return sum + activity.count;
|
|
174
|
+
}, 0),
|
|
175
|
+
weekdayLabelOffset: weekdayLabelOffset,
|
|
176
|
+
year: year
|
|
177
|
+
})]
|
|
178
|
+
}));
|
|
179
|
+
});
|
|
180
|
+
Heatmaps.displayName = 'Heatmaps';
|
|
181
|
+
export default Heatmaps;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const useStyles: (props?: unknown) => import("antd-style").ReturnStyles<{
|
|
2
|
+
calendar: import("antd-style").SerializedStyles;
|
|
3
|
+
container: import("antd-style").SerializedStyles;
|
|
4
|
+
footer: import("antd-style").SerializedStyles;
|
|
5
|
+
legendColors: import("antd-style").SerializedStyles;
|
|
6
|
+
scrollContainer: import("antd-style").SerializedStyles;
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import _taggedTemplateLiteral from "@babel/runtime/helpers/esm/taggedTemplateLiteral";
|
|
2
|
+
var _templateObject, _templateObject2, _templateObject3, _templateObject4, _templateObject5;
|
|
3
|
+
import { createStyles } from 'antd-style';
|
|
4
|
+
export var useStyles = createStyles(function (_ref) {
|
|
5
|
+
var css = _ref.css,
|
|
6
|
+
token = _ref.token;
|
|
7
|
+
return {
|
|
8
|
+
calendar: css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n overflow: visible;\n display: block;\n\n text {\n fill: currentcolor;\n }\n "]))),
|
|
9
|
+
container: css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n display: flex;\n flex-direction: column;\n gap: 8px;\n\n width: max-content;\n max-width: 100%;\n\n rect {\n stroke: ", ";\n stroke-width: 1px;\n shape-rendering: geometricprecision;\n }\n "])), token.colorFillTertiary),
|
|
10
|
+
footer: css(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\n display: flex;\n flex-wrap: wrap;\n gap: 4px 16px;\n white-space: nowrap;\n "]))),
|
|
11
|
+
legendColors: css(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["\n display: flex;\n gap: 3px;\n align-items: center;\n margin-left: auto;\n "]))),
|
|
12
|
+
scrollContainer: css(_templateObject5 || (_templateObject5 = _taggedTemplateLiteral(["\n overflow: auto hidden;\n max-width: 100%;\n "])))
|
|
13
|
+
};
|
|
14
|
+
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
|
+
import { isOnSeverSide } from "../utils";
|
|
2
3
|
export var useOnWindowResize = function useOnWindowResize(handler) {
|
|
3
4
|
useEffect(function () {
|
|
4
|
-
if (
|
|
5
|
+
if (isOnSeverSide) return;
|
|
5
6
|
var handleResize = function handleResize() {
|
|
6
7
|
handler();
|
|
7
8
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const usePrefersReducedMotion: () => boolean;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { isOnSeverSide } from "../utils";
|
|
4
|
+
var query = '(prefers-reduced-motion: reduce)';
|
|
5
|
+
export var usePrefersReducedMotion = function usePrefersReducedMotion() {
|
|
6
|
+
var _useState = useState(true),
|
|
7
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
8
|
+
prefersReducedMotion = _useState2[0],
|
|
9
|
+
setPrefersReducedMotion = _useState2[1];
|
|
10
|
+
useEffect(function () {
|
|
11
|
+
if (isOnSeverSide) return;
|
|
12
|
+
var mediaQuery = window.matchMedia(query);
|
|
13
|
+
setPrefersReducedMotion(mediaQuery.matches);
|
|
14
|
+
var onChange = function onChange(event) {
|
|
15
|
+
setPrefersReducedMotion(event.matches);
|
|
16
|
+
};
|
|
17
|
+
mediaQuery.addEventListener('change', onChange);
|
|
18
|
+
return function () {
|
|
19
|
+
mediaQuery.removeEventListener('change', onChange);
|
|
20
|
+
};
|
|
21
|
+
}, []);
|
|
22
|
+
return prefersReducedMotion;
|
|
23
|
+
};
|
package/es/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { type Bar, default as BarList, type BarListProps } from './BarList';
|
|
|
4
4
|
export { default as ChartTooltipFrame } from './common/ChartTooltip/ChartTooltipFrame';
|
|
5
5
|
export { default as Legend, type LegendProps } from './components/Legend';
|
|
6
6
|
export { default as DonutChart, type DonutChartProps } from './DonutChart';
|
|
7
|
+
export { default as Heatmaps, type HeatmapsProps } from './Heatmaps';
|
|
7
8
|
export { default as LineChart, type LineChartProps } from './LineChart';
|
|
8
9
|
export { default as ScatterChart, type ScatterChartProps } from './ScatterChart';
|
|
9
10
|
export * from './SparkChart';
|
package/es/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { default as BarList } from "./BarList";
|
|
|
4
4
|
export { default as ChartTooltipFrame } from "./common/ChartTooltip/ChartTooltipFrame";
|
|
5
5
|
export { default as Legend } from "./components/Legend";
|
|
6
6
|
export { default as DonutChart } from "./DonutChart";
|
|
7
|
+
export { default as Heatmaps } from "./Heatmaps";
|
|
7
8
|
export { default as LineChart } from "./LineChart";
|
|
8
9
|
export { default as ScatterChart } from "./ScatterChart";
|
|
9
10
|
export * from "./SparkChart";
|
package/es/types.d.ts
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
import type { AnimationEvent, ClipboardEvent, CompositionEvent, DOMAttributes, DragEvent, FocusEvent, FormEvent, HTMLAttributes, JSXElementConstructor, KeyboardEvent, MouseEvent, PointerEvent, ReactElement, SVGAttributes, SyntheticEvent, TouchEvent, TransitionEvent, UIEvent, WheelEvent } from 'react';
|
|
2
|
+
export interface Activity {
|
|
3
|
+
count: number;
|
|
4
|
+
date: string;
|
|
5
|
+
level: number;
|
|
6
|
+
}
|
|
7
|
+
export type Week = Array<Activity | undefined>;
|
|
8
|
+
export type Labels = Partial<{
|
|
9
|
+
legend: Partial<{
|
|
10
|
+
less: string;
|
|
11
|
+
more: string;
|
|
12
|
+
}>;
|
|
13
|
+
months: Array<string>;
|
|
14
|
+
tooltip: string;
|
|
15
|
+
totalCount: string;
|
|
16
|
+
weekdays: Array<string>;
|
|
17
|
+
}>;
|
|
18
|
+
interface BlockAttributes extends SVGAttributes<SVGRectElement>, HTMLAttributes<SVGRectElement> {
|
|
19
|
+
}
|
|
20
|
+
export type BlockElement = ReactElement<BlockAttributes, JSXElementConstructor<SVGRectElement>>;
|
|
21
|
+
export type SVGRectEventHandler = Omit<DOMAttributes<SVGRectElement>, 'css' | 'children' | 'dangerouslySetInnerHTML'>;
|
|
22
|
+
export type EventHandlerMap = {
|
|
23
|
+
[key in keyof SVGRectEventHandler]: (...event: Parameters<NonNullable<SVGRectEventHandler[keyof SVGRectEventHandler]>>) => (activity: Activity) => void;
|
|
24
|
+
};
|
|
25
|
+
export type ReactEvent<E extends Element> = AnimationEvent<E> & ClipboardEvent<E> & CompositionEvent<E> & DragEvent<E> & FocusEvent<E> & FormEvent<E> & KeyboardEvent<E> & MouseEvent<E> & PointerEvent<E> & SyntheticEvent<E> & TouchEvent<E> & TransitionEvent<E> & UIEvent<E> & WheelEvent<E>;
|
|
1
26
|
type FixedProps = {
|
|
2
27
|
categoryClicked: string;
|
|
3
28
|
eventType: 'dot' | 'category' | 'bar' | 'slice' | 'bubble';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Day as WeekDay } from 'date-fns';
|
|
2
|
+
import type { Activity, Week } from "../types";
|
|
3
|
+
export declare const DEFAULT_MONTH_LABELS: string[];
|
|
4
|
+
export declare const DEFAULT_LABELS: {
|
|
5
|
+
legend: {
|
|
6
|
+
less: string;
|
|
7
|
+
more: string;
|
|
8
|
+
};
|
|
9
|
+
months: string[];
|
|
10
|
+
tooltip: string;
|
|
11
|
+
totalCount: string;
|
|
12
|
+
weekdays: string[];
|
|
13
|
+
};
|
|
14
|
+
interface MonthLabel {
|
|
15
|
+
label: string;
|
|
16
|
+
weekIndex: number;
|
|
17
|
+
}
|
|
18
|
+
export declare const groupByWeeks: (activities: Array<Activity>, weekStart?: WeekDay) => Array<Week>;
|
|
19
|
+
export declare const getMonthLabels: (weeks: Array<Week>, monthNames?: Array<string>) => Array<MonthLabel>;
|
|
20
|
+
export declare const generateEmptyData: () => Array<Activity>;
|
|
21
|
+
export declare const generateTestData: (args: {
|
|
22
|
+
interval?: {
|
|
23
|
+
end: Date;
|
|
24
|
+
start: Date;
|
|
25
|
+
} | undefined;
|
|
26
|
+
maxLevel?: number | undefined;
|
|
27
|
+
}) => Array<Activity>;
|
|
28
|
+
export declare const maxWeekdayLabelLength: (firstWeek: Week, weekStart: number, labels: string[], fontSize: number) => number;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
|
|
2
|
+
import { differenceInCalendarDays, eachDayOfInterval, endOfYear, formatISO, getDay, getMonth, nextDay, parseISO, startOfYear, subWeeks } from 'date-fns';
|
|
3
|
+
import { isOnSeverSide } from "./index";
|
|
4
|
+
export var DEFAULT_MONTH_LABELS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
5
|
+
export var DEFAULT_LABELS = {
|
|
6
|
+
legend: {
|
|
7
|
+
less: 'Less',
|
|
8
|
+
more: 'More'
|
|
9
|
+
},
|
|
10
|
+
months: DEFAULT_MONTH_LABELS,
|
|
11
|
+
tooltip: '{{count}} activities on {{date}}',
|
|
12
|
+
totalCount: '{{count}} activities in {{year}}',
|
|
13
|
+
weekdays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
|
14
|
+
};
|
|
15
|
+
var fillHoles = function fillHoles(activities) {
|
|
16
|
+
if (activities.length === 0) {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
var calendar = new Map(activities.map(function (a) {
|
|
20
|
+
return [a.date, a];
|
|
21
|
+
}));
|
|
22
|
+
var firstActivity = activities[0];
|
|
23
|
+
var lastActivity = activities.at(-1);
|
|
24
|
+
return eachDayOfInterval({
|
|
25
|
+
end: parseISO(lastActivity.date),
|
|
26
|
+
start: parseISO(firstActivity.date)
|
|
27
|
+
}).map(function (day) {
|
|
28
|
+
var date = formatISO(day, {
|
|
29
|
+
representation: 'date'
|
|
30
|
+
});
|
|
31
|
+
if (calendar.has(date)) {
|
|
32
|
+
return calendar.get(date);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
count: 0,
|
|
36
|
+
date: date,
|
|
37
|
+
level: 0
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
var calcTextDimensions = function calcTextDimensions(text, fontSize) {
|
|
42
|
+
if (isOnSeverSide) return;
|
|
43
|
+
if (fontSize < 1) {
|
|
44
|
+
throw new RangeError('fontSize must be positive');
|
|
45
|
+
}
|
|
46
|
+
if (text.length === 0) {
|
|
47
|
+
return {
|
|
48
|
+
height: 0,
|
|
49
|
+
width: 0
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
var namespace = 'http://www.w3.org/2000/svg';
|
|
53
|
+
var svg = document.createElementNS(namespace, 'svg');
|
|
54
|
+
svg.style.position = 'absolute';
|
|
55
|
+
svg.style.visibility = 'hidden';
|
|
56
|
+
svg.style.fontFamily = window.getComputedStyle(document.body).fontFamily;
|
|
57
|
+
svg.style.fontSize = "".concat(fontSize, "px");
|
|
58
|
+
var textNode = document.createElementNS(namespace, 'text');
|
|
59
|
+
textNode.textContent = text;
|
|
60
|
+
svg.append(textNode);
|
|
61
|
+
document.body.append(svg);
|
|
62
|
+
var boundingBox = textNode.getBBox();
|
|
63
|
+
svg.remove();
|
|
64
|
+
return {
|
|
65
|
+
height: boundingBox.height,
|
|
66
|
+
width: boundingBox.width
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
export var groupByWeeks = function groupByWeeks(activities) {
|
|
70
|
+
var weekStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
71
|
+
if (activities.length === 0) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
var normalizedActivities = fillHoles(activities);
|
|
75
|
+
|
|
76
|
+
// Determine the first date of the calendar. If the first date is not the
|
|
77
|
+
// set start weekday, the selected weekday one week earlier is used.
|
|
78
|
+
var firstActivity = normalizedActivities[0];
|
|
79
|
+
var firstDate = parseISO(firstActivity.date);
|
|
80
|
+
var firstCalendarDate = getDay(firstDate) === weekStart ? firstDate : subWeeks(nextDay(firstDate, weekStart), 1);
|
|
81
|
+
|
|
82
|
+
// To correctly group activities by week, it is necessary to left-pad the list
|
|
83
|
+
// because the first date might not be set start weekday.
|
|
84
|
+
var paddedActivities = [].concat(_toConsumableArray(Array.from({
|
|
85
|
+
length: differenceInCalendarDays(firstDate, firstCalendarDate)
|
|
86
|
+
}).fill(null)), _toConsumableArray(normalizedActivities));
|
|
87
|
+
var numberOfWeeks = Math.ceil(paddedActivities.length / 7);
|
|
88
|
+
|
|
89
|
+
// Finally, group activities by week
|
|
90
|
+
return Array.from({
|
|
91
|
+
length: numberOfWeeks
|
|
92
|
+
}).fill(null).map(function (_, weekIndex) {
|
|
93
|
+
return paddedActivities.slice(weekIndex * 7, weekIndex * 7 + 7);
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
export var getMonthLabels = function getMonthLabels(weeks) {
|
|
97
|
+
var monthNames = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_MONTH_LABELS;
|
|
98
|
+
return weeks.reduce(function (labels, week, weekIndex) {
|
|
99
|
+
var firstActivity = week.filter(Boolean).find(function (activity) {
|
|
100
|
+
return activity !== undefined;
|
|
101
|
+
});
|
|
102
|
+
if (!firstActivity) {
|
|
103
|
+
throw new Error("Unexpected error: Week ".concat(weekIndex + 1, " is empty: [").concat(week, "]."));
|
|
104
|
+
}
|
|
105
|
+
var month = monthNames[getMonth(parseISO(firstActivity.date))];
|
|
106
|
+
if (!month) {
|
|
107
|
+
var monthName = new Date(firstActivity.date).toLocaleString('en-US', {
|
|
108
|
+
month: 'short'
|
|
109
|
+
});
|
|
110
|
+
throw new Error("Unexpected error: undefined month label for ".concat(monthName, "."));
|
|
111
|
+
}
|
|
112
|
+
var prevLabel = labels.at(-1);
|
|
113
|
+
if (weekIndex === 0 || !prevLabel || prevLabel.label !== month) {
|
|
114
|
+
return [].concat(_toConsumableArray(labels), [{
|
|
115
|
+
label: month,
|
|
116
|
+
weekIndex: weekIndex
|
|
117
|
+
}]);
|
|
118
|
+
}
|
|
119
|
+
return labels;
|
|
120
|
+
}, []).filter(function (_ref, index, labels) {
|
|
121
|
+
var weekIndex = _ref.weekIndex;
|
|
122
|
+
// Labels should only be shown if there is "enough" space (data).
|
|
123
|
+
// This is a naive implementation that does not take the block size,
|
|
124
|
+
// font size, etc. into account.
|
|
125
|
+
var minWeeks = 3;
|
|
126
|
+
|
|
127
|
+
// Skip the first month label if there is not enough space to the next one.
|
|
128
|
+
if (index === 0) {
|
|
129
|
+
return labels[1] && labels[1].weekIndex - weekIndex >= minWeeks;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Skip the last month label if there is not enough data in that month
|
|
133
|
+
// to avoid overflowing the calendar on the right.
|
|
134
|
+
if (index === labels.length - 1) {
|
|
135
|
+
return weeks.slice(weekIndex).length >= minWeeks;
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
export var generateEmptyData = function generateEmptyData() {
|
|
141
|
+
var year = new Date().getFullYear();
|
|
142
|
+
var days = eachDayOfInterval({
|
|
143
|
+
end: new Date(year, 11, 31),
|
|
144
|
+
start: new Date(year, 0, 1)
|
|
145
|
+
});
|
|
146
|
+
return days.map(function (date) {
|
|
147
|
+
return {
|
|
148
|
+
count: 0,
|
|
149
|
+
date: formatISO(date, {
|
|
150
|
+
representation: 'date'
|
|
151
|
+
}),
|
|
152
|
+
level: 0
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
export var generateTestData = function generateTestData(args) {
|
|
157
|
+
var _args$interval;
|
|
158
|
+
var maxCount = 20;
|
|
159
|
+
var maxLevel = args.maxLevel ? Math.max(1, args.maxLevel) : 4;
|
|
160
|
+
var now = new Date();
|
|
161
|
+
var days = eachDayOfInterval((_args$interval = args.interval) !== null && _args$interval !== void 0 ? _args$interval : {
|
|
162
|
+
end: endOfYear(now),
|
|
163
|
+
start: startOfYear(now)
|
|
164
|
+
});
|
|
165
|
+
return days.map(function (date) {
|
|
166
|
+
// The random activity count is shifted by up to 80% towards zero.
|
|
167
|
+
var c = Math.round(Math.random() * maxCount - Math.random() * (0.8 * maxCount));
|
|
168
|
+
var count = Math.max(0, c);
|
|
169
|
+
var level = Math.ceil(count / maxCount * maxLevel);
|
|
170
|
+
return {
|
|
171
|
+
count: count,
|
|
172
|
+
date: formatISO(date, {
|
|
173
|
+
representation: 'date'
|
|
174
|
+
}),
|
|
175
|
+
level: level
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
export var maxWeekdayLabelLength = function maxWeekdayLabelLength(firstWeek, weekStart, labels, fontSize) {
|
|
180
|
+
if (isOnSeverSide) return 0;
|
|
181
|
+
if (labels.length !== 7) {
|
|
182
|
+
throw new Error('Exactly 7 labels, one for each weekday must be passed.');
|
|
183
|
+
}
|
|
184
|
+
return firstWeek.reduce(function (maxLength, _, index) {
|
|
185
|
+
if (index % 2 !== 0) {
|
|
186
|
+
var dayIndex = (index + weekStart) % 7;
|
|
187
|
+
var label = labels[dayIndex];
|
|
188
|
+
// @ts-ignore
|
|
189
|
+
var curLength = Math.ceil(calcTextDimensions(label, fontSize).width);
|
|
190
|
+
return Math.max(maxLength, curLength);
|
|
191
|
+
}
|
|
192
|
+
return maxLength;
|
|
193
|
+
}, 0);
|
|
194
|
+
};
|
package/es/utils/index.d.ts
CHANGED
package/es/utils/index.js
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import _typeof from "@babel/runtime/helpers/esm/typeof";
|
|
2
|
+
import chroma from 'chroma-js';
|
|
3
|
+
var isstringScale = function isstringScale(colors, size) {
|
|
4
|
+
var invalidstring = colors.find(function (color) {
|
|
5
|
+
return !chroma.valid(color);
|
|
6
|
+
});
|
|
7
|
+
if (invalidstring) {
|
|
8
|
+
throw new Error("Invalid color \"".concat(String(invalidstring), "\" passed. All CSS color formats are accepted."));
|
|
9
|
+
}
|
|
10
|
+
return colors.length === size;
|
|
11
|
+
};
|
|
12
|
+
var createstringScale = function createstringScale(colors, size) {
|
|
13
|
+
return chroma.scale(colors).mode('lch').colors(size);
|
|
14
|
+
};
|
|
15
|
+
var validateTheme = function validateTheme(input, size) {
|
|
16
|
+
if (_typeof(input) !== 'object' || input === undefined) {
|
|
17
|
+
throw new Error("The theme object must contain at least one of the fields \"light\" and \"dark\" with exactly 2 or ".concat(size, " colors respectively."));
|
|
18
|
+
}
|
|
19
|
+
if (input) {
|
|
20
|
+
var length = input.length;
|
|
21
|
+
if (length !== 2 && length !== size) {
|
|
22
|
+
throw new Error("theme.light must contain exactly 2 or ".concat(size, " colors, ").concat(length, " passed."));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var createDefaultTheme = function createDefaultTheme(size) {
|
|
27
|
+
return createstringScale(['hsl(0, 0%, 20%)', 'hsl(0, 0%, 92%)'], size);
|
|
28
|
+
};
|
|
29
|
+
export function createTheme(input) {
|
|
30
|
+
var size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5;
|
|
31
|
+
var defaultTheme = createDefaultTheme(size);
|
|
32
|
+
if (input) {
|
|
33
|
+
validateTheme(input, size);
|
|
34
|
+
var theme = input !== null && input !== void 0 ? input : defaultTheme;
|
|
35
|
+
|
|
36
|
+
// @ts-ignore
|
|
37
|
+
return isstringScale(theme, size) ? theme : createstringScale(theme, size);
|
|
38
|
+
}
|
|
39
|
+
return defaultTheme;
|
|
40
|
+
}
|
|
41
|
+
export var convertAlphaToSolid = function convertAlphaToSolid(foreground, background) {
|
|
42
|
+
var fgColor = chroma(foreground);
|
|
43
|
+
var bgColor = chroma(background);
|
|
44
|
+
var alpha = fgColor.alpha();
|
|
45
|
+
var alphaComplement = 1 - alpha;
|
|
46
|
+
var mixedColor = [fgColor.get('rgb.r') * alpha + bgColor.get('rgb.r') * alphaComplement, fgColor.get('rgb.g') * alpha + bgColor.get('rgb.g') * alphaComplement, fgColor.get('rgb.b') * alpha + bgColor.get('rgb.b') * alphaComplement];
|
|
47
|
+
var resultColor = chroma(mixedColor);
|
|
48
|
+
return resultColor.hex();
|
|
49
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/charts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "React modern charts components built on recharts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lobehub",
|
|
@@ -68,8 +68,8 @@
|
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
70
|
"@babel/runtime": "^7.24.8",
|
|
71
|
+
"date-fns": "^3.6.0",
|
|
71
72
|
"polished": "^4.3.1",
|
|
72
|
-
"react-layout-kit": "^1.9.0",
|
|
73
73
|
"recharts": "^2.12.7"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"@lobehub/lint": "^1.24.3",
|
|
79
79
|
"@lobehub/ui": "^1.146.8",
|
|
80
80
|
"@testing-library/react": "^14.3.1",
|
|
81
|
+
"@types/chroma-js": "^2.4.4",
|
|
81
82
|
"@types/pangu": "^4.0.2",
|
|
82
83
|
"@types/query-string": "^6.3.0",
|
|
83
84
|
"@types/react": "18.2.40",
|
|
@@ -109,9 +110,11 @@
|
|
|
109
110
|
"ahooks": ">=3",
|
|
110
111
|
"antd": ">=5.13.0",
|
|
111
112
|
"antd-style": ">=3",
|
|
113
|
+
"chroma-js": ">=2",
|
|
112
114
|
"lucide-react": ">=0.396.0",
|
|
113
115
|
"react": ">=18",
|
|
114
|
-
"react-dom": ">=18"
|
|
116
|
+
"react-dom": ">=18",
|
|
117
|
+
"react-layout-kit": ">=1"
|
|
115
118
|
},
|
|
116
119
|
"publishConfig": {
|
|
117
120
|
"access": "public",
|