Add constituency map

pull/15/head
Michał Górny 2023-10-10 12:16:45 +02:00
rodzic 8f91e48048
commit d74f6d567e
9 zmienionych plików z 206 dodań i 61 usunięć

Wyświetl plik

@ -17,5 +17,6 @@ module.exports = {
'^.+\\.[jt]s$': 'ts-jest', '^.+\\.[jt]s$': 'ts-jest',
'\\.(pug)$': 'jest-transform-pug', '\\.(pug)$': 'jest-transform-pug',
'.+\\.(css)$': 'jest-transform-stub', '.+\\.(css)$': 'jest-transform-stub',
'^.+\\.svg$': 'jest-transform-stub',
}, },
}; };

Wyświetl plik

@ -1,8 +1,10 @@
import {BarChart, PieChart, Svg} from 'chartist'; import {BarChart, PieChart, Svg} from 'chartist';
import {benchSort, committees, constituencies} from './data'; import {benchSort, committees, constituencies} from './data';
import calculateMandates from './mandates'; import calculateMandates from './mandates';
import constituencyResultsTemplate from './templates/cresults.pug';
import constituencyTemplate from './templates/constituency.pug'; import constituencyTemplate from './templates/constituency.pug';
import tableTemplate from './templates/table.pug'; import tableTemplate from './templates/table.pug';
import constituenciesMap from './images/c41.svg';
import './styles/styles.css'; import './styles/styles.css';
const {location} = window; const {location} = window;
@ -33,7 +35,7 @@ export const clearResults = (): void => {
document.getElementById('support-bar-chart')!.innerHTML = ''; document.getElementById('support-bar-chart')!.innerHTML = '';
document.getElementById('division-pie-chart')!.innerHTML = ''; document.getElementById('division-pie-chart')!.innerHTML = '';
const constituencyResultContainer = document.getElementById('constituency-results'); const constituencyResultContainer = document.getElementById('constituency-results');
if (constituencyResultContainer) constituencyResultContainer.innerHTML = ''; if (constituencyResultContainer) constituencyResultContainer.innerHTML = constituencyResultsTemplate();
if (location.search) { if (location.search) {
const urlWithoutSearchString = location.href.split('?')[0]; const urlWithoutSearchString = location.href.split('?')[0];
window.history.pushState('', '', urlWithoutSearchString); window.history.pushState('', '', urlWithoutSearchString);
@ -126,21 +128,57 @@ const displayPieChart = (mandates: number[]) => {
const displayConstituencyResults = () => { const displayConstituencyResults = () => {
const container = document.getElementById('constituency-results'); const container = document.getElementById('constituency-results');
if (container) { if (container) {
constituencies.forEach((constituency, constituenyIndex) => { container.innerHTML = constituencyResultsTemplate();
const data = (constituency.mandates && constituency.support) const constituencyResult = document.getElementById('constituency-result')!;
? constituency.mandates.map((mandates, committeeIndex) => ({
committee: committees[committeeIndex].name, const mapObject = document.getElementById('constituencies-map')! as HTMLObjectElement;
support: (constituency.support as number[])[committeeIndex], mapObject.setAttribute('data', constituenciesMap);
mandates, mapObject.addEventListener('load', () => {
})) const svgDocument = mapObject.contentDocument!;
: []; const constituencyNumber = svgDocument.getElementById('constituency-number')!;
data.sort((a, b) => b.support - a.support); const constituencyName = svgDocument.getElementById('constituency-name')!;
container.insertAdjacentHTML('beforeend', constituencyTemplate({ const paths = svgDocument.querySelectorAll<SVGPathElement>('svg path');
number: constituenyIndex + 1, paths.forEach((path) => {
name: constituency.name, const cid = parseInt(path.dataset.cid!, 10);
size: constituency.size, const constituency = constituencies[cid - 1];
data, const data = (constituency.mandates && constituency.support)
})); ? constituency.mandates.map((mandates, committeeIndex) => ({
committee: committees[committeeIndex],
support: (constituency.support as number[])[committeeIndex],
mandates,
}))
: [];
data.sort((a, b) => b.support - a.support);
if (data[0].support === data[1].support) {
path.style.fill = 'lightgray';
} else {
path.classList.add(data[0].committee.id);
if (data[0].support >= 50) path.classList.add('bright50');
else if (data[0].support < 40) path.classList.add('bright30');
}
path.addEventListener('mouseover', (event) => {
const pathElement = event.target as SVGPathElement;
pathElement.style.stroke = '#444';
constituencyNumber.innerHTML = `Okręg nr ${pathElement.dataset.cid}`;
constituencyName.innerHTML = pathElement.dataset.cname as string;
});
path.addEventListener('mouseout', (event) => {
const pathElement = event.target as SVGPathElement;
pathElement.style.stroke = '#fff';
constituencyNumber.innerHTML = '';
constituencyName.innerHTML = '';
});
path.addEventListener('click', (event) => {
(event.target as SVGPathElement).style.stroke = '#ccc';
constituencyResult.innerHTML = constituencyTemplate({
number: cid,
name: constituency.name,
size: constituency.size,
data,
});
});
});
}); });
} }
}; };

53
src/images/c41.svg 100644

File diff suppressed because one or more lines are too long

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 225 KiB

Wyświetl plik

@ -7,7 +7,8 @@ body {
font-size: 15px; font-size: 15px;
} }
.container { main,
footer {
width: 100%; width: 100%;
max-width: 58rem; max-width: 58rem;
margin: 1rem auto; margin: 1rem auto;
@ -21,6 +22,10 @@ body {
padding: 0.25rem auto 1rem; padding: 0.25rem auto 1rem;
} }
.updates-texts {
font-size: 13.5px;
}
.updates { .updates {
margin-top: -10px; margin-top: -10px;
list-style-type: none; list-style-type: none;
@ -44,6 +49,8 @@ li {
display: flex; display: flex;
height: auto; height: auto;
margin-bottom: 1rem; margin-bottom: 1rem;
z-index: -1;
pointer-events: none;
} }
.ct-chart:empty { .ct-chart:empty {
@ -90,40 +97,48 @@ h2 {
justify-content: space-around; justify-content: space-around;
} }
#constituency-results::after { #constituency-result {
content: ''; flex: 1;
flex: auto; }
max-width: 26rem;
margin: 0.5rem 0; #constituency-result .info {
padding: 0.5rem 1rem; margin-top: 32px;
border: 1px solid white; text-align: center;
}
#constituencies-map {
flex: 1;
max-width: min(438px, 90vw);
} }
.constituency { .constituency {
display: flex; max-width: 25rem;
flex-wrap: wrap; margin: 0.5rem auto;
max-width: 26rem;
margin: 0.5rem 0;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
border: 1px solid grey;
}
.constituency-data {
width: 11rem;
} }
.constituency table { .constituency table {
margin-left: 1rem; border-collapse: collapse;
} }
.constituency p, .constituency p,
.constituency th,
.constituency td { .constituency td {
font-size: 14px; font-size: 14px;
} }
.constituency th,
.constituency td {
padding: 4px 12px;
border: 1px solid #888;
text-align: left;
}
.constituency th:nth-child(2),
.constituency th:last-child,
.constituency td:nth-child(2),
.constituency td:last-child { .constituency td:last-child {
width: 2rem; width: 2rem;
padding-left: 1rem;
text-align: right; text-align: right;
} }
@ -149,36 +164,55 @@ h2 {
height: 2rem; height: 2rem;
} }
#support-form table td:nth-child(2) {
white-space: nowrap;
}
#support-form table td:last-child { #support-form table td:last-child {
text-align: right; text-align: right;
} }
:root {
--pis: #31469b;
--ko: #fca43f;
--lewica: #d44;
--td: #8bd32e;
--konfederacja: #655;
--mn: #aaa;
}
.pis { .pis {
stroke: #263778; stroke: var(--pis);
fill: var(--pis);
} }
.ko { .ko {
stroke: #fca43f; stroke: var(--ko);
fill: var(--ko);
} }
.lewica { .lewica {
stroke: #d44; stroke: var(--lewica);
fill: var(--lewica);
} }
.td { .td {
stroke: #8bd32e; stroke: var(--td);
fill: var(--td);
} }
.konfederacja { .konfederacja {
stroke: #655; stroke: var(--konfederacja);
fill: var(--konfederacja);
} }
.mn { .mn {
stroke: #aaa; stroke: var(--mn);
fill: var(--mn);
}
.bright30 {
filter: brightness(125%);
}
.bright50 {
filter: brightness(75%);
} }
.bar-value { .bar-value {
@ -189,7 +223,7 @@ footer {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-between; justify-content: space-between;
margin: 0.5rem 1rem; padding: 1rem 1.5rem;
} }
address { address {

4
src/svg.ts 100644
Wyświetl plik

@ -0,0 +1,4 @@
declare module '*.svg' {
const content: string;
export default content;
}

Wyświetl plik

@ -1,10 +1,14 @@
.constituency .constituency
.constituency-data h2 Okręg #{number} #{name}
h2 Okręg #{number} #{name}
p Łącznie #{size} mandatów
table table
tr
th Komitet
th Poparcie
th Mandaty
each datum in data each datum in data
tr tr
td #{datum.committee} td #{datum.committee.name}
td #{datum.support.toLocaleString('pl', {minimumFractionDigits: 1, maximumFractionDigits: 1})}%
td #{datum.mandates} td #{datum.mandates}
p Łącznie #{size} mandatów

Wyświetl plik

@ -1,4 +1,4 @@
div(class=(embed ? 'container embed' : 'container')) main(class=(embed ? 'embed' : ''))
unless embed unless embed
h1 Kalkulator mandatów w wyborach do Sejmu h1 Kalkulator mandatów w wyborach do Sejmu
@ -12,8 +12,8 @@ div(class=(embed ? 'container embed' : 'container'))
wtedy poparcie wyższe/niższe niż w całym kraju, również w tej symulacji będzie miało w tym wtedy poparcie wyższe/niższe niż w całym kraju, również w tej symulacji będzie miało w tym
okręgu odpowiednio wyższe/niższe poparcie. Pozwala to na sporządzenie szczegółowej prognozy okręgu odpowiednio wyższe/niższe poparcie. Pozwala to na sporządzenie szczegółowej prognozy
podziału mandatów na poziomie poszczególnych okręgów. podziału mandatów na poziomie poszczególnych okręgów.
p Aktualizacje: p.updates-texts Aktualizacje:
ol.updates ol.updates.updates-texts
li. li.
2019-11-15: od teraz w prognozie brane są pod uwagę wyniki wyborów do Sejmu z 2019 roku. 2019-11-15: od teraz w prognozie brane są pod uwagę wyniki wyborów do Sejmu z 2019 roku.
li. li.
@ -37,6 +37,7 @@ div(class=(embed ? 'container embed' : 'container'))
unless embed unless embed
#constituency-results #constituency-results
footer unless embed
a.github(href='https://github.com/drastus/sejm-calculator') Kod źródłowy footer
address Kontakt: zrchos na serwerze gmail.com a.github(href='https://github.com/drastus/sejm-calculator') Kod źródłowy
address Kontakt: zrchos na serwerze gmail.com

Wyświetl plik

@ -0,0 +1,4 @@
object(id="constituencies-map")
#constituency-result
p.info
em Kliknij okręg, by zobaczyć szczegółowe wyniki.

Wyświetl plik

@ -27,6 +27,16 @@ export default {
exclude: /node_modules/, exclude: /node_modules/,
use: '@webdiscus/pug-loader', use: '@webdiscus/pug-loader',
}, },
{
test: /\.svg$/,
type: 'asset/resource',
generator: {
filename: (name: {filename: string}) => {
const filePath = name.filename.split('/').slice(1, -1).join('/');
return `${filePath}/[name][ext]`;
},
},
},
{ {
test: /\.css$/, test: /\.css$/,
exclude: /node_modules/, exclude: /node_modules/,
@ -65,11 +75,7 @@ export default {
devServer: { devServer: {
watchFiles: ['src/**/*'], watchFiles: ['src/**/*'],
client: { client: {
overlay: { overlay: false,
warnings: false,
errors: true,
runtimeErrors: true,
},
}, },
}, },
}; };