Skip to content

Commit b45f0d7

Browse files
committed
feat(examples): group together w/ filters
1 parent 32566e1 commit b45f0d7

3 files changed

Lines changed: 274 additions & 61 deletions

File tree

site/docs/examples.mdx

Lines changed: 77 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -8,168 +8,189 @@ We're providing this data in the hopes it will help you tell a story about what
88

99
If you've used our data and are willing to share or link to your usage, please [contact us](/docs/contact), or [open a pull request on Github](https://github.com/TechForPalestine/palestine-datasets).
1010

11-
## Daily Casualties / Summary Dataset
12-
1311
import { ExampleUsageGrid } from "@site/src/components";
1412

1513
<ExampleUsageGrid
1614
examples={[
15+
{
16+
description: "We Will Say Their Names (Exhibit Video)",
17+
image: "/img/exampleuse/we-will-say-their-names.jpg",
18+
link: "https://www.instagram.com/reel/DVUA357Afyd",
19+
tags: ["killed-in-gaza", "memorial"],
20+
},
21+
{
22+
description: "Stones of Gaza",
23+
image: "/img/exampleuse/stones-of-gaza.png",
24+
link: "https://stonesofgaza.com",
25+
tags: ["killed-in-gaza", "memorial", "interactive"],
26+
},
27+
{
28+
description: "Civilian Names Poster + Artist Statement",
29+
image: "/img/exampleuse/civilians-infographic.jpg",
30+
link: "/docs/examples/poster",
31+
tags: ["killed-in-gaza", "visualization", "comprehending-scale"],
32+
},
33+
{
34+
description: "DOTS • Gaza Memorial",
35+
image: "/img/exampleuse/dots.png",
36+
link: "https://omibahuguna.github.io/dots",
37+
tags: ["killed-in-gaza", "memorial", "comprehending-scale"],
38+
},
39+
{
40+
description: "Beyond the Numbers (Names)",
41+
image: "/img/exampleuse/beyondthenumbers-names.png",
42+
link: "https://beyond-the-numbers.vercel.app/martyrs",
43+
tags: ["killed-in-gaza", "interactive", "memorial"],
44+
},
45+
{
46+
description: "Remember Gaza",
47+
image: "/img/exampleuse/nameskilledingaza.png",
48+
link: "https://www.remembergaza.id",
49+
tags: ["killed-in-gaza", "memorial"],
50+
},
51+
{
52+
description: "From the River to the Sea (Never Forget Page)",
53+
image: "/img/exampleuse/river-to-the-sea-names.png",
54+
link: "https://from.the.river.to.the.sea.giving/names",
55+
tags: ["killed-in-gaza", "memorial"],
56+
},
57+
{
58+
description: "Python API example",
59+
image: "/img/exampleuse/python-api.png",
60+
link: "https://github.com/ummahrican/palestine-api",
61+
tags: ["killed-in-gaza", "developer-tool"],
62+
},
1763
{
1864
description: "Palestine Poppy (The Weight of Counting)",
1965
image: "/img/exampleuse/palestine-poppy.jpg",
2066
link: "https://medium.com/design-bootcamp/the-weight-of-counting-b460b375ebb3",
67+
tags: ["daily-casualties", "comprehending-scale"],
2168
},
2269
{
2370
description: "BBC Bias Investigation",
2471
image: "/img/exampleuse/dropsite.png",
2572
link: "https://www.dropsitenews.com/p/bbc-civil-war-gaza-israel-biased-coverage",
73+
tags: ["daily-casualties", "journalism"],
2674
},
2775
{
2876
description: "DataActivists.org: Estimating the Death Toll",
2977
image: "/img/exampleuse/data-activists.png",
3078
link: "https://dataactivists.org/estimating_death_toll_of_war_on_gaza/",
79+
tags: ["daily-casualties", "journalism"],
3180
},
3281
{
3382
description: "Beyond the Numbers",
3483
image: "/img/exampleuse/beyondthenumbers.png",
3584
link: "https://beyond-the-numbers.vercel.app/",
85+
tags: ["daily-casualties", "interactive", "visualization"],
3686
},
3787
{
3888
description: "Rebel Metrics",
3989
image: "/img/exampleuse/rebel-metrics-gaza-bars.png",
4090
link: "/docs/examples/rebel-metrics",
91+
tags: ["daily-casualties", "interactive", "visualization"],
4192
},
4293
{
4394
description: "Genocide Monitor",
4495
image: "/img/exampleuse/genocidemonitor.png",
4596
link: "https://genocidemonitor.com/",
97+
tags: ["daily-casualties", "interactive", "visualization"],
4698
},
4799
{
48100
description: "How many more?",
49101
image: "/img/exampleuse/howmanymore.png",
50102
link: "https://howmanymore.xyz/",
103+
tags: ["daily-casualties", "interactive", "comprehending-scale"],
51104
},
52105
{
53106
description: "palestina.ba",
54107
image: "/img/exampleuse/palestina-ba.png",
55108
link: "https://palestina.ba/",
109+
tags: ["daily-casualties", "interactive"],
56110
},
57111
{
58-
description: "Hind's Banner for Markdown Readmes",
112+
description: "Hind's Banner for Markdown Readme",
59113
image: "/img/exampleuse/hinds-banner.png",
60114
link: "https://github.com/alvii147/hinds-banner",
115+
tags: ["daily-casualties", "developer-tool"],
61116
},
62117
{
63118
description: "The National: UN Ramadan ceasefire news",
64119
image: "/img/exampleuse/nworld-ramadan.png",
65120
link: "https://www.thenationalnews.com/world/us-news/2024/03/25/un-security-council-adopts-gaza-ceasefire-resolution/",
121+
tags: ["daily-casualties", "journalism"],
66122
},
67123
{
68124
description: "Oct7FactCheck analysis",
69125
image: "/img/exampleuse/oct7factcheck.jpeg",
70126
link: "https://x.com/nickburbank710/status/1770226449116430688?s=20",
127+
tags: ["daily-casualties", "journalism"],
71128
},
72129
{
73130
description: "Latest total killed label on avatar maker",
74131
image: "/img/exampleuse/tfp-ppm.png",
75132
link: "https://ppm.techforpalestine.org/",
133+
tags: ["daily-casualties", "interactive"],
76134
},
77135
{
78136
description: "Line chart & count visualization",
79137
image: "/img/exampleuse/linechart.png",
80138
link: "https://genocide.club/hillel-fuld",
139+
tags: ["daily-casualties", "visualization"],
81140
},
82141
{
83142
description: "Number of people killed daily",
84143
image: "/img/exampleuse/szh-counts.png",
85144
link: "https://www.instagram.com/p/C3Tyoy2rp-Z/?img_index=1",
145+
tags: ["daily-casualties", "visualization"],
86146
},
87147
{
88148
description: "Bar chart race",
89149
image: "/img/exampleuse/racingbars.png",
90150
link: "https://livecodes.io/?x=id/5vjcrw2xdj8",
151+
tags: ["daily-casualties", "visualization"],
91152
},
92153
{
93154
description: "Summary count grid",
94155
image: "/img/exampleuse/countgrid.png",
95156
link: "https://gazanumbers.jariyah.app/",
157+
tags: ["daily-casualties", "visualization"],
96158
},
97159
{
98160
description: "The Economist",
99161
image: "/img/exampleuse/economist.png",
100162
link: "https://www.economist.com/",
163+
tags: ["daily-casualties", "journalism"],
101164
},
102165
{
103166
description: "From the River to the Sea",
104167
image: "/img/exampleuse/river-to-the-sea.png",
105168
link: "https://from.the.river.to.the.sea.giving",
106-
},
107-
]}
108-
/>
109-
110-
## Killed in Gaza
111-
112-
<ExampleUsageGrid
113-
examples={[
114-
{
115-
description: "We Will Say Their Names (Exhibit Video)",
116-
image: "/img/exampleuse/we-will-say-their-names.jpg",
117-
link: "https://www.instagram.com/reel/DVUA357Afyd",
118-
},
119-
{
120-
description: "Stones of Gaza",
121-
image: "/img/exampleuse/stones-of-gaza.png",
122-
link: "https://stonesofgaza.com",
169+
tags: ["daily-casualties", "interactive", "comprehending-scale"],
123170
},
124171
{
125-
description: "Civilian Names Poster + Artist Statement",
126-
image: "/img/exampleuse/civilians-infographic.jpg",
127-
link: "/docs/examples/poster",
128-
},
129-
{
130-
description: "Remember Their Names",
131-
image: "/img/exampleuse/af2c-remember.png",
132-
link: "https://af2c.org/Palestine/",
133-
},
134-
{
135-
description: "DOTS • Gaza Memorial",
136-
image: "/img/exampleuse/dots.png",
137-
link: "https://omibahuguna.github.io/dots",
172+
description: "Video scrolling 14k names in 7 hours",
173+
image: "/img/exampleuse/scrollingnames.png",
174+
link: "https://www.youtube.com/watch?v=O7ar_xvvHxE",
175+
tags: ["killed-in-gaza", "memorial", "comprehending-scale"],
138176
},
139177
{
140178
description: "Birds of Gaza children names",
141179
image: "/img/exampleuse/birdsofgaza.png",
142180
link: "https://birdsofgaza.com",
181+
tags: ["killed-in-gaza", "memorial", "interactive"],
143182
},
144183
{
145-
description: "Beyond the Numbers",
146-
image: "/img/exampleuse/beyondthenumbers-names.png",
147-
link: "https://beyond-the-numbers.vercel.app/martyrs",
148-
},
149-
{
150-
description: "Remember Gaza",
151-
image: "/img/exampleuse/nameskilledingaza.png",
152-
link: "https://www.remembergaza.id",
153-
},
154-
{
155-
description: "From the River to the Sea (Never Forget Page)",
156-
image: "/img/exampleuse/river-to-the-sea-names.png",
157-
link: "https://from.the.river.to.the.sea.giving/names",
158-
},
159-
{
160-
description: "Python API example",
161-
image: "/img/exampleuse/python-api.png",
162-
link: "https://github.com/ummahrican/palestine-api",
163-
},
164-
{
165-
description: "Video scrolling 14k names in 7 hours",
166-
image: "/img/exampleuse/scrollingnames.png",
167-
link: "https://www.youtube.com/watch?v=O7ar_xvvHxE",
184+
description: "Remember Their Names",
185+
image: "/img/exampleuse/af2c-remember.png",
186+
link: "https://af2c.org/Palestine/",
187+
tags: ["killed-in-gaza", "memorial", "interactive"],
168188
},
169189
{
170190
description: "UnityFast.org family names",
171191
image: "/img/exampleuse/unityfast.png",
172192
link: "https://unityfast.org",
193+
tags: ["killed-in-gaza", "memorial"],
173194
},
174195
]}
175196
/>

