Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
252 changes: 252 additions & 0 deletions docs/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -1376,6 +1376,20 @@ button {
width: 100%;
}

.range-slider-container {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
}

.range-slider-wrapper {
flex: 1;
display: flex;
flex-direction: column;
gap: 2px;
}

.range-values {
display: flex;
justify-content: space-between;
Expand All @@ -1387,6 +1401,50 @@ button {
color: var(--bluegrey);
}

.timeline-play-button {
flex-shrink: 0;
width: 32px;
height: 32px;
padding: 0;
background: rgba(151, 174, 196, 0.15);
border: 1px solid rgba(151, 174, 196, 0.4);
border-radius: 6px;
color: var(--bluegrey);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}

.timeline-play-button:hover {
background: rgba(151, 174, 196, 0.25);
border-color: rgba(151, 174, 196, 0.6);
color: #fff;
}

.timeline-play-button:active {
transform: scale(0.95);
}

.timeline-play-button svg {
width: 18px;
height: 18px;
display: none;
}

.timeline-play-button .play-icon {
display: block;
}

.timeline-play-button.playing .play-icon {
display: none;
}

.timeline-play-button.playing .pause-icon {
display: block;
}

.range-slider {
position: relative;
width: 100%;
Expand Down Expand Up @@ -1588,6 +1646,200 @@ button {
text-transform: none;
}

.bubble-chart-button {
width: 100%;
margin-top: 16px;
padding: 12px 16px;
background: rgba(151, 174, 196, 0.15);
border: 1px solid rgba(151, 174, 196, 0.4);
border-radius: 10px;
color: var(--bluegrey);
font-family: var(--font-display);
font-size: 14px;
letter-spacing: 0.5px;
text-transform: uppercase;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: all 0.25s ease;
}

.bubble-chart-button:hover {
background: rgba(151, 174, 196, 0.25);
border-color: rgba(151, 174, 196, 0.6);
color: #fff;
transform: translateY(-1px);
}

.bubble-chart-button:active {
transform: translateY(0);
}

.bubble-chart-icon {
font-size: 10px;
line-height: 1;
letter-spacing: -1px;
display: inline-block;
}

.globe-icon {
font-size: 16px;
line-height: 1;
display: none;
}

.bubble-chart-button.active .bubble-chart-icon {
display: none;
}

.bubble-chart-button.active .globe-icon {
display: inline-block;
}

.bubble-chart-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: transparent;
opacity: 0;
pointer-events: none;
transition: opacity 0.8s ease;
z-index: 10;
}

#globeContainer.hide-for-bubbles {
opacity: 0;
pointer-events: none;
transition: opacity 0.5s ease;
}

.bubble-chart-container.visible {
opacity: 1;
pointer-events: auto;
}

.bubble-chart-container.hidden {
display: none;
}

#bubbleChartSvg {
width: 100%;
height: 100%;
}

.close-bubble-chart {
position: absolute;
top: 24px;
right: 24px;
width: 40px;
height: 40px;
padding: 0;
background: rgba(151, 174, 196, 0.15);
border: 1px solid rgba(151, 174, 196, 0.4);
border-radius: 8px;
color: var(--bluegrey);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
z-index: 11;
}

.close-bubble-chart:hover {
background: rgba(151, 174, 196, 0.25);
border-color: rgba(151, 174, 196, 0.6);
color: #fff;
}

.close-bubble-chart svg {
width: 20px;
height: 20px;
}

.bubble-zoom-controls {
position: absolute;
bottom: 24px;
right: 24px;
display: flex;
flex-direction: column;
gap: 8px;
z-index: 11;
}

.zoom-control-btn {
width: 40px;
height: 40px;
padding: 0;
background: rgba(151, 174, 196, 0.15);
border: 1px solid rgba(151, 174, 196, 0.4);
border-radius: 8px;
color: var(--bluegrey);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}

.zoom-control-btn:hover {
background: rgba(151, 174, 196, 0.25);
border-color: rgba(151, 174, 196, 0.6);
color: #fff;
}

.zoom-control-btn svg {
width: 20px;
height: 20px;
}

.bubble-node {
cursor: pointer;
transition: opacity 0.3s ease;
}

.bubble-node circle {
stroke: rgba(255, 255, 255, 0.3);
stroke-width: 2px;
}

.bubble-node:hover circle {
stroke: rgba(255, 255, 255, 0.6);
stroke-width: 3px;
}

.bubble-label {
font-family: var(--font-garamond);
font-size: 12px;
fill: #000000;
text-anchor: middle;
pointer-events: none;
user-select: none;
text-transform: capitalize;
letter-spacing: 0.3px;
font-weight: 1000;
filter: drop-shadow(0 0 2px rgba(255, 255, 255, 0.8))
drop-shadow(0 0 2px rgba(255, 255, 255, 0.5))
drop-shadow(0 1px 1px rgba(255, 255, 255, 0.6));
}

.bubble-label tspan {
fill: #000000;
}

