Sorting not working yet
Этот коммит содержится в:
родитель
ed89c0f3f6
Коммит
5db9ecdb67
@ -1,6 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { utcFormat } from 'd3-time-format';
|
import { utcFormat } from 'd3-time-format';
|
||||||
export let cases
|
export let cases
|
||||||
|
console.log(cases.map(d => d.Attribution_ID))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
|
|||||||
@ -2,143 +2,171 @@
|
|||||||
import copy from '../data/copy.json';
|
import copy from '../data/copy.json';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { csv } from 'd3-fetch';
|
import { csv } from 'd3-fetch';
|
||||||
import { max, extent } from 'd3-array';
|
import { max, extent } from 'd3-array';
|
||||||
import { base } from '$app/paths';
|
import { base } from '$app/paths';
|
||||||
import CaseCard from '$lib/components/CaseCard.svelte';
|
import CaseCard from '$lib/components/CaseCard.svelte';
|
||||||
import CaseTable from '$lib/components/CaseTable.svelte';
|
import CaseTable from '$lib/components/CaseTable.svelte';
|
||||||
import Timeline from '$lib/components/Timeline.svelte';
|
import Timeline from '$lib/components/Timeline.svelte';
|
||||||
import TimelineMobile from '$lib/components/TimelineMobile.svelte';
|
import TimelineMobile from '$lib/components/TimelineMobile.svelte';
|
||||||
import Controls from '$lib/components/Controls.svelte';
|
import Controls from '$lib/components/Controls.svelte';
|
||||||
import AnimatedFilterIcon from '$lib/components/AnimatedFilterIcon.svelte';
|
import AnimatedFilterIcon from '$lib/components/AnimatedFilterIcon.svelte';
|
||||||
import { splitString, haveOverlap, withinRange, includesTextSearch } from '$lib/utils/misc'
|
import { splitString, haveOverlap, withinRange, includesTextSearch } from '$lib/utils/misc';
|
||||||
//import { setScales } from '$lib/utils/scales';
|
//import { setScales } from '$lib/utils/scales';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { parseUrl } from '$lib/utils/share';
|
import { parseUrl } from '$lib/utils/share';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
platformFilter,
|
platformFilter,
|
||||||
actorNationFilter,
|
actorNationFilter,
|
||||||
sourceFilter,
|
sourceFilter,
|
||||||
sourceCategoryFilter,
|
sourceCategoryFilter,
|
||||||
methodFilter,
|
methodFilter,
|
||||||
attributionScoreFilter,
|
attributionScoreFilter,
|
||||||
attributionScoreDef,
|
attributionScoreDef,
|
||||||
textSearchFilter,
|
textSearchFilter,
|
||||||
timeRangeFilter,
|
timeRangeFilter,
|
||||||
fullTimeRange
|
fullTimeRange
|
||||||
} from '../stores/filters';
|
} from '../stores/filters';
|
||||||
|
|
||||||
$: innerWidth = 0
|
$: innerWidth = 0;
|
||||||
$: isMobile = innerWidth < 520
|
$: isMobile = innerWidth < 520;
|
||||||
$: displayDataAs = isMobile ? "Cards" : "Table"
|
$: displayDataAs = isMobile ? 'Cards' : 'Table';
|
||||||
|
|
||||||
let cases = [];
|
let cases = [];
|
||||||
let events = [];
|
let events = [];
|
||||||
let metrics = [];
|
let metrics = [];
|
||||||
let maxAttribution = 0;
|
let maxAttribution = 0;
|
||||||
|
|
||||||
onMount(async function () {
|
onMount(async function () {
|
||||||
const response = await csv(`https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/Demo_Attribution_Data.csv`);
|
const response = await csv(
|
||||||
|
`https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/Demo_Attribution_Data.csv`
|
||||||
|
);
|
||||||
//const response = await csv(`${base}/Demo_Attribution_Data.csv`);
|
//const response = await csv(`${base}/Demo_Attribution_Data.csv`);
|
||||||
cases = response;
|
cases = response;
|
||||||
cases = cases.filter(d => d.Attribution_ID != '')
|
cases = cases.filter((d) => d.Attribution_ID != '');
|
||||||
cases.forEach(d => {
|
cases.forEach((d) => {
|
||||||
d.platform = splitString(d.Platforms)
|
d.platform = splitString(d.Platforms);
|
||||||
d.actor_nation = splitString(d.Actor_Nation)
|
d.actor_nation = splitString(d.Actor_Nation);
|
||||||
d.source = splitString(d.Source)
|
d.source = splitString(d.Source);
|
||||||
d.methods = splitString(d.Methods)
|
d.methods = splitString(d.Methods);
|
||||||
d.attribution_total_score = +d.attribution_total_score
|
d.attribution_total_score = +d.attribution_total_score;
|
||||||
d.attribution_date = new Date(d.Attribution_Date)
|
d.attribution_date = new Date(d.Attribution_Date);
|
||||||
d.search = [d.Short_Description, d.Short_Title, d.platform, d.methods, d.Source, d.Source_Nation, d.Source_cCtegory].flat().join('__').toLowerCase()
|
d.search = [
|
||||||
|
d.Short_Description,
|
||||||
|
d.Short_Title,
|
||||||
|
d.platform,
|
||||||
|
d.methods,
|
||||||
|
d.Source,
|
||||||
|
d.Source_Nation,
|
||||||
|
d.Source_Category
|
||||||
|
].flat()
|
||||||
|
.join('__')
|
||||||
|
.toLowerCase();
|
||||||
|
|
||||||
d.show = false
|
d.show = false;
|
||||||
})
|
});
|
||||||
|
|
||||||
maxAttribution = max(cases.map(d => d.attribution_total_score))
|
maxAttribution = max(cases.map((d) => d.attribution_total_score));
|
||||||
|
|
||||||
platformFilter.init(cases, 'platform');
|
platformFilter.init(cases, 'platform');
|
||||||
actorNationFilter.init(cases, 'actor_nation');
|
actorNationFilter.init(cases, 'actor_nation');
|
||||||
sourceFilter.init(cases, 'source')
|
sourceFilter.init(cases, 'source');
|
||||||
sourceCategoryFilter.init(cases, 'Source_Category')
|
sourceCategoryFilter.init(cases, 'Source_Category');
|
||||||
methodFilter.init(cases, 'methods')
|
methodFilter.init(cases, 'methods');
|
||||||
$attributionScoreFilter = attributionScoreDef;
|
$attributionScoreFilter = attributionScoreDef;
|
||||||
$timeRangeFilter = extent(cases.map((d) => new Date(d.attribution_date)))
|
$timeRangeFilter = extent(cases.map((d) => new Date(d.attribution_date)));
|
||||||
$fullTimeRange = extent(cases.map((d) => new Date(d.attribution_date)))
|
$fullTimeRange = extent(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`)
|
const eventsResponse = await csv(
|
||||||
events = eventsResponse
|
`https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/Key_Events_List.csv`
|
||||||
events.forEach(d => {
|
);
|
||||||
d.date = new Date(d.Date)
|
events = eventsResponse;
|
||||||
})
|
events.forEach((d) => {
|
||||||
|
d.date = new Date(d.Date);
|
||||||
|
});
|
||||||
|
|
||||||
const metricsResponse = await csv('https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/fiat_country_metrics.csv')
|
const metricsResponse = await csv(
|
||||||
|
'https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/fiat_country_metrics.csv'
|
||||||
|
);
|
||||||
|
|
||||||
metrics = metricsResponse.map(d => {
|
metrics = metricsResponse.map((d) => {
|
||||||
let obj = {}
|
let obj = {};
|
||||||
obj.date = new Date(d.Date),
|
(obj.date = new Date(d.Date)), (obj.posts = +d.Posts);
|
||||||
obj.posts = +d.Posts
|
obj.country = d.Country;
|
||||||
obj.country = d.Country
|
return obj;
|
||||||
return obj
|
});
|
||||||
}
|
metrics.sort((a, b) => {
|
||||||
)
|
return a.date - b.date;
|
||||||
metrics.sort((a, b) =>{
|
});
|
||||||
return a.date - b.date
|
|
||||||
})
|
|
||||||
|
|
||||||
//console.log($page.url.searchParams.get('filters'))
|
|
||||||
if ($page.url.searchParams.has('filters')) {
|
|
||||||
const urlFilters = parseUrl($page.url.searchParams.get('filters'));
|
|
||||||
console.log(urlFilters)
|
|
||||||
|
|
||||||
actorNationFilter.applyBoolArray(urlFilters.actorNations);
|
if ($page.url.searchParams.has('filters')) {
|
||||||
platformFilter.applyBoolArray(urlFilters.platforms);
|
const urlFilters = parseUrl($page.url.searchParams.get('filters'));
|
||||||
methodFilter.applyBoolArray(urlFilters.methods);
|
|
||||||
sourceFilter.applyBoolArray(urlFilters.sources);
|
actorNationFilter.applyBoolArray(urlFilters.actorNations);
|
||||||
sourceCategoryFilter.applyBoolArray(urlFilters.sourceCategories);
|
platformFilter.applyBoolArray(urlFilters.platforms);
|
||||||
$attributionScoreFilter = urlFilters.attributionScores;
|
methodFilter.applyBoolArray(urlFilters.methods);
|
||||||
$textSearchFilter = urlFilters.textSearch;
|
sourceFilter.applyBoolArray(urlFilters.sources);
|
||||||
}
|
sourceCategoryFilter.applyBoolArray(urlFilters.sourceCategories);
|
||||||
|
$attributionScoreFilter = urlFilters.attributionScores;
|
||||||
|
$textSearchFilter = urlFilters.textSearch;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$: if (cases) {
|
|
||||||
cases = cases.map(d => ({
|
|
||||||
...d,
|
|
||||||
show: haveOverlap($actorNationFilter, d.Actor_Nation)
|
|
||||||
&& haveOverlap($platformFilter, d.platform)
|
|
||||||
&& haveOverlap($sourceFilter, d.source)
|
|
||||||
&& haveOverlap($sourceCategoryFilter, d.Source_Category)
|
|
||||||
&& haveOverlap($methodFilter, d.methods)
|
|
||||||
&& withinRange($attributionScoreFilter, d.attribution_total_score)
|
|
||||||
&& withinRange($timeRangeFilter, d.attribution_date)
|
|
||||||
&& includesTextSearch($textSearchFilter, d.search)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
let width = 1200
|
let sortedCases = []
|
||||||
let margin = {
|
$: if (cases) {
|
||||||
top: 30,
|
cases = cases.map((d) => ({
|
||||||
right: 30,
|
...d,
|
||||||
bottom: 30,
|
show:
|
||||||
left: 30
|
haveOverlap($actorNationFilter, d.Actor_Nation) &&
|
||||||
}
|
haveOverlap($platformFilter, d.platform) &&
|
||||||
|
haveOverlap($sourceFilter, d.source) &&
|
||||||
// set the scales
|
haveOverlap($sourceCategoryFilter, d.Source_Category) &&
|
||||||
//$: setScales(cases, width, margin);
|
haveOverlap($methodFilter, d.methods) &&
|
||||||
|
withinRange($attributionScoreFilter, d.attribution_total_score) &&
|
||||||
let sidebarOpen = false;
|
withinRange($timeRangeFilter, d.attribution_date) &&
|
||||||
|
includesTextSearch($textSearchFilter, d.search)
|
||||||
let toggleSidebar = function(){
|
}));
|
||||||
sidebarOpen = !sidebarOpen
|
sortedCases = cases.sort((a, b) => a[selectedSorting.id] - b[selectedSorting.id])
|
||||||
|
sortedCases = sortedCases
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let width = 1200;
|
||||||
|
let margin = {
|
||||||
|
top: 30,
|
||||||
|
right: 30,
|
||||||
|
bottom: 30,
|
||||||
|
left: 30
|
||||||
|
};
|
||||||
|
|
||||||
|
// set the scales
|
||||||
|
//$: setScales(cases, width, margin);
|
||||||
|
|
||||||
|
let sidebarOpen = false;
|
||||||
|
|
||||||
|
let toggleSidebar = function () {
|
||||||
|
sidebarOpen = !sidebarOpen;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortOptions = [
|
||||||
|
{id: 'attribution_date', label: 'Attribution Date'},
|
||||||
|
{id: 'attribution_total_score', label: 'Attribution Score'},
|
||||||
|
{id: 'actor_nation', label: 'Actor Nation'},
|
||||||
|
{id: 'platform', label: 'Platform'},
|
||||||
|
{id: 'source', label: 'Source'},
|
||||||
|
{id: 'Source_Category', label: 'Source Category'},
|
||||||
|
]
|
||||||
|
|
||||||
|
let selectedSorting = {id: 'attribution_date', label: 'Attribution Date'}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window bind:innerWidth />
|
<svelte:window bind:innerWidth />
|
||||||
|
|
||||||
{#if isMobile}
|
{#if isMobile}
|
||||||
<div class="filter-button">
|
<div class="filter-button">
|
||||||
<button on:click={() => toggleSidebar()}><AnimatedFilterIcon {sidebarOpen}></AnimatedFilterIcon></button>
|
<button on:click={() => toggleSidebar()}
|
||||||
</div>
|
><AnimatedFilterIcon {sidebarOpen}></AnimatedFilterIcon></button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<section class="section">
|
<section class="section">
|
||||||
@ -152,90 +180,115 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class={isMobile && sidebarOpen
|
<section
|
||||||
? "section sidebar open controls"
|
class={isMobile && sidebarOpen
|
||||||
: isMobile && !sidebarOpen
|
? 'section sidebar open controls'
|
||||||
? "section sidebar closed controls"
|
: isMobile && !sidebarOpen
|
||||||
: "section sticky controls"}>
|
? 'section sidebar closed controls'
|
||||||
<Controls {cases} ></Controls>
|
: 'section sticky controls'}
|
||||||
|
>
|
||||||
|
<Controls {cases}></Controls>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div>
|
<div>
|
||||||
{#if isMobile}
|
{#if isMobile}
|
||||||
<TimelineMobile {cases}></TimelineMobile>
|
<TimelineMobile {cases}></TimelineMobile>
|
||||||
{:else}
|
{:else}
|
||||||
<Timeline {cases} {events} {metrics}></Timeline>
|
<Timeline {cases} {events} {metrics}></Timeline>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container grid is-col-min-12">
|
||||||
<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-dark is-selected is-small" : "button is-small"} on:click={() => {displayDataAs = "Table"}}>Table</button>
|
<button
|
||||||
<button class={displayDataAs == "Cards" ? "button is-dark is-selected is-small" : "button is-small"} on:click={() => {displayDataAs = "Cards"}}>Cards</button>
|
class={displayDataAs == 'Table'
|
||||||
</div>
|
? 'button is-dark is-selected is-small'
|
||||||
</div>
|
: 'button is-small'}
|
||||||
</div>
|
on:click={() => {
|
||||||
</section>
|
displayDataAs = 'Table';
|
||||||
|
}}>Table</button
|
||||||
{#if displayDataAs == "Cards"}
|
>
|
||||||
<section class="section">
|
<button
|
||||||
<div class="container">
|
class={displayDataAs == 'Cards'
|
||||||
<div class="grid is-col-min-12">
|
? 'button is-dark is-selected is-small'
|
||||||
{#each cases as attrCase}
|
: 'button is-small'}
|
||||||
{#if attrCase.show}
|
on:click={() => {
|
||||||
<div class="cell">
|
displayDataAs = 'Cards';
|
||||||
<CaseCard cardData={attrCase}></CaseCard>
|
}}>Cards</button
|
||||||
</div>
|
>
|
||||||
{/if}
|
</div>
|
||||||
{/each}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="select is-small">
|
||||||
|
<select bind:value={selectedSorting}>
|
||||||
|
{#each sortOptions as sortOpt}
|
||||||
|
<option value={sortOpt}>
|
||||||
|
{sortOpt.label}
|
||||||
|
</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{#if displayDataAs == 'Cards'}
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="grid is-col-min-12">
|
||||||
|
{#each cases as attrCase}
|
||||||
|
{#if attrCase.show}
|
||||||
|
<div class="cell">
|
||||||
|
<CaseCard cardData={attrCase}></CaseCard>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if displayDataAs == "Table"}
|
{#if displayDataAs == 'Table' && sortedCases.length > 0}
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<CaseTable {cases}></CaseTable>
|
<CaseTable cases={sortedCases}></CaseTable>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.intro {
|
.intro {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
.controls {
|
.controls {
|
||||||
background-color: #ffffffdd;
|
background-color: #ffffffdd;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 500;
|
z-index: 500;
|
||||||
}
|
}
|
||||||
.sticky {
|
.sticky {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
}
|
}
|
||||||
.sidebar {
|
.sidebar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
transition: left 0.5s;
|
transition: left 0.5s;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
.closed {
|
.closed {
|
||||||
left: -100%;
|
left: -100%;
|
||||||
}
|
}
|
||||||
.open {
|
.open {
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
.filter-button {
|
.filter-button {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
z-index: 750;
|
z-index: 750;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Загрузка…
x
Ссылка в новой задаче
Block a user