feat: add configurable people maps

Add independent map profiles, offline city lookup, bundled world artwork
and collision-free portrait placement with optional person details.

Deploy with updated requirements, migrations 0029-0031 and collectstatic.
This commit is contained in:
mohamad
2026-11-03 04:41:30 +08:00
parent 6fd236f518
commit d4ec85add4
22 changed files with 725 additions and 4 deletions
+5 -1
View File
@@ -1,4 +1,4 @@
{% load static %}
{% load static people_maps %}
<!DOCTYPE html>
<html lang="en">
<head>
@@ -22,6 +22,7 @@
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="{% static 'css/main.css' %}" />
<style>:root { {{ site_branding.theme_style }} }</style>
<link rel="stylesheet" href="{% static 'css/people-map.css' %}" />
{% block extra_css %}{% endblock %}
</head>
<body>
@@ -29,12 +30,15 @@
{% include "partials/_navbar.html" %}
<main id="main-content">
{% people_maps 'top' %}
{% block content %}{% endblock %}
{% people_maps 'bottom' %}
</main>
{% include "partials/_footer.html" %}
<script src="{% static 'js/main.js' %}"></script>
<script src="{% static 'js/people-map.js' %}" defer></script>
{% block extra_js %}{% endblock %}
</body>
</html>
+31
View File
@@ -0,0 +1,31 @@
{% load static %}
{% for placement in placements %}
<section class="section"><div class="container">
<article class="people-map people-map--{{ placement.people_map.style }}">
<header class="people-map-heading"><span class="people-map-eyebrow">CONNECTED ACROSS THE WORLD</span><h2>{{ placement.people_map.title }}</h2><p>{{ placement.people_map.description }}</p></header>
<div class="people-map-scroll" tabindex="0" aria-label="World map. Scroll horizontally on small screens.">
<div class="people-map-canvas" data-people-map>
<img class="people-map-land{% if placement.people_map.background_style != 'classic' %} people-map-land--preset{% endif %}" src="{% if placement.people_map.background_image %}{{ placement.people_map.background_image.url }}{% else %}{% static placement.people_map.background_static_path %}{% endif %}" alt="World map" />
<svg class="people-map-lines" aria-hidden="true"></svg>
{% for member in placement.people_map.memberships.all %}
<{% if placement.people_map.show_people_details %}a{% else %}span{% endif %} class="people-map-pin" {% if placement.people_map.show_people_details %}href="#map-{{ placement.pk }}-person-{{ member.pk }}"{% endif %} data-lat="{{ member.latitude|stringformat:'.6f' }}" data-lon="{{ member.longitude|stringformat:'.6f' }}" aria-label="{{ member.person.name }} — {{ member.city }} {{ member.country }}">
{% if member.person.photo %}<img src="{{ member.person.photo.url }}" alt="" loading="lazy" />{% else %}<span class="people-map-initial">{{ member.person.name|first }}</span>{% endif %}
<span class="people-map-label">{{ member.person.name }}</span>
</{% if placement.people_map.show_people_details %}a{% else %}span{% endif %}>
{% endfor %}
</div>
</div>
{% if placement.people_map.show_people_details %}
<div class="people-map-directory">
{% for member in placement.people_map.memberships.all %}
<article class="people-map-person" id="map-{{ placement.pk }}-person-{{ member.pk }}" tabindex="-1">
{% if member.person.photo %}<img src="{{ member.person.photo.url }}" alt="{{ member.person.name }}" loading="lazy" />{% endif %}
<div><h3>{{ member.person.name }}{% if member.person.qualifications %}, {{ member.person.qualifications }}{% endif %}</h3><p>{{ member.person.role }}</p><p>{{ member.person.institution }}</p><small>{{ member.city }}{% if member.city and member.country %}, {% endif %}{{ member.country }}</small>
{% if member.person.biography %}<p>{{ member.person.biography|linebreaksbr }}</p>{% endif %}
{% if member.person.profile_url %}<a href="{{ member.person.profile_url }}" rel="noopener noreferrer">View profile ↗</a>{% endif %}</div>
</article>
{% endfor %}
</div>
{% endif %}
</article></div></section>
{% endfor %}