apify-rbc-pages
Version:
Convert data on the RBC page of choice to a JSON
276 lines (257 loc) • 8.59 kB
HTML
<html lang="en">
<head>
<style>
body,
.legend-container {
display: flex;
flex-direction: column;
}
#chart + .legend-container {
margin-top: 50px;
}
.legend-title {
font-size: 12px;
font-family: Roboto;
}
.legend-title + .legend {
margin-top: 12px;
}
.legend {
display: flex;
align-content: center;
flex-direction: row-reverse;
justify-content: flex-end;
padding-left: 10px;
}
.legend.focused {
background-color: #ffd440;
}
.legend + .legend {
margin-top: 12px;
}
label {
display: inline-flex;
align-items: center;
margin-left: 12px;
font-family: Roboto;
font-size: 14px;
cursor: pointer;
}
label.disabled {
text-decoration: line-through;
pointer-events: none;
}
input[type='checkbox'] {
display: none;
}
.legend-color {
height: 24px;
width: 24px;
border-radius: 2px;
cursor: pointer;
}
label.disabled ~ .legend-color {
background-color: rgba(0, 0, 0, 0) ;
border: 2px dotted gray;
box-sizing: border-box;
}
</style>
<script
type="text/javascript"
src="https://www.gstatic.com/charts/loader.js"
></script>
<script type="text/javascript" src="dataTable.js"></script>
</head>
<body>
<div id="chart"></div>
<div class="legend-container">
<section class="legend-title">filter</section>
</div>
<script type="text/javascript">
const allColors = [
'#3366CC',
'#DC3912',
'#FF9900',
'#109618',
'#990099',
'#3B3EAC',
'#0099C6',
'#DD4477',
'#66AA00',
'#B82E2E',
'#316395',
'#994499',
'#22AA99',
'#AAAA11',
'#6633CC',
'#E67300',
'#8B0707',
'#329262',
'#5574A6',
'#3B3EAC',
];
const columnColors = Array.from(dataTableColumns.slice(1)).map(
(_, idx) => allColors[idx % allColors.length]
);
// initial legend select state
const showMap = new Map(
Array.from(dataTableColumns.slice(1)).map((_, idx) => [idx, true])
);
function drawLegends(chart, dataTable, classicOptions) {
const legends = dataTableColumns.slice(1).map(([_, title]) => title);
const legendContainerElement = document.querySelector(
'.legend-container'
);
legends.forEach((legend, idx) => {
const legendElement = document.createElement('section');
legendElement.className = 'legend';
legendContainerElement.appendChild(legendElement);
const labelElement = document.createElement('label');
labelElement.setAttribute('for', 'legend_' + idx);
labelElement.appendChild(document.createTextNode(legend));
labelElement.onclick = () => {
// update legend
[...document.querySelectorAll('.legend')].forEach((targetElement) =>
targetElement.classList.remove('focused')
);
legendElement.classList.add('focused');
// update chart
const skip = [...showMap].filter(
([_idx, show]) => _idx < idx && !show
).length;
chart.setSelection([
{ row: dataTable.getNumberOfRows() - 1, column: idx + 1 - skip },
]);
document
.querySelector('#chart')
.scrollIntoView({ behavior: 'smooth', block: 'center' });
};
legendElement.appendChild(labelElement);
const checkboxElement = document.createElement('input');
checkboxElement.setAttribute('type', 'checkbox');
checkboxElement.setAttribute('id', 'legend_' + idx);
checkboxElement.setAttribute('name', 'legend_' + idx);
checkboxElement.setAttribute('value', `${idx}`);
legendElement.appendChild(checkboxElement);
const colorElement = document.createElement('div');
colorElement.className = 'legend-color';
colorElement.style.backgroundColor = columnColors[idx];
colorElement.onclick = () => {
[...document.querySelectorAll('.legend')].forEach((targetElement) =>
targetElement.classList.remove('focused')
);
const show = !showMap.get(idx);
if (!show && [...showMap].filter(([_, show]) => show).length < 2) {
return;
}
showMap.set(idx, show);
document.querySelector(
`label[for="legend_${idx}"]`
).className = showMap.get(idx) ? '' : 'disabled';
updateLineChart(chart, dataTable, classicOptions);
};
legendElement.appendChild(colorElement);
});
}
function createDataTable({ DataTable }) {
const dataTable = new DataTable();
dataTableColumns.forEach(([type, title]) =>
dataTable.addColumn(type, title)
);
dataTableRows.forEach((row) => {
dataTable.addRow(
row.map((col, idx) => {
const [type] = dataTableColumns[idx];
switch (type) {
case 'date':
return new Date(col);
default:
return col;
}
})
);
});
return dataTable;
}
function drawLineChart() {
google.charts.load('current', { packages: ['line'] });
return new Promise((resolve) =>
google.charts.setOnLoadCallback(() => {
const dataTable = createDataTable(google.visualization);
const classicOptions = {
chart: {
title,
subtitle,
},
vAxis: {
viewWindow: {
min: 0,
},
},
colors: columnColors,
legend: { position: 'none' },
height: 400,
};
const chart = new google.charts.Line(
document.getElementById('chart')
);
chart.draw(
dataTable,
google.charts.Line.convertOptions(classicOptions)
);
resolve([chart, dataTable, classicOptions]);
})
);
}
function updateLineChart(chart, dataTable, classicOptions) {
const view = new google.visualization.DataView(dataTable);
const hideColumns = [...showMap.entries()]
.filter(([_, show]) => !show)
.map(([idx]) => idx);
view.hideColumns(hideColumns.map((idx) => idx + 1));
const colors = [...columnColors];
hideColumns.reverse().forEach((idx) => colors.splice(idx, 1));
chart.draw(
view,
google.charts.Line.convertOptions({ ...classicOptions, colors })
);
}
function scrollToLegend(chart) {
[...document.querySelectorAll('.legend')].forEach((targetElement) =>
targetElement.classList.remove('focused')
);
const [cell] = chart.getSelection();
const { column } = cell || {}; // a column refers to a line
// if click on chart background
if (!column) {
return;
}
const targetElement = document.querySelector(
`label[for=legend_${column - 1}]`
).parentElement;
targetElement.classList.add('focused');
targetElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
drawLineChart().then(([chart, dataTable, classicOptions]) => {
setTimeout(() => window.scrollTo(0, 0), 0);
window.onresize = () =>
updateLineChart(chart, dataTable, classicOptions);
google.visualization.events.addListener(chart, 'select', () => {
const [{ row, column }] = chart.getSelection() || {};
if (!column) {
return;
}
// if user clicks on a dot, OR
// chart.setSelection([{row, column}]) fires a 'select' event, which is an API bug
if (row !== null && row !== undefined) {
return;
}
// if the user clicks on the line
scrollToLegend(chart);
});
drawLegends(chart, dataTable, classicOptions);
});
</script>
</body>
</html>