site/src/components/ExampleUsageGrid/index.tsx

Lines changed: 114 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,143 @@
11
import clsx from "clsx";
22
import styles from "./styles.module.css";
3-
import { useMemo } from "react";
3+
import { useMemo, useState } from "react";
4+
5+
type DatasetTag = "daily-casualties" | "killed-in-gaza";
6+
type TypeTag =
7+
| "interactive"
8+
| "journalism"
9+
| "comprehending-scale"
10+
| "visualization"
11+
| "memorial"
12+
| "developer-tool";
13+
14+
export type Example = {
15+
image: string;
16+
description: string;
17+
link: string;
18+
tags: Array<DatasetTag | TypeTag>;
19+
};
420

521
type Props = {
6-
examples: Array<{ image: string; description: string; link: string }>;
22+
examples: Array<Example>;
723
};
824

925
const numberPerRow = 2;
1026

27+
const tagGroups = {
28+
dataset: {
29+
label: "Dataset",
30+
tags: {
31+
"daily-casualties": "Daily Casualties",
32+
"killed-in-gaza": "Killed in Gaza",
33+
} satisfies Record<DatasetTag, string>,
34+
},
35+
type: {
36+
label: "Type",
37+
tags: {
38+
interactive: "Interactive",
39+
journalism: "Journalism",
40+
"comprehending-scale": "Comprehending Scale",
41+
visualization: "Visualization",
42+
memorial: "Memorial",
43+
"developer-tool": "Developer Tool",
44+
} satisfies Record<TypeTag, string>,
45+
},
46+
};
47+
48+
type TagKey = DatasetTag | TypeTag;
49+
1150
const linkTargetFor = (link: string) => {
1251
if (link.endsWith("pdf") || link.startsWith("http")) {
1352
return "_blank";
1453
}
1554
};
1655

