add calendar

This commit is contained in:
Krist Wongsuphasawat 2019-01-30 14:55:22 -08:00 committed by Yongjie Zhao
parent b72e17ee94
commit 1d3e3c5c27
11 changed files with 4027 additions and 0 deletions

View File

@ -0,0 +1,34 @@
## @superset-ui/legacy-plugin-chart-calendar
[![Version](https://img.shields.io/npm/v/@superset-ui/legacy-plugin-chart-calendar.svg?style=flat-square)](https://img.shields.io/npm/v/@superset-ui/legacy-plugin-chart-calendar.svg?style=flat-square)
[![David (path)](https://img.shields.io/david/apache-superset/superset-ui.svg?path=packages%2Fsuperset-ui-legacy-plugin-chart-calendar&style=flat-square)](https://david-dm.org/apache-superset/superset-ui?path=packages/superset-ui-legacy-plugin-chart-calendar)
This plugin provides Calendar Heatmap for Superset.
### Usage
Configure `key`, which can be any `string`, and register the plugin. This `key` will be used to lookup this chart throughout the app.
```js
import CalendarChartPlugin from '@superset-ui/legacy-plugin-chart-calendar';
new CalendarChartPlugin()
.configure({ key: 'calendar' })
.register();
```
Then use it via `SuperChart`. See [storybook](https://apache-superset.github.io/superset-ui-legacy/?selectedKind=plugin-chart-calendar) for more details.
```js
<SuperChart
chartType="calendar"
chartProps={{
width: 600,
height: 600,
formData: {...},
payload: {
data: {...},
},
}}
/>
```

View File

@ -0,0 +1,48 @@
{
"name": "@superset-ui/legacy-plugin-chart-calendar",
"version": "0.0.0",
"description": "Superset Legacy Chart - Calendar Heatmap",
"sideEffects": false,
"main": "lib/index.js",
"module": "esm/index.js",
"files": [
"esm",
"lib"
],
"repository": {
"type": "git",
"url": "git+https://github.com/apache-superset/superset-ui-legacy.git"
},
"keywords": [
"superset"
],
"author": "Superset",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/apache-superset/superset-ui-legacy/issues"
},
"homepage": "https://github.com/apache-superset/superset-ui-legacy#readme",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@superset-ui/core": "^0.9.x",
"d3-array": "^2.0.3",
"d3-selection": "^1.4.0",
"prop-types": "^15.6.2"
},
"devDependencies": {
"@superset-ui/chart": "^0.9.x",
"@superset-ui/color": "^0.9.x",
"@superset-ui/number-format": "^0.9.x",
"@superset-ui/time-format": "^0.9.x",
"@superset-ui/translation": "^0.9.x"
},
"peerDependencies": {
"@superset-ui/chart": "^0.9.x",
"@superset-ui/color": "^0.9.x",
"@superset-ui/number-format": "^0.9.x",
"@superset-ui/time-format": "^0.9.x",
"@superset-ui/translation": "^0.9.x"
}
}

View File

@ -0,0 +1,28 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
.cal_heatmap {
padding: 10px;
position: static !important;
overflow: auto !important;
}
.cal_heatmap .ch-tooltip {
margin-left: 20px;
margin-top: 5px;
}

View File

@ -0,0 +1,159 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/* eslint-disable sort-keys, no-magic-numbers, react/forbid-prop-types */
import PropTypes from 'prop-types';
import { extent as d3Extent, range as d3Range } from 'd3-array';
import { select as d3Select } from 'd3-selection';
import { getSequentialSchemeRegistry } from '@superset-ui/color';
import { getNumberFormatter } from '@superset-ui/number-format';
import { getTimeFormatter } from '@superset-ui/time-format';
import CalHeatMap from './vendor/cal-heatmap';
import './vendor/cal-heatmap.css';
import './Calendar.css';
function convertUTC(dttm) {
return new Date(
dttm.getUTCFullYear(),
dttm.getUTCMonth(),
dttm.getUTCDate(),
dttm.getUTCHours(),
dttm.getUTCMinutes(),
dttm.getUTCSeconds(),
);
}
const convertUTCTS = uts => convertUTC(new Date(uts)).getTime();
const propTypes = {
data: PropTypes.shape({
// Object hashed by metric name,
// then hashed by timestamp (in seconds, not milliseconds) as float
// the innermost value is count
// e.g. { count_distinct_something: { 1535034236.0: 3 } }
data: PropTypes.object,
domain: PropTypes.string,
range: PropTypes.number,
// timestamp in milliseconds
start: PropTypes.number,
subdomain: PropTypes.string,
}),
height: PropTypes.number,
cellPadding: PropTypes.number,
cellRadius: PropTypes.number,
cellSize: PropTypes.number,
linearColorScheme: PropTypes.string,
showLegend: PropTypes.bool,
showMetricName: PropTypes.bool,
showValues: PropTypes.bool,
steps: PropTypes.number,
timeFormat: PropTypes.string,
valueFormat: PropTypes.string,
verboseMap: PropTypes.object,
};
function Calendar(element, props) {
const {
data,
height,
cellPadding = 3,
cellRadius = 0,
cellSize = 10,
linearColorScheme,
showLegend,
showMetricName,
showValues,
steps,
timeFormat,
valueFormat,
verboseMap,
} = props;
const valueFormatter = getNumberFormatter(valueFormat);
const timeFormatter = getTimeFormatter(timeFormat);
const container = d3Select(element).style('height', height);
container.selectAll('*').remove();
const div = container.append('div');
const subDomainTextFormat = showValues ? (date, value) => valueFormatter(value) : null;
// Trick to convert all timestamps to UTC
// TODO: Verify if this conversion is really necessary
// since all timestamps should always be in UTC.
const metricsData = {};
Object.keys(data.data).forEach(metric => {
metricsData[metric] = {};
Object.keys(data.data[metric]).forEach(ts => {
metricsData[metric][convertUTCTS(ts * 1000) / 1000] = data.data[metric][ts];
});
});
Object.keys(metricsData).forEach(metric => {
const calContainer = div.append('div');
if (showMetricName) {
calContainer.text(`Metric: ${verboseMap[metric] || metric}`);
}
const timestamps = metricsData[metric];
const extents = d3Extent(Object.keys(timestamps), key => timestamps[key]);
const step = (extents[1] - extents[0]) / (steps - 1);
const colorScale = getSequentialSchemeRegistry()
.get(linearColorScheme)
.createLinearScale(extents);
const legend = d3Range(steps).map(i => extents[0] + step * i);
const legendColors = legend.map(colorScale);
const cal = new CalHeatMap();
cal.init({
start: convertUTCTS(data.start),
data: timestamps,
itemSelector: calContainer.node(),
legendVerticalPosition: 'top',
cellSize,
cellPadding,
cellRadius,
legendCellSize: cellSize,
legendCellPadding: 2,
legendCellRadius: cellRadius,
tooltip: true,
domain: data.domain,
subDomain: data.subdomain,
range: data.range,
browsing: true,
legend,
legendColors: {
colorScale,
min: legendColors[0],
max: legendColors[legendColors.length - 1],
empty: 'white',
},
displayLegend: showLegend,
itemName: '',
valueFormatter,
timeFormatter,
subDomainTextFormat,
});
});
}
Calendar.displayName = 'Calendar';
Calendar.propTypes = propTypes;
export default Calendar;

View File

@ -0,0 +1,22 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { reactify } from '@superset-ui/chart';
import Component from './Calendar';
export default reactify(Component);

View File

@ -0,0 +1,39 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { t } from '@superset-ui/translation';
import { ChartMetadata, ChartPlugin } from '@superset-ui/chart';
import transformProps from './transformProps';
import thumbnail from './images/thumbnail.png';
const metadata = new ChartMetadata({
credits: ['https://github.com/wa0x6e/cal-heatmap'],
description: '',
name: t('Calendar Heatmap'),
thumbnail,
});
export default class ChordChartPlugin extends ChartPlugin {
constructor() {
super({
loadChart: () => import('./ReactCalendar'),
metadata,
transformProps,
});
}
}

View File

@ -0,0 +1,52 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/* eslint-disable sort-keys */
export default function transformProps(chartProps) {
const { height, formData, payload, datasource } = chartProps;
const {
cellPadding,
cellRadius,
cellSize,
linearColorScheme,
showLegend,
showMetricName,
showValues,
steps,
xAxisTimeFormat,
yAxisFormat,
} = formData;
const { verboseMap } = datasource;
return {
height,
data: payload.data,
cellPadding,
cellRadius,
cellSize,
linearColorScheme,
showLegend,
showMetricName,
showValues,
steps,
timeFormat: xAxisTimeFormat,
valueFormat: yAxisFormat,
verboseMap,
};
}

View File

@ -0,0 +1,141 @@
/* [LICENSE TBD] */
/* Cal-HeatMap CSS */
.cal-heatmap-container {
display: block;
}
.cal-heatmap-container .graph-label
{
fill: #999;
font-size: 10px
}
.cal-heatmap-container .graph, .cal-heatmap-container .graph-legend rect {
shape-rendering: crispedges
}
.cal-heatmap-container .graph-rect
{
fill: #ededed
}
.cal-heatmap-container .graph-subdomain-group rect:hover
{
stroke: #000;
stroke-width: 1px
}
.cal-heatmap-container .subdomain-text {
font-size: 8px;
fill: #999;
pointer-events: none
}
.cal-heatmap-container .hover_cursor:hover {
cursor: pointer
}
.cal-heatmap-container .qi {
background-color: #999;
fill: #999
}
/*
Remove comment to apply this style to date with value equal to 0
.q0
{
background-color: #fff;
fill: #fff;
stroke: #ededed
}
*/
.cal-heatmap-container .q1
{
background-color: #dae289;
fill: #dae289
}
.cal-heatmap-container .q2
{
background-color: #cedb9c;
fill: #9cc069
}
.cal-heatmap-container .q3
{
background-color: #b5cf6b;
fill: #669d45
}
.cal-heatmap-container .q4
{
background-color: #637939;
fill: #637939
}
.cal-heatmap-container .q5
{
background-color: #3b6427;
fill: #3b6427
}
.cal-heatmap-container rect.highlight
{
stroke:#444;
stroke-width:1
}
.cal-heatmap-container text.highlight
{
fill: #444
}
.cal-heatmap-container rect.highlight-now
{
stroke: red
}
.cal-heatmap-container text.highlight-now
{
fill: red;
font-weight: 800
}
.cal-heatmap-container .domain-background {
fill: none;
shape-rendering: crispedges
}
.ch-tooltip {
padding: 10px;
background: #222;
color: #bbb;
font-size: 12px;
line-height: 1.4;
width: 140px;
position: absolute;
z-index: 99999;
text-align: center;
border-radius: 2px;
box-shadow: 2px 2px 2px rgba(0,0,0,0.2);
display: none;
box-sizing: border-box;
}
.ch-tooltip::after{
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
content: "";
padding: 0;
display: block;
bottom: -6px;
left: 50%;
margin-left: -6px;
border-width: 6px 6px 0;
border-top-color: #222;
}