Multiple Charts¶
This demo renders multiple visualizations in a single page. Click on a visualization to select it. The configuration panel updates itself with the configuration of the selected visualization.
You can also use the configuration panel to update the selected visualization in different ways. For example, you can change the chart type, group-by attribute(s), limit, sort, and metrics. Different visualizations may provide different options. For example, the “Line Trend: Attribute Values” visualization provides options to configure a time attribute and its granularity. The “Scatter Plot” visualization allows to select three metrics for x-axis, y-axis, and bubble size.
If you'd like to see the demo in full screen click here
Review the complete source code and copy/paste it to create your own.
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Multiple Chart Demo</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="charts-div">
<div class="chart-wrapper">
<div id="chart1" class="chart active"></div>
<div class="loader" id="chart1_loader"></div>
</div>
<div class="chart-wrapper">
<div id="chart2" class="chart"></div>
<div class="loader" id="chart2_loader"></div>
</div>
</div>
<div class="options-wrapper">
<div class="options">
<div class="card">
<h4 class="card-header">Chart type</h4>
<div id="chart-type-card" class="card-body">
<div class="property-container">
<select id="chart-select" class="input-control">
<option value="Bars" selected>Bars</option>
<option value="Pie">Pie</option>
<option value="Donut">Donut</option>
<option value="Trend">Trend</option>
<option value="Scatter Plot">Scatter Plot</option>
<option value="Tree Map">Tree Map</option>
<option value="Bars and Line">Bars and Line</option>
<option value="Area Line">Area Line</option>
</select>
</div>
</div>
</div>
<div class="card">
<h4 class="card-header">Group by</h4>
<div id="attribute-card" class="card-body">
<div class="property-container">
<select id="attribute-select" class="input-control">
<option value="venuecity.keyword" selected>Venue city</option>
<option value="venuestate">Venue state</option>
<option value="venuename.keyword">Venue name</option>
</select>
</div>
<div>
<div class="property-container">
<label for="attribute-limit">Limit</label>
<input id="attribute-limit" class="input-control grid-input" type="number" step="1" value="10" min="1">
</div>
</div>
<div>
<div class="property-container">
<label for="attribute-sort-dir">Sort</label>
<select id="attribute-sort-dir" class="input-control">
<option value="desc" selected>Desc</option>
<option value="asc">Asc</option>
</select>
</div>
</div>
</div>
</div>
<div id="time-card-ct" class="form-group" style="display:none">
<h4 class="card-header">Time group</h4>
<div id="time-card" class="card-body collapse">
<div class="property-container">
<select id="time-attribute-select" class="input-control">
<option value="saletime" selected>Sale time</option>
<option value="starttime">Start time</option>
</select>
</div>
<div>
<div class="property-container">
<label for="time-attribute-limit">Limit</label>
<input id="time-attribute-limit" class="input-control grid-input" type="number" step="1" value="1000" min="1">
</div>
</div>
<div>
<div class="property-container">
<label for="time-attribute-func">Granularity</label>
<select id="time-attribute-func" class="input-control">
<option value="SECOND">Second</option>
<option value="HOUR">Hour</option>
<option value="DAY">Day</option>
<option value="MONTH" selected>Month</option>
<option vaue="YEAR">Year</option>
</select>
</div>
</div>
</div>
</div>
<div class="card">
<h4 class="card-header">Metric</h4>
<div id="metric-card" class="card-body collapse">
<div class="property-container">
<select id="metric-select" class="input-control">
<option value="commission" selected>Commission</option>
<option value="pricepaid">Price paid</option>
<option value="venueseats">Venue seats</option>
</select>
</div>
<div>
<div class="property-container">
<label for="metric-func-select">Function</label>
<select id="metric-func-select" class="input-control">
<option value="sum" selected>Sum</option>
<option value="avg">Average</option>
<option value="min">Min</option>
<option value="max">Max</option>
</select>
</div>
</div>
</div>
</div>
<div id="metric2-card-ct" class="card form-group" style="display:none">
<h4 class="card-header">2nd Metric</h4>
<div id="metric2-card" class="card-body collapse">
<div class="property-container">
<select id="metric2-select" class="input-control">
<option value="commission" selected>Commission</option>
<option value="pricepaid">Price paid</option>
<option value="venueseats">Venue seats</option>
</select>
</div>
<div>
<div class="property-container">
<label for="metric2-func-select">Function</label>
<select id="metric2-func-select" class="input-control">
<option value="sum" selected>Sum</option>
<option value="avg">Average</option>
<option value="min">Min</option>
<option value="max">Max</option>
</select>
</div>
</div>
</div>
</div>
<div id="metric3-card-ct" class="card form-group" style="display:none">
<h4 class="card-header">3rd Metric</h4>
<div id="metric3-card" class="card-body collapse">
<div class="property-container">
<select id="metric3-select" class="input-control">
<option value="commission" selected>Commission</option>
<option value="pricepaid">Price paid</option>
<option value="venueseats">Venue seats</option>
</select>
</div>
<div>
<div class="property-container">
<label for="metric3-func-select">Function</label>
<select id="metric3-func-select" class="input-control">
<option value="sum" selected>Sum</option>
<option value="avg">Average</option>
<option value="min">Min</option>
<option value="max">Max</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="../assets/jquery.min.js"></script>
<script src="../../lib/cftoolkit.min.js"></script>
<script src="../assets/cft-elasticsearch-provider.min.js"></script>
<script src="../assets/cft-standard-charts.min.js"></script>
<script src="./index.js"></script>
</body>
</html>
|
let $ = window.$;
let cf = window.cf;
let selectedChart = 'chart1';
let hideLoader = function (e) {
let id = e.definition.element + '_loader';
let ldr = document.getElementById(id);
$(ldr).css('display', 'none');
};
let chartState = {
chart1: {
selectedChart: 'Bars',
selectedAttribute: 'venuecity.keyword',
limit: 10,
sortDir: 'desc',
selectedTimeAttr: 'saletime',
timeLimit: 1000,
timeFunc: 'MONTH',
selectedMetric: 'commission',
selectedMetricFunc: 'sum',
selectedMetric2: 'pricepaid',
selectedMetricFunc2: 'sum',
selectedMetric3: 'venueseats',
selectedMetricFunc3: 'sum'
},
chart2: {
selectedChart: 'Trend',
selectedAttribute: 'venuecity.keyword',
limit: 10,
sortDir: 'desc',
selectedTimeAttr: 'saletime',
timeLimit: 1000,
timeFunc: 'MONTH',
selectedMetric: 'commission',
selectedMetricFunc: 'sum',
selectedMetric2: 'pricepaid',
selectedMetricFunc2: 'sum',
selectedMetric3: 'venueseats',
selectedMetricFunc3: 'sum'
}
};
let updateChart = function (event) {
let state = chartState[selectedChart];
state.selectedChart = $('#chart-select').val();
let hasTime = false;
if (['Trend', 'Area Line'].indexOf(state.selectedChart) > -1) {
$('#time-card-ct').show();
hasTime = true;
} else {
$('#time-card-ct').hide();
}
state.selectedAttribute = $('#attribute-select').val();
state.limit = $('#attribute-limit').val();
state.sortDir = $('#attribute-sort-dir').val();
state.selectedTimeAttr = $('#time-attribute-select').val();
state.timeLimit = parseInt($('#time-attribute-limit').val(), 10);
state.timeFunc = $('#time-attribute-func').val();
state.selectedMetric = $('#metric-select').val();
state.selectedMetricFunc = $('#metric-func-select').val();
state.selectedMetric2 = $('#metric2-select').val();
state.selectedMetricFunc2 = $('#metric2-func-select').val();
state.selectedMetric3 = $('#metric3-select').val();
state.selectedMetricFunc3 = $('#metric3-func-select').val();
// Define metric
let metrics = [];
metrics.push(cf.Metric(state.selectedMetric, state.selectedMetricFunc));
if (['Scatter Plot'].indexOf(state.selectedChart) > -1) {
metrics.push(cf.Metric(state.selectedMetric2, state.selectedMetricFunc2));
metrics.push(cf.Metric(state.selectedMetric3, state.selectedMetricFunc3));
$('#metric2-card-ct').show();
$('#metric3-card-ct').show();
} else if (['Bars and Line'].indexOf(state.selectedChart) > -1) {
metrics.push(cf.Metric(state.selectedMetric2, state.selectedMetricFunc2));
$('#metric2-card-ct').show();
} else {
$('#metric2-card-ct').hide();
$('#metric3-card-ct').hide();
}
// Define Attribute to group by
let groups = [];
groups.push(
cf
.Attribute(state.selectedAttribute)
.limit(state.limit)
.sort(state.sortDir, metrics[0])
);
if (hasTime) {
if (['Trend', 'Area Line'].indexOf(state.selectedChart) > -1) {
groups.unshift(
cf
.Attribute(state.selectedTimeAttr)
.limit(state.timeLimit)
.func(state.timeFunc)
.sort('asc', state.selectedTimeAttr)
);
} else {
groups.push(
cf
.Attribute(state.selectedTimeAttr)
.limit(state.timeLimit)
.func(state.timeFunc)
.sort('asc', state.selectedTimeAttr)
);
}
}
// configure query
// Since this visualization already has the element defined
// there's no need to do that again
let myChart = cf.getVisualization(selectedChart);
myChart
.groupby(...groups)
.metrics(...metrics)
.graph(state.selectedChart)
.execute().then(e => {
hideLoader(e);
});
};
let updateOptions = function () {
let state = chartState[selectedChart];
$('#chart-select').val(state.selectedChart);
if (['Trend', 'Area Line'].indexOf(state.selectedChart) > -1) {
$('#time-card-ct').show();
} else {
$('#time-card-ct').hide();
}
$('#attribute-select').val(state.selectedAttribute);
$('#attribute-limit').val(state.limit);
$('#attribute-sort-dir').val(state.sortDir);
$('#time-attribute-select').val(state.selectedTimeAttr);
$('#time-attribute-limit').val(state.timeLimit);
$('#time-attribute-func').val(state.timeFunc);
$('#metric-select').val(state.selectedMetric);
$('#metric-func-select').val(state.selectedMetricFunc);
$('#metric2-select').val(state.selectedMetric2);
$('#metric2-func-select').val(state.selectedMetricFunc2);
$('#metric3-select').val(state.selectedMetric3);
$('#metric3-func-select').val(state.selectedMetricFunc3);
if (['Scatter Plot'].indexOf(selectedChart) > -1) {
$('#metric2-card-ct').show();
$('#metric3-card-ct').show();
} else if (['Bars and Line'].indexOf(selectedChart) > -1) {
$('#metric2-card-ct').show();
$('#metric3-card-ct').show();
} else {
$('#metric2-card-ct').hide();
$('#metric3-card-ct').hide();
}
};
let onSelectChart = function (event) {
$('.chart').removeClass('active');
$(this).addClass('active');
selectedChart = $(this).prop('id');
updateOptions();
};
$(document).ready(function () {
let providers = [{
name: 'Elasticsearch',
provider: 'elasticsearch',
url: 'https://chartfactor.com/elastic/'
}];
cf.setProviders(providers);
// Create the 2 charts
cf
.provider('Elasticsearch')
.source('ticket_sales')
.element('chart1');
cf
.provider('Elasticsearch')
.source('ticket_sales')
.element('chart2');
selectedChart = 'chart2';
updateOptions();
updateChart();
selectedChart = 'chart1';
updateOptions();
updateChart();
$('#chart1,#chart2').on('click', onSelectChart);
$('.options select,.options input').on('change', updateChart);
});
|
html {
font-size: 12px;
}
@media only screen and (max-width: 760px) {
.options {
justify-content: left !important;
}
}
.options {
position: relative;
display: flex;
justify-content: center;
min-width: 530px;
margin-top: 5px;
margin-bottom: 2px;
}
.card {
margin: 0 2px;
}
.options-wrapper {
width: 100vw;
overflow-x: auto;
}
.property-container {
margin: 2px;
}
.card-body {
padding: 6px;
display: inline-flex;
border-bottom: solid 1px;
border-right: solid 1px;
border-left: solid 1px;
height: 35px;
}
.card-header {
text-align: center;
padding: 4px;
color: white;
background: #24282D;
margin: 0;
}
.grid-input {
border-radius: 3px;
border-style: solid;
border-width: 1px;
border-color: lightgray;
width: 40px;
}
.charts-div {
position: relative;
display: flex;
justify-content: center;
height: calc(100% - 100px);
width: 100%;
margin-bottom: 15px;
}
.chart-wrapper {
position: relative;
width: calc(50% - 8px);
height: 100%;
margin: 0 2px;
padding: 2px;
}
.chart {
padding-top: 8px;
width: 100%;
height: 100%;
}
html, body {
height: 100%;
margin: 0;
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
-webkit-font-smoothing: antialiased;
}
.loader {
position: absolute;
top: calc(50% - 30px);
left: calc(50% - 30px);
border: 16px solid #f3f3f3;
/* Light grey */
border-top: 16px solid #24282D;
/* Blue */
border-radius: 50%;
width: 60px;
height: 60px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.chart2 {
width: calc(100% - 4px);
height: 60%;
}
.chart.active {
border-top: 2px solid #24282D;
}
div::-webkit-scrollbar {
width: 5px;
height: 5px;
border-radius: 5px;
}
div::-webkit-scrollbar-track-piece {
background: #E9ECEF;
border-radius: 5px;
}
div::-webkit-scrollbar-thumb {
background: #888;
border-radius: 5px;
}
|
The Ticket Sales table is derived from the Amazon’s Sample Database for Redshift. The data represents ticket sales activity for the fictional TICKIT web site, where users buy and sell tickets online for different types of events.