├── .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 | [![npm Version](https://img.shields.io/npm/v/g2plot-calendar.svg)](https://www.npmjs.com/package/g2plot-calendar) 6 | [![npm License](https://img.shields.io/npm/l/g2plot-calendar.svg)](https://www.npmjs.com/package/g2plot-calendar) 7 | 8 | 9 | ![image](https://gw.alipayobjects.com/zos/antfincdn/AVAgk25n1f/158f43ae-9056-4e40-a95c-deb96d24b573.png) 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 |
73 |

74 | G2Plot 78 | Calendar Generator 80 | 95 |

96 |
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 | --------------------------------------------------------------------------------