1756
export const ExampleUsageGrid = (props: Props) => {
57+
const [activeTags, setActiveTags] = useState<Set<TagKey>>(new Set());
58+
59+
const toggleTag = (tag: TagKey) => {
60+
setActiveTags((prev) => {
61+
const next = new Set(prev);
62+
if (next.has(tag)) {
63+
next.delete(tag);
64+
} else {
65+
next.add(tag);
66+
}
67+
return next;
68+
});
69+
};
70+
71+
const clearFilters = () => {
72+
setActiveTags(new Set());
73+
};
74+
75+
const filteredExamples = useMemo(() => {
76+
if (activeTags.size === 0) return props.examples;
77+
78+
const activeDatasetTags = Object.keys(tagGroups.dataset.tags).filter((t) =>
79+
activeTags.has(t as DatasetTag)
80+
);
81+
const activeTypeTags = Object.keys(tagGroups.type.tags).filter((t) =>
82+
activeTags.has(t as TypeTag)
83+
);
84+
85+
return props.examples.filter((example) => {
86+
const matchesDataset =
87+
activeDatasetTags.length === 0 ||
88+
activeDatasetTags.some((t) => example.tags.includes(t as DatasetTag));
89+
const matchesType =
90+
activeTypeTags.length === 0 ||
91+
activeTypeTags.some((t) => example.tags.includes(t as TypeTag));
92+
return matchesDataset && matchesType;
93+
});
94+
}, [props.examples, activeTags]);
95+
1896
const rows = useMemo(() => {
19-
return props.examples.reduce((acc, example) => {
97+
return filteredExamples.reduce((acc, example) => {
2098
const newRow = Number.isInteger(acc.length / numberPerRow);
2199
if (newRow) {
22100
acc.push([example]);
23101
} else {
24102
acc[acc.length - 1].push(example);
25103
}
26104
return acc;
27-
}, [] as Array<Props["examples"]>);
28-
}, [props.examples]);
105+
}, [] as Array<Example[]>);
106+
}, [filteredExamples]);
29107

30108
return (
31109
<div>
110+
<div className={styles.filterContainer}>
111+
{Object.values(tagGroups).map((group) => (
112+
<div key={group.label} className={styles.filterGroup}>
113+
<span className={styles.filterGroupLabel}>{group.label}:</span>
114+
<div className={styles.filterButtons}>
115+
{Object.entries(group.tags).map(([tag, label]) => (
116+
<button
117+
key={tag}
118+
className={clsx(
119+
styles.filterButton,
120+
activeTags.has(tag as TagKey) && styles.filterButtonActive
121+
)}
122+
onClick={() => toggleTag(tag as TagKey)}
123+
>
124+
{label}
125+
</button>
126+
))}
127+
</div>
128+
</div>
129+
))}
130+
{activeTags.size > 0 && (
131+
<button className={styles.clearButton} onClick={clearFilters}>
132+
Clear filters
133+
</button>
134+
)}
135+
</div>
136+
{filteredExamples.length === 0 && (
137+
<div className={styles.noResults}>
138+
No examples match the selected filters.
139+
</div>
140+
)}
32141
{rows.map((row) => (
33142
<div className="row">
34143
{row.map((col) => (

0 commit comments

Comments
 (0)