Этот коммит содержится в:
Maarten 2024-09-27 21:26:02 +02:00
родитель 85e95811ff
Коммит f75f12e940
2 изменённых файлов: 70 добавлений и 25 удалений

Просмотреть файл

@ -1,50 +1,87 @@
<script> <script>
import { scaleUtc } from 'd3-scale'; import { scaleUtc, scalePoint, scaleOrdinal } from 'd3-scale';
import { extent } from 'd3-array'; import { extent } from 'd3-array';
import { utcFormat } from 'd3-time-format'; import { utcFormat } from 'd3-time-format';
import { fade } from 'svelte/transition'
export let cases; export let cases;
const margins = { const margins = {
top: 24, top: 24,
right: 24, right: 24,
bottom: 24, bottom: 38,
left: 24 left: 120
}; };
let width; let width;
let height = 120; let height = 280;
$: dateExtent = extent(cases.map((d) => new Date(d.attribution_date))); $: dateExtent = extent(cases.map((d) => new Date(d.attribution_date)));
$: xScale = scaleUtc(dateExtent, [0, width - margins.right - margins.left]); $: xScale = scaleUtc(dateExtent, [0, width - margins.right - margins.left]);
$: ticks = xScale.ticks(5); $: ticks = xScale.ticks(5);
const actorNations = ['China', 'Iran', 'North Korea', 'Russia'];
const colors = ['#0f4c8a', '#8a0f8a', '#8a4d0f', '#0f8a0f'];
let yScale = scalePoint(actorNations, [height - margins.bottom - margins.top, 0]).padding(1);
let colorScale = scaleOrdinal(actorNations, colors);
</script> </script>
<div class="timeline-container" bind:clientWidth={width}> <div class="timeline-container" bind:clientWidth={width}>
<svg {width} {height}> <svg {width} {height}>
{#if xScale} {#if xScale}
<g transform={`translate(${margins.left},${margins.top})`}> <g transform={`translate(${margins.left},${margins.top})`}>
<line x1={0} x2={width} y1={0} y2={0} stroke={'#000000'} stroke-width={2}></line> <!--line
x1={0}
x2={width}
y1={height - margins.bottom - margins.top}
y2={height - margins.bottom - margins.top}
stroke={'#000000'}
stroke-width={2}
></line-->
{#each actorNations as nation}
<line
x1={0}
x2={width}
y1={yScale(nation)}
y2={yScale(nation)}
style:stroke={colorScale(nation)}
stroke-width={2}
opacity={0.3}
></line>
<text
class="country-label"
x={-10}
y={yScale(nation) + 4}
text-anchor={"end"}
fill={colorScale(nation)}
>{nation}</text>
{/each}
{#each ticks as tick} {#each ticks as tick}
<line <line
x1={xScale(tick)} x1={xScale(tick)}
x2={xScale(tick)} x2={xScale(tick)}
y1={0} y1={height - margins.bottom - margins.top}
y2={12} y2={height - margins.bottom - margins.top + 10}
stroke={'#000000'} stroke={'#bbbbbb'}
stroke-width={2} stroke-width={1}
></line> ></line>
<text x={xScale(tick)} y={32} text-anchor={'middle'}>{utcFormat('%b %d')(tick)}</text> <text
class="time-axis-tick-label"
x={xScale(tick)}
y={height - margins.bottom - margins.top + 24}
text-anchor={'middle'}>{utcFormat('%b')(tick)}</text
>
{/each} {/each}
{#each cases as attrCase} {#each cases as attrCase}
{#if attrCase.show} {#if attrCase.show}
<a href={'#case-' + attrCase.attribution_id}> <a href={'#case-' + attrCase.attribution_id} transition:fade>
<circle <circle
cx={xScale(new Date(attrCase.attribution_date))} cx={xScale(new Date(attrCase.attribution_date))}
cy={0} cy={yScale(attrCase.actor_nation[0])}
r={6} r={6}
fill={'#000000'} style:fill={colorScale(attrCase.actor_nation[0])}
stroke={'#ffffff'} stroke={'#ffffff'}
stroke-width={2} stroke-width={2}
></circle> ></circle>
@ -60,4 +97,12 @@
.timeline-container { .timeline-container {
width: 100%; width: 100%;
} }
.country-label {
font-weight: bold;
font-size: 0.9rem;
}
.time-axis-tick-label {
font-size: 0.8rem;
fill: #888888;
}
</style> </style>

Просмотреть файл

@ -48,7 +48,7 @@
methodFilter.init(cases, 'methods') methodFilter.init(cases, 'methods')
$attributionScoreFilter = attributionScoreDef; $attributionScoreFilter = attributionScoreDef;
//console.log(cases.map(d => d.attribution_date)) console.log(cases.map(d => d.campaign))
}); });
$: if (cases) { $: if (cases) {
@ -104,8 +104,8 @@
<div class="container"> <div class="container">
<div class="field has-addons"> <div class="field has-addons">
<div class="buttons has-addons"> <div class="buttons has-addons">
<button class={displayDataAs == "Table" ? "button is-success is-selected" : "button"} on:click={() => {displayDataAs = "Table"}}>Table</button> <button class={displayDataAs == "Table" ? "button is-dark is-selected is-small" : "button is-small"} on:click={() => {displayDataAs = "Table"}}>Table</button>
<button class={displayDataAs == "Cards" ? "button is-success is-selected" : "button"} on:click={() => {displayDataAs = "Cards"}}>Cards</button> <button class={displayDataAs == "Cards" ? "button is-dark is-selected is-small" : "button is-small"} on:click={() => {displayDataAs = "Cards"}}>Cards</button>
</div> </div>
</div> </div>
</div> </div>