mirror of
https://github.com/apache/superset.git
synced 2024-09-19 20:19:37 -04:00
add calendar
This commit is contained in:
parent
b72e17ee94
commit
1d3e3c5c27
@ -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: {...},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
```
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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;
|
@ -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);
|
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
@ -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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
|
};
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user