├── .commitlintrc.js
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── index.html
├── package.json
├── rollup.config.js
├── src
├── adaptor.ts
├── index.ts
├── types.ts
└── utils.ts
└── tsconfig.json
/.commitlintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rules: {
3 | 'type-enum': [
4 | 2,
5 | 'always',
6 | ['build', 'chore', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'style', 'test', 'wip'],
7 | ],
8 | },
9 | };
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Sys
10 | .DS_STORE
11 | .idea
12 |
13 | # Node
14 | node_modules/
15 |
16 | # Build
17 | dist
18 | lib
19 | esm
20 |
21 | # Test
22 | coverage
23 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": true,
3 | "singleQuote": true,
4 | "trailingComma": "all",
5 | "bracketSpacing": true,
6 | "printWidth": 120,
7 | "arrowParens": "always"
8 | }
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 visiky
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # G2Plot-Calendar
2 |
3 | > G2Plot-Calendar: plugin based on G2Plot v2. [Live Demo](https://visiky.github.io/g2plot-calendar/), [CodeSandbox](https://codesandbox.io/s/g2plot-calendar-p6418).
4 |
5 | [](https://www.npmjs.com/package/g2plot-calendar)
6 | [](https://www.npmjs.com/package/g2plot-calendar)
7 |
8 |
9 | 
10 |
11 |
12 | ## Install
13 |
14 | ```bash
15 | $ npm i --save g2plot-calendar
16 | ```
17 |
18 |
19 | ## Usage
20 |
21 | - render
22 |
23 | ```ts
24 | import { P } from '@antv/g2plot';
25 | import * as G2PlotCalendar from 'g2plot-calendar';
26 |
27 | let plot;
28 |
29 | fetch('https://gw.alipayobjects.com/os/antfincdn/nvYn7dOQB9/result.json')
30 | .then((data) => data.json())
31 | .then((data) => {
32 | // 第一次默认渲染
33 | plot = new P('container', {}, G2PlotCalendar.adaptor, {
34 | ...G2PlotCalendar.defaultOptions,
35 | width: 800,
36 | height: 100,
37 | data,
38 | });
39 |
40 | plot.render();
41 | });
42 | ```
43 |
44 | - update
45 |
46 | ```ts
47 | plot.update({
48 | data: [],
49 | });
50 | ```
51 |
52 | - browser
53 |
54 | ```ts
55 |
56 |
57 |
72 | ```
73 |
74 | ## Configure
75 |
76 | ```ts
77 | type Datum = {
78 | date: string;
79 | value: number | null;
80 | };
81 |
82 | export interface CalendarOptions {
83 | /** 数据 */
84 | readonly data: Datum[];
85 | /** 颜色, 默认: ['#ebedf0', '#9be9a8', '#40c463', '#30a14e', '#216e39'] */
86 | readonly color: string[];
87 | }
88 | ```
89 |
90 | ## License
91 |
92 | MIT@[visiky](https://github.com/visiky).
93 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | G2Plot-Calendar
7 |
68 |
69 |
70 |
71 |
72 |
97 |
98 |
99 |
100 |
137 |
138 |
139 |
140 |
141 |
156 |
157 |
158 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "g2plot-calendar",
3 | "version": "1.0.4",
4 | "description": "A component like github-contribution-calendar based on AntV/G2Plot.",
5 | "license": "MIT",
6 | "main": "lib/index.js",
7 | "module": "esm/index.js",
8 | "unpkg": "dist/g2plot-calendar.min.js",
9 | "types": "lib/index.d.ts",
10 | "files": [
11 | "lib",
12 | "esm",
13 | "dist",
14 | "src"
15 | ],
16 | "scripts": {
17 | "clean": "rimraf -rf lib esm dist",
18 | "lint-staged": "lint-staged",
19 | "test": "npm run build",
20 | "build:umd": "rimraf ./dist && rollup -c",
21 | "build:cjs": "rimraf ./lib && tsc --module commonjs --outDir lib",
22 | "build:esm": "rimraf ./esm && tsc --module ESNext --outDir esm",
23 | "build": "npm run clean && npm run build:cjs && npm run build:esm && npm run build:umd",
24 | "prepublishOnly": "npm run build"
25 | },
26 | "peerDependencies": {
27 | "@antv/g2plot": "^2.0.0",
28 | "dayjs": "^1.10.4"
29 | },
30 | "keywords": [
31 | "g2plot",
32 | "github-calendar"
33 | ],
34 | "devDependencies": {
35 | "@antv/g2plot": "^2.0.0",
36 | "@commitlint/cli": "^8.2.0",
37 | "@rollup/plugin-commonjs": "^15.0.0",
38 | "@rollup/plugin-node-resolve": "^9.0.0",
39 | "husky": "^3.0.5",
40 | "lint-staged": "^9.3.0",
41 | "prettier": "^1.18.2",
42 | "rimraf": "^3.0.0",
43 | "rollup": "^2.26.10",
44 | "rollup-plugin-node-resolve": "^5.2.0",
45 | "rollup-plugin-size": "^0.2.2",
46 | "rollup-plugin-typescript": "^1.0.1",
47 | "rollup-plugin-uglify": "^6.0.3",
48 | "typescript": "^3.6.3"
49 | },
50 | "lint-staged": {
51 | "*.{ts,tsx}": [
52 | "prettier --write",
53 | "git add"
54 | ]
55 | },
56 | "husky": {
57 | "hooks": {
58 | "pre-commit": "npm run lint-staged",
59 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { uglify } from 'rollup-plugin-uglify';
2 | import resolve from '@rollup/plugin-node-resolve';
3 | import typescript from 'rollup-plugin-typescript';
4 | import size from 'rollup-plugin-size';
5 | import commonjs from '@rollup/plugin-commonjs'
6 |
7 | module.exports = [{
8 | input: 'src/index.ts',
9 | output: {
10 | file: 'dist/g2plot-calendar.min.js',
11 | name: 'G2PlotCalendar',
12 | format: 'umd',
13 | sourcemap: false,
14 | },
15 | plugins: [
16 | resolve(),
17 | typescript(),
18 | commonjs(),
19 | uglify(),
20 | size(),
21 | ],
22 | }];
23 |
--------------------------------------------------------------------------------
/src/adaptor.ts:
--------------------------------------------------------------------------------
1 | import { Params } from '@antv/g2plot';
2 | import { DAY_OF_WEEK, getCalendarData, getFirstDateOfMonthByWeek } from './utils';
3 | import { CalendarOptions } from './types';
4 |
5 | export const defaultOptions = {
6 | color: ['#ebedf0', '#9be9a8', '#40c463', '#30a14e', '#216e39'],
7 | tooltip: {
8 | shared: true,
9 | showMarkers: false,
10 | fields: ['value', 'date'],
11 | showTitle: true,
12 | title: (title, { date }) => date,
13 | domStyles: {
14 | 'g2-tooltip': {
15 | margin: 0,
16 | padding: '2px 4px',
17 | fontSize: '10px',
18 | },
19 | 'g2-tooltip-list-item': {
20 | margin: '6px 0',
21 | },
22 | 'g2-tooltip-title': {
23 | margin: '4px 0 0',
24 | },
25 | },
26 | },
27 | style: () => {
28 | return {
29 | radius: 4,
30 | stroke: '#fff',
31 | lineWidth: 4,
32 | };
33 | },
34 | };
35 |
36 | export function adaptor(params: Params): Params {
37 | const { chart, options } = params;
38 |
39 | const { data, color, tooltip, style } = options;
40 |
41 | const calendarData = getCalendarData(data);
42 |
43 | chart.data(calendarData).scale('day', { values: DAY_OF_WEEK });
44 |
45 | const geometry = chart.polygon().position('x*day');
46 |
47 | geometry.color('value', color);
48 |
49 | if (typeof style === 'function') {
50 | geometry.style('value*month*week*day', (value, month, week, day) => style({ value, month, week, day }));
51 | } else if (style) {
52 | geometry.style(style);
53 | }
54 |
55 | geometry.shape('', () => ['square', 1, 1]);
56 |
57 | chart.coordinate().reflect('y');
58 |
59 | chart.legend(false);
60 | chart.tooltip(tooltip);
61 | chart.axis('x', {
62 | line: null,
63 | grid: null,
64 | tickLine: null,
65 | subTickLine: null,
66 | label: {
67 | autoHide: false,
68 | formatter: (text, item) => {
69 | return getFirstDateOfMonthByWeek(Number(text.slice(5)));
70 | },
71 | },
72 | });
73 | chart.axis('day', { line: null, grid: null });
74 |
75 | return params;
76 | }
77 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | // 统一导出结构为
2 | export { defaultOptions, adaptor } from './adaptor';
3 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import { Options, StyleAttr } from '@antv/g2plot';
2 |
3 | export type Datum = {
4 | date: string;
5 | value: number | null;
6 | };
7 |
8 | export type CalendarDatum = {
9 | x: string;
10 | year: string;
11 | month: string;
12 | week: string;
13 | day: string;
14 | date: string;
15 | value: number | null;
16 | };
17 |
18 | export interface CalendarOptions
19 | extends Omit {
20 | /** 数据 */
21 | readonly data: Datum[];
22 | /** 热力图形样式 */
23 | readonly style?: StyleAttr;
24 | }
25 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import dayjs from 'dayjs';
2 | import weekOfYear from 'dayjs/plugin/weekOfYear';
3 | import { CalendarDatum, Datum } from './types';
4 |
5 | dayjs.extend(weekOfYear);
6 |
7 | function mapDayToMonth(month: number) {
8 | const dayOfMonth = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
9 | return dayOfMonth[month];
10 | }
11 |
12 | export const DAY_OF_WEEK = ['Sun', 'Mon', 'Tue', 'Wed', 'Thur', 'Fri', 'Sat'];
13 |
14 | function mapDayToWeek(day: number) {
15 | return DAY_OF_WEEK[day];
16 | }
17 |
18 | export function getCalendarData(values: Datum[], lastDay?: string, dateFormat = 'YYYY-MM-DD') {
19 | const endDate = dayjs(lastDay);
20 | const startDate = endDate.subtract(1, 'year');
21 |
22 | const result: (Omit & { date: dayjs.Dayjs; day: number })[] = [];
23 | for (let date = startDate; !date.isAfter(endDate); ) {
24 | result.push({
25 | x: `${date.week() === 1 && date.month() === 11 ? date.year() + 1 : date.year()}-${date.week()}`,
26 | year: `${date.year()}`,
27 | value: values.find((v) => v.date === date.format(dateFormat))?.value || 0,
28 | month: mapDayToMonth(date.month()),
29 | week: `${date.week()}`,
30 | date: date,
31 | day: date.day(),
32 | });
33 | date = date.add(1, 'day');
34 | }
35 |
36 | return result.map((d) => ({ ...d, date: d.date.format(dateFormat), day: mapDayToWeek(d.day) }));
37 | }
38 |
39 | /**
40 | * 根据 第 N 周获取月份(只获取第一个周的月份)
41 | * @param week
42 | * @returns
43 | */
44 | export function getFirstDateOfMonthByWeek(week: number): string {
45 | const date = dayjs().week(week);
46 | return date.date() > 7 ? '' : mapDayToMonth(date.month());
47 | }
48 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "lib",
4 | "module": "commonjs",
5 | "target": "es5",
6 | "jsx": "preserve",
7 | "moduleResolution": "node",
8 | "experimentalDecorators": true,
9 | "declaration": true,
10 | "sourceMap": false,
11 | "allowSyntheticDefaultImports": true,
12 | "esModuleInterop": true,
13 | "pretty": true,
14 | "lib": ["dom", "esnext"],
15 | "skipLibCheck": true,
16 | "baseUrl": "src"
17 | },
18 | "include": ["src/**/*"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------