Этот коммит содержится в:
Maarten 2024-10-11 10:00:39 +02:00
родитель 3ddd9c467a
Коммит ad33923c37
3 изменённых файлов: 125 добавлений и 80 удалений

20
src/lib/components/CardModal.svelte Обычный файл
Просмотреть файл

@ -0,0 +1,20 @@
<script>
import CaseCard from "./CaseCard.svelte";
export let modalOpen
export let activeCaseData
function closeModal(){
modalOpen = false
}
</script>
<div id="card-modal" class={modalOpen ? "modal is-active" : "modal"}>
<div class="modal-background" on:click={closeModal}></div>
<div class="modal-content">
{#if activeCaseData}
<CaseCard cardData={activeCaseData} expanded={true} ></CaseCard>
{/if}
</div>
<div class="modal-close" on:click={closeModal}></div>
</div>

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

@ -3,7 +3,16 @@
import { utcFormat } from 'd3-time-format';
import { platformFilter, actorNationFilter, sourceFilter } from '../../stores/filters';
import ScoreBar from '$lib/components/ScoreBar.svelte';
export let cardData;
export let expanded;
export let modalOpen;
export let activeCaseData
let openCase = function(caseID){
modalOpen = true
activeCaseData = cardData
}
</script>
<div class="card" transition:fade id={'case-' + cardData.attribution_id}>
@ -12,27 +21,29 @@
<h2 class="is-size-5">{cardData.short_title}</h2>
</div>
</div>
<div class="score-bars">
<div class="score-bar-wrapper">
<ScoreBar value={cardData.credibility} maxValue={5} />
<p>Credibility</p>
</div>
<div class="score-bar-wrapper">
<ScoreBar value={cardData.objectivity} maxValue={3} />
<p>Objectivity</p>
</div>
<div class="score-bar-wrapper">
<ScoreBar value={cardData.evidence} maxValue={5} />
<p>Evidence</p>
</div>
<div class="score-bar-wrapper">
<ScoreBar value={cardData.transparency} maxValue={5} />
<p>Transparency</p>
</div>
<!--span class="score-info-icon disable-select" on:click|self={() => scoreQuestionsExpanded = !scoreQuestionsExpanded}>
{#if expanded}
<div class="score-bars">
<div class="score-bar-wrapper">
<ScoreBar value={cardData.credibility} maxValue={5} />
<p>Credibility</p>
</div>
<div class="score-bar-wrapper">
<ScoreBar value={cardData.objectivity} maxValue={3} />
<p>Objectivity</p>
</div>
<div class="score-bar-wrapper">
<ScoreBar value={cardData.evidence} maxValue={5} />
<p>Evidence</p>
</div>
<div class="score-bar-wrapper">
<ScoreBar value={cardData.transparency} maxValue={5} />
<p>Transparency</p>
</div>
<!--span class="score-info-icon disable-select" on:click|self={() => scoreQuestionsExpanded = !scoreQuestionsExpanded}>
{scoreQuestionsExpanded ? 'X' : '?'}
</span-->
</div>
</div>
{/if}
<div class="card-image">
<figure class="image">
<img src={`/images/${cardData.attribution_id}.jpg`} />
@ -40,33 +51,40 @@
</div>
<div class="card-content">
<div class="content">
<p>{utcFormat('%B %d, %Y')(new Date(cardData.attribution_date))}</p>
<p><a href={cardData.attribution_url} target="_blank">{cardData.source}</a></p>
{#if expanded}
<p>{utcFormat('%B %d, %Y')(new Date(cardData.attribution_date))}</p>
<p><a href={cardData.attribution_url} target="_blank">{cardData.source}</a></p>
{/if}
<p>{cardData.short_description}</p>
<p>
<button class="button is-info is-small" on:click={sourceFilter.selectOne(cardData.source)}
>{cardData.source}</button
>
{#each cardData.actor_nation as nation}
<button class="button is-danger is-small" on:click={actorNationFilter.selectOne(nation)}
>{nation}</button
{#if expanded}
<p>
<button class="button is-info is-small" on:click={sourceFilter.selectOne(cardData.source)}
>{cardData.source}</button
>
{/each}
{#each cardData.platform as platform}
<button class="button is-link is-small" on:click={platformFilter.selectOne(platform)}
>{platform}</button
>
{/each}
</p>
{#each cardData.actor_nation as nation}
<button class="button is-danger is-small" on:click={actorNationFilter.selectOne(nation)}
>{nation}</button
>
{/each}
{#each cardData.platform as platform}
<button class="button is-link is-small" on:click={platformFilter.selectOne(platform)}
>{platform}</button
>
{/each}
</p>
{/if}
</div>
</div>
{#if !expanded}
<footer class="card-footer">
<button on:click={openCase(cardData.attribution_id)} class="card-footer-item">Open case</button>
</footer>
{/if}
</div>
<style>
div.card {
max-width: 780px;
/*max-height: 600px;
overflow-y: scroll;*/
}
.score-bars {
display: flex;

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

@ -3,12 +3,13 @@
import { onMount } from 'svelte';
import { csv } from 'd3-fetch';
import { max, extent } from 'd3-array';
import Header from '$lib/components/Header.svelte';
import Header from '$lib/components/Header.svelte';
import CaseCard from '$lib/components/CaseCard.svelte';
import CaseTable from '$lib/components/CaseTable.svelte';
import Timeline from '$lib/components/Timeline.svelte';
import TimelineMobile from '$lib/components/TimelineMobile.svelte';
import Controls from '$lib/components/Controls.svelte';
import CardModal from '$lib/components/CardModal.svelte';
import AnimatedFilterIcon from '$lib/components/AnimatedFilterIcon.svelte';
import { splitString, haveOverlap, withinRange, includesTextSearch } from '$lib/utils/misc';
//import { setScales } from '$lib/utils/scales';
@ -21,7 +22,7 @@
sourceFilter,
sourceCategoryFilter,
methodFilter,
campaignFilter,
campaignFilter,
attributionScoreFilter,
attributionScoreDef,
textSearchFilter,
@ -29,8 +30,8 @@
fullTimeRange
} from '../stores/filters';
$: console.log($timeRangeFilter)
//$: console.log($fullTimeRange)
//$: console.log($timeRangeFilter)
//$: console.log($fullTimeRange)
$: innerWidth = 0;
$: isMobile = innerWidth < 520;
@ -39,21 +40,21 @@
let cases = [];
let events = [];
let metrics = [];
let maxAttribution = 0;
let maxAttribution = 0;
onMount(async function () {
const response = await csv(
`https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/fiat_2024_attribution_data.csv`
`https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/fiat_2024_attribution_data.csv`
);
cases = response;
cases = cases.filter((d) => d.attribution_id != '');
cases.forEach((d) => {
d.platform = splitString(d.platforms);
d.medium = splitString(d.medium)
d.medium = splitString(d.medium);
d.actor_nation = splitString(d.actor_nation);
d.source = splitString(d.source);
d.methods = splitString(d.methods);
d.campaign = splitString(d.campaign)
d.campaign = splitString(d.campaign);
d.attribution_total_score = +d.attribution_score;
d.attribution_date = new Date(d.attribution_date);
d.search = [
@ -79,12 +80,12 @@
sourceFilter.init(cases, 'source');
sourceCategoryFilter.init(cases, 'source_category');
methodFilter.init(cases, 'methods');
campaignFilter.init(cases, 'campaign')
campaignFilter.init(cases, 'campaign');
$attributionScoreFilter = attributionScoreDef;
$timeRangeFilter = extent(cases.map((d) => new Date(d.attribution_date)));
//$timeRangeFilter = [new Date('2024-01-01'), max(cases.map((d) => new Date(d.attribution_date)))];
//$timeRangeFilter = [new Date('2024-01-01'), max(cases.map((d) => new Date(d.attribution_date)))];
$fullTimeRange = extent(cases.map((d) => new Date(d.attribution_date)));
//$fullTimeRange = [new Date('2022-01-01'), max(cases.map((d) => new Date(d.attribution_date)))];
//$fullTimeRange = [new Date('2022-01-01'), max(cases.map((d) => new Date(d.attribution_date)))];
const eventsResponse = await csv(
`https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/Key_Events_List.csv`
@ -153,7 +154,7 @@
haveOverlap($sourceFilter, d.source) &&
haveOverlap($sourceCategoryFilter, d.source_category) &&
haveOverlap($methodFilter, d.methods) &&
haveOverlap($campaignFilter, d.campaign) &&
haveOverlap($campaignFilter, d.campaign) &&
withinRange($attributionScoreFilter, d.attribution_total_score) &&
withinRange($timeRangeFilter, d.attribution_date) &&
includesTextSearch($textSearchFilter, d.search)
@ -188,18 +189,21 @@
];
let selectedSorting = { id: 'attribution_date', label: 'Attribution Date', type: 'date' };
let modalOpen = false;
let activeCaseData;
</script>
<svelte:window bind:innerWidth />
<svelte:head>
<title>{copy.meta.title}</title>
<meta property="og:site_name" content={copy.meta.og_site_name} />
<meta property="og:description" content={copy.meta.og_description} />
<meta property="og:url" content={copy.meta.og_url} />
<meta property="og:image" content={copy.meta.og_image} />
<meta property="og:type" content="website">
<meta property="og:locale" content="en_US">
<title>{copy.meta.title}</title>
<meta property="og:site_name" content={copy.meta.og_site_name} />
<meta property="og:description" content={copy.meta.og_description} />
<meta property="og:url" content={copy.meta.og_url} />
<meta property="og:image" content={copy.meta.og_image} />
<meta property="og:type" content="website" />
<meta property="og:locale" content="en_US" />
</svelte:head>
{#if isMobile}
@ -211,7 +215,7 @@
{/if}
<section class="section">
<Header />
<Header />
</section>
<section class="section">
@ -222,17 +226,18 @@
{#if block.type == 'text'}
<p class="intro">{block.text}</p>
{/if}
{/each}
</div>
<div class="container">
{#each copy.intro as block}
{#if block.type == 'concealed-text'}
{/each}
</div>
<div class="container">
{#each copy.intro as block}
{#if block.type == 'concealed-text'}
<p>{block.title}</p>
{/if}
{/each}
</div>
</div>
</section>
{#if !modalOpen}
<section
class={isMobile && sidebarOpen
? 'section sidebar open controls'
@ -240,8 +245,10 @@
? 'section sidebar closed controls'
: 'section sticky controls'}
>
<Controls {cases}></Controls>
</section>
{/if}
<section class="section">
<div>
@ -276,7 +283,7 @@
</div>
</div>
<div class="cases-control">
<label for="sort-select">Sort cases by </label>
<label for="sort-select">Sort cases by </label>
<div class="select is-small">
<select bind:value={selectedSorting} id="sort-select">
{#each sortOptions as sortOpt}
@ -293,12 +300,15 @@
{#if displayDataAs == 'Cards'}
<section class="section">
<div class="container">
<a href="https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/fiat_2024_attribution_data.csv">Download the data</a>
<a
href="https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/fiat_2024_attribution_data.csv"
>Download the data</a
>
<div class="grid is-col-min-16">
{#each sortedCases as attrCase}
{#if attrCase.show}
<div class="cell">
<CaseCard cardData={attrCase}></CaseCard>
<CaseCard cardData={attrCase} expanded={false} bind:modalOpen bind:activeCaseData></CaseCard>
</div>
{/if}
{/each}
@ -310,7 +320,10 @@
{#if displayDataAs == 'Table' && sortedCases.length > 0}
<section class="section">
<div class="container">
<a href="https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/fiat_2024_attribution_data.csv">Download the data</a>
<a
href="https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/fiat_2024_attribution_data.csv"
>Download the data</a
>
<CaseTable cases={sortedCases}></CaseTable>
</div>
</section>
@ -322,25 +335,19 @@
{#if block.type == 'text'}
<p>{block.text}</p>
{/if}
{#if block.type == 'concealed-text'}
{#if block.type == 'concealed-text'}
<p>{block.title}</p>
{/if}
{/each}
</div>
</section>
<CardModal bind:modalOpen {activeCaseData}></CardModal>
<style>
section {
font-family: var(--font-02);
}
.title {
font-family: var(--font-01);
color: var(--usa-blue)
}
.subtitle {
font-family: var(--font-02);
color: var(--usa-blue)
}
section {
font-family: var(--font-02);
}
.intro {
max-width: 800px;
margin: auto;
@ -378,6 +385,6 @@
}
.cases-control {
display: inline-block;
margin-left: 3rem;
margin-left: 3rem;
}
</style>