sejm-calculator/src/index.ts

180 wiersze
5.4 KiB
TypeScript
Czysty Zwykły widok Historia

2020-10-18 19:06:38 +00:00
import Chartist, {ChartistStatic, IChartistSvg} from 'chartist';
2019-09-24 17:21:02 +00:00
import {committees, constituencies} from './data';
2020-11-02 16:56:39 +00:00
import calculateMandates from './mandates';
import constituencyTemplate from './templates/constituency.pug';
import tableTemplate from './templates/table.pug';
import './styles/styles.css';
2019-08-29 17:07:09 +00:00
2019-09-24 09:57:02 +00:00
const clearResults = (bar: ChartistStatic['Bar'], pie: ChartistStatic['Pie']) => {
2020-10-18 19:06:38 +00:00
document.querySelectorAll<HTMLTableDataCellElement>('tr td:last-child').forEach((td) => {
2020-11-02 16:56:39 +00:00
td.innerHTML = ''; // eslint-disable-line no-param-reassign
2019-09-24 17:21:02 +00:00
});
2019-09-24 09:57:02 +00:00
bar.detach();
pie.detach();
2019-09-24 17:21:02 +00:00
document.getElementById('support-bar-chart')!.innerHTML = '';
2019-09-24 09:57:02 +00:00
document.getElementById('division-pie-chart')!.innerHTML = '';
2019-09-24 17:21:02 +00:00
document.getElementById('constituency-results')!.innerHTML = '';
2020-10-18 19:06:38 +00:00
};
2019-09-24 17:21:02 +00:00
2020-11-06 23:08:59 +00:00
const displayResults = (mandates: number[]) => {
2019-09-24 09:57:02 +00:00
mandates.forEach((value, index) => {
const committeeId = committees[index].id;
const td = document.querySelector<HTMLTableDataCellElement>(`tr.${committeeId} td:last-child`);
if (td) td.textContent = value.toString();
});
2020-11-06 23:08:59 +00:00
};
2019-09-24 09:57:02 +00:00
2020-11-06 23:08:59 +00:00
const displayUrl = (support: number[]) => {
const searchParams = new URLSearchParams();
support.forEach((s, i) => {
if (s > 0) searchParams.append(committees[i].id, s.toString());
});
const url = `${window.location.href}?${searchParams}`;
document.getElementById('url')!.innerHTML = `Link do wyników: ${url.link(url)}`;
};
const displayBarChart = (support: number[]) => {
2020-10-28 19:58:47 +00:00
const sortedSupport = committees
.map((c, i) => ({
label: c.shortName,
support: {value: support[i], className: c.id},
}))
.filter((s) => s.support.value && s.support.value > 0)
.sort((a, b) => b.support.value - a.support.value);
2019-09-07 18:30:44 +00:00
const barChartData = {
2020-10-28 16:32:32 +00:00
labels: sortedSupport.map((ss) => ss.label),
series: sortedSupport.map((ss) => ss.support),
2019-09-07 18:30:44 +00:00
};
2019-09-21 18:39:32 +00:00
const barChartOptions = {
distributeSeries: true,
};
2020-11-06 23:08:59 +00:00
const barChart = new Chartist.Bar('#support-bar-chart', barChartData, barChartOptions);
barChart.on('draw', (data: {type: string; element: IChartistSvg}) => {
2019-09-25 18:45:22 +00:00
if (data.type === 'bar') {
data.element.attr({
style: 'stroke-width: 30px',
});
}
});
2020-11-06 23:08:59 +00:00
return barChart;
};
2019-09-07 18:30:44 +00:00
2020-11-06 23:08:59 +00:00
const displayPieChart = (mandates: number[]) => {
2020-10-28 19:58:47 +00:00
const sortedMandates = committees
.map((c, i) => ({
label: c.shortName,
mandates: {value: mandates[i], className: c.id},
}))
.filter((m) => m.mandates.value && m.mandates.value > 0)
.sort((a, b) => b.mandates.value - a.mandates.value);
2019-09-07 18:30:44 +00:00
const pieChartData = {
2020-10-28 16:32:32 +00:00
series: sortedMandates.map((sm) => sm.mandates),
2019-09-07 18:30:44 +00:00
};
const pieChartOptions = {
donut: true,
donutWidth: 60,
startAngle: 270,
total: 460 * 2,
2020-10-28 16:32:32 +00:00
labelInterpolationFnc: (value: number, index: number) => (
value < 15 ? '' : `${sortedMandates[index].label} ${value}`
),
2019-09-07 18:30:44 +00:00
};
2020-11-06 23:08:59 +00:00
return new Chartist.Pie('#division-pie-chart', pieChartData, pieChartOptions);
};
const displayConstituencyResults = () => {
const container = document.getElementById('constituency-results');
constituencies.forEach((constituency, constituenyIndex) => {
const data = (constituency.mandates && constituency.support)
? constituency.mandates.map((mandates, committeeIndex) => ({
committee: committees[committeeIndex].name,
support: (constituency.support as number[])[committeeIndex],
mandates,
}))
: [];
data.sort((a, b) => b.support - a.support);
container!.insertAdjacentHTML('beforeend', constituencyTemplate({
number: constituenyIndex + 1,
name: constituency.name,
size: constituency.size,
data,
}));
});
};
const validate = (inputs: NodeListOf<HTMLInputElement>, support: number[]) => {
const supportSum = support.reduce((a, b) => a + b, 0);
if (supportSum > 100) {
inputs.forEach((input) => input.setCustomValidity('Suma poparcia nie może przekraczać 100%'));
return false;
}
if (supportSum <= 0) {
inputs.forEach((input) => input.setCustomValidity('Suma poparcia musi być wyższa niż 0%'));
return false;
}
inputs.forEach((input) => input.setCustomValidity(''));
return document.querySelector<HTMLFormElement>('#support-form')!.checkValidity();
};
const calculate = () => {
const inputs = document.querySelectorAll<HTMLInputElement>('#support-form input');
const support = Array
.from(inputs)
.map((input) => parseFloat(input.value))
.map((value) => (Number.isNaN(value) ? 0 : value));
if (!validate(inputs, support)) return;
const mandates = calculateMandates(support);
displayResults(mandates);
displayUrl(support);
const barChart = displayBarChart(support);
const pieChart = displayPieChart(mandates);
2019-09-24 09:57:02 +00:00
2019-09-24 17:21:02 +00:00
displayConstituencyResults();
2020-10-18 19:06:38 +00:00
inputs.forEach((input) => input.addEventListener('input', () => {
2020-11-06 23:08:59 +00:00
clearResults(barChart, pieChart);
2019-09-24 17:21:02 +00:00
}));
2020-10-18 19:06:38 +00:00
};
2019-09-07 18:30:44 +00:00
2020-11-06 23:08:59 +00:00
const handleCalculateButtonClick = (event: Event) => {
event.preventDefault();
calculate();
};
const generateTable = () => {
2020-10-28 16:32:32 +00:00
const form: HTMLElement = document.getElementById('support-form')!;
form.insertAdjacentHTML('afterbegin', tableTemplate({
committees,
}));
2020-10-18 19:06:38 +00:00
};
2020-11-06 23:08:59 +00:00
const loadResultsFromUrl = () => {
if (window.location.search) {
const searchParams = new URLSearchParams(window.location.search);
searchParams.forEach((value, key) => {
const input = document.querySelector<HTMLInputElement>(`tr.${key} td:nth-child(2) input`);
if (input) input.value = value;
});
calculate();
}
};
2019-09-07 18:30:44 +00:00
const bindActions = () => {
document
.querySelector('#calculate-button')!
.addEventListener('click', handleCalculateButtonClick);
2020-10-18 19:06:38 +00:00
};
2019-09-07 18:30:44 +00:00
document.addEventListener('DOMContentLoaded', () => {
generateTable();
2020-11-06 23:08:59 +00:00
loadResultsFromUrl();
2019-09-07 18:30:44 +00:00
bindActions();
});