.bubble-count {
font-family: var(--font-inter);
font-size: 11px;
fill: rgba(255, 255, 255, 0.8);
text-anchor: middle;
pointer-events: none;
user-select: none;
}

.earth-story-card {
position: absolute;
right: 27px;
Expand Down
66 changes: 57 additions & 9 deletions docs/globe.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,28 @@
<section class="filter-section" aria-labelledby="timePeriodTitle">
<h2 class="filter-title" id="timePeriodTitle">Time period</h2>
<div class="year-range-control">
<div class="range-values" aria-hidden="true">
<span id="yearMinVal">1975</span>
<span id="yearMaxVal">2025</span>
</div>
<div class="range-slider" aria-label="Select event year range">
<div class="range-track">
<div class="range-fill" id="yearRangeFill"></div>
<div class="range-slider-container">
<button type="button" id="timelinePlayButton" class="timeline-play-button" aria-label="Play timeline animation">
<svg class="play-icon" viewBox="0 0 24 24" fill="currentColor">
<path d="M8 5v14l11-7z"/>
</svg>
<svg class="pause-icon" viewBox="0 0 24 24" fill="currentColor">
<path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z"/>
</svg>
</button>
<div class="range-slider-wrapper">
<div class="range-values" aria-hidden="true">
<span id="yearMinVal">1975</span>
<span id="yearMaxVal">2025</span>
</div>
<div class="range-slider" aria-label="Select event year range">
<div class="range-track">
<div class="range-fill" id="yearRangeFill"></div>
</div>
<input class="range-input range-input-min" type="range" id="yearStartSlider" min="1975" max="2025" value="1975" aria-label="Start year" />
<input class="range-input range-input-max" type="range" id="yearEndSlider" min="1975" max="2025" value="1975" aria-label="End year" />
</div>
</div>
<input class="range-input range-input-min" type="range" id="yearStartSlider" min="1975" max="2025" value="1975" aria-label="Start year" />
<input class="range-input range-input-max" type="range" id="yearEndSlider" min="1975" max="2025" value="2025" aria-label="End year" />
</div>
</div>
</section>
Expand All @@ -64,8 +76,44 @@ <h2 class="filter-title" id="disasterTypesTitle">Disaster types</h2>
<div class="events-label">Events displayed</div>
<div class="data-status hidden" id="dataStatus"></div>
</div>

<button type="button" id="bubbleChartButton" class="bubble-chart-button">
<span class="bubble-chart-icon">●●●</span>
<span class="globe-icon">🌍</span>
<span class="bubble-chart-text">Open bubble chart</span>
</button>
</aside>

<!-- Bubble Chart Container -->
<div id="bubbleChartContainer" class="bubble-chart-container hidden">
<svg id="bubbleChartSvg"></svg>
<button type="button" id="closeBubbleChart" class="close-bubble-chart" aria-label="Close bubble chart">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M18 6L6 18M6 6l12 12"/>
</svg>
</button>
<div class="bubble-zoom-controls">
<button type="button" id="zoomIn" class="zoom-control-btn" aria-label="Zoom in">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/>
<path d="M21 21l-4.35-4.35M11 8v6M8 11h6"/>
</svg>
</button>
<button type="button" id="zoomOut" class="zoom-control-btn" aria-label="Zoom out">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/>
<path d="M21 21l-4.35-4.35M8 11h6"/>
</svg>
</button>
<button type="button" id="zoomReset" class="zoom-control-btn" aria-label="Reset zoom">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
<path d="M3 3v5h5"/>
</svg>
</button>
</div>
</div>

<aside class="earth-story-card" aria-labelledby="earthStoryCardTitle">
<h2 id="earthStoryCardTitle">Earth Story</h2>
<p>How has the global profile of disasters shifted over time? Explore our Earth Story and uncover 50 years of striking trends.</p>
Expand Down
13 changes: 12 additions & 1 deletion docs/js/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const WORLD_COUNTRY_COUNT = 220;

// Metadata for normalized values from the CSV `type` column.
const DISASTER_TYPE_META = {
all: { label: 'All', color: '#1E3A8A', order: 0 },
drought: { label: 'Drought', color: '#FFC857', order: 10 },
earthquake: { label: 'Earthquake', color: '#C96B32', order: 20 },
extreme_temperature: { label: 'Extreme Temperature', color: '#FF5DB8', order: 30 },
Expand Down Expand Up @@ -390,14 +391,24 @@ function getAvailableDisasterTypes() {
? Array.from(new Set(RAW_DATA.map(d => d.type).filter(Boolean)))
: [];

return types
// Add the "All" option at the beginning
const allOption = {
type: 'all',
label: 'All',
color: '#1E3A8A',
order: 0
};

const typeOptions = types
.map(type => ({
type,
label: getDisasterTypeLabel(type),
color: getDisasterColor(type),
order: DISASTER_TYPE_META[type]?.order ?? 999
}))
.sort((a, b) => a.order - b.order || a.label.localeCompare(b.label));

return [allOption, ...typeOptions];
}

// Helper to format large numbers
Expand Down
Loading
Loading