narrative web version
1004
public/assets/css/web.css
Normal file
|
After Width: | Height: | Size: 191 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
Uuid: 0ofsryvoc2wwnnqr
|
||||
|
||||
----
|
||||
|
||||
Template: blocks/image
|
||||
|
After Width: | Height: | Size: 1.8 MiB |
|
|
@ -0,0 +1,5 @@
|
|||
Uuid: kjb9tgal4d7m95mt
|
||||
|
||||
----
|
||||
|
||||
Template: blocks/image
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
Title: Maison
|
||||
|
||||
----
|
||||
|
||||
Cover: - file://0ofsryvoc2wwnnqr
|
||||
|
||||
----
|
||||
|
||||
Text: [{"content":{"location":"kirby","image":["file://0ofsryvoc2wwnnqr"],"src":"","alt":"","caption":"Mon appartement","link":"","width":"","position":""},"id":"bf74d3e0-3fff-4ef6-a1bc-ba7605e67a74","isHidden":false,"type":"image"},{"content":{"text":"<p>Il fait chaud, la luminosité est chaleureuse, et l'odeur apaisante. Un chat est assis sur la table basse du salon, un autre sur le meuble télé, un dort sur le canapé, et un autre semble se diriger vers la chambre. Des toiles et des posters recouvrent les murs, et de la musique jaillit des quatre coins de la pièce. Je me sens bien ici, car je fais en sorte que ce soit le cas. Mais pourtant, depuis quelques jours, j'ai du mal à rester dans mon salon, dans ma cuisine ou même dans ma chambre. Même mon balcon devient étouffant. Il y a soit trop de silence chez moi, soit trop de bruit. Mon salon semble soit trop rangé minutieusement, soit trop en désordre. Je n'ai plus envie de rester ici, et je n'y arrive tout simplement plus. Mais je n'ai pas non plus envie d'en sortir. Alors je mets de la musique pour casser ce lourd silence, mais en même temps je change de musique toutes les secondes, car aucune ne me convient. Cette dualité mentale m'épuise et m'empêche de réfléchir, parfois même de respirer. Alors en ce moment je doute de tout. Tout me semble terne, je n'ai plus d'inspiration et je n'arrive plus a peindre. Je suis bloquée. Et plus rien ne me donne envie. J'ai mal partout, mon dos est bloqué, je suis constamment recroquevillée sur moi-même, et j'ai parfois l'impression que mes jambes ne sont plus capables de me porter.</p><p>Un de mes chats se réveille doucement de sa sieste : Fely. C'est mon chat tout gris. Le plus gourmand des quatre. Alors forcement, s'il se réveille de sa sieste c'est uniquement pour aller manger. Mais voilà que je l'entends miauler malgré la musique qui se fait retentir chez moi. Ce qui vient balayer mes pensées. Il n'y a plus de croquettes dans les gamelles, et, pire encore, plus de croquettes dans le paquet. C'est une excuse parfaite pour enfin sortir de chez moi, et pour ne penser qu'à une chose : <a href=\"https://geoproject.fr/ba/markers/mes-premiers-pas/\">faire les courses.</a></p>"},"id":"42deaa16-dcd1-4327-a4b3-875a2c61d47b","isHidden":false,"type":"text"},{"content":{"location":"kirby","image":["file://kjb9tgal4d7m95mt"],"src":"","alt":"","caption":"Fely sur mon canapé","link":"","width":"","position":""},"id":"ab6104a9-6c93-4d72-9ff9-3eb75476cf94","isHidden":false,"type":"image"}]
|
||||
|
||||
----
|
||||
|
||||
Latitude: 48.850647669811
|
||||
|
||||
----
|
||||
|
||||
Longitude: 2.3252579641452
|
||||
|
||||
----
|
||||
|
||||
Markericon:
|
||||
|
||||
----
|
||||
|
||||
Markericonsize: 40
|
||||
|
||||
----
|
||||
|
||||
Uuid: mkl0rcgmk2va5v2p
|
||||
|
After Width: | Height: | Size: 128 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
Uuid: ugt8nkibhgeaxws8
|
||||
|
||||
----
|
||||
|
||||
Template: blocks/image
|
||||
|
After Width: | Height: | Size: 510 KiB |
|
|
@ -0,0 +1 @@
|
|||
Uuid: nrzg8aiibnsxecfl
|
||||
|
After Width: | Height: | Size: 214 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
Uuid: v3cgqqjcu5xi0bef
|
||||
|
||||
----
|
||||
|
||||
Template: blocks/image
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
Title: Mes premiers pas
|
||||
|
||||
----
|
||||
|
||||
Cover: - file://nrzg8aiibnsxecfl
|
||||
|
||||
----
|
||||
|
||||
Text: [{"content":{"location":"kirby","image":["file://ugt8nkibhgeaxws8"],"src":"","alt":"","caption":"Vue de la rue","link":"","width":"","position":""},"id":"480e0e09-5554-420e-b058-ea82e3985a6c","isHidden":false,"type":"image"},{"content":{"text":"<p>J'ai enfilé un pull un peu au hasard, mes baskets aux pieds, et mes écouteurs diffusent mon son du moment : <em>Rue de Sèvres</em> de Dinos. Me voilà en route vers le supermarché le plus proche. La rue semble silencieuse à côté de ce qu'il se passe dans ma tête. Même en me trouvant à l'extérieur, je me sens bloquée et j'ai de nouveau l'impression d'étouffer. Je me sens coincée dans ma vie. Plus rien ne me fait vibrer. Même cette rue que j'apprécie tant, à fini par me blaser. Je crois que j'attends quelque chose, un signe, un événement pour avoir un renouveau d'inspiration. J'essaie de me concentrer uniquement sur le chemin et sur ma liste de courses, mais ces pensées ne cessent de revenir. Pourquoi est-ce que je n'arrive plus à peindre ? Pourquoi est-ce que cette activité qui me faisait du bien autrefois devient un réel calvaire ? Pourquoi est-ce que même mon propre appartement ne me convient plus ? Pourquoi ? Pourquoi ? Pourquoi ?!</p><p><code>Une pause. Il me faut une pause.</code></p>"},"id":"33bbfdfd-f92a-475a-b175-440fb50fb1b4","isHidden":false,"type":"text"},{"content":{"location":"kirby","image":["file://v3cgqqjcu5xi0bef"],"src":"","alt":"","caption":"Mes pas","link":"","width":"","position":""},"id":"4d8d7e98-4b6a-4166-8077-3c728a7d8ee4","isHidden":false,"type":"image"},{"content":{"text":"<p>Je vais aller m'asseoir sur ce banc, dans <a href=\"https://geoproject.fr/ba/markers/le-banc-du-parc/\">ce parc</a> dans lequel je n'ai pas mis les pieds depuis des années.</p>"},"id":"8d7cf47a-20c4-4a90-9b91-fc470b67fc09","isHidden":false,"type":"text"}]
|
||||
|
||||
----
|
||||
|
||||
Latitude: 48.850268819336
|
||||
|
||||
----
|
||||
|
||||
Longitude: 2.3242447746227
|
||||
|
||||
----
|
||||
|
||||
Markericon:
|
||||
|
||||
----
|
||||
|
||||
Markericonsize: 40
|
||||
|
||||
----
|
||||
|
||||
Uuid: ttylgpb9rz43bncb
|
||||
|
After Width: | Height: | Size: 644 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
Uuid: ublkpzmpvyfnraw8
|
||||
|
||||
----
|
||||
|
||||
Template: blocks/image
|
||||
|
After Width: | Height: | Size: 3.4 MiB |
|
|
@ -0,0 +1,5 @@
|
|||
Uuid: yk4i3gznft3k8mcs
|
||||
|
||||
----
|
||||
|
||||
Template: blocks/image
|
||||
|
After Width: | Height: | Size: 3.6 MiB |
|
|
@ -0,0 +1 @@
|
|||
Uuid: ih9crswtmcuvhnlr
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
Title: Le banc du parc
|
||||
|
||||
----
|
||||
|
||||
Cover: - file://ih9crswtmcuvhnlr
|
||||
|
||||
----
|
||||
|
||||
Text: [{"content":{"location":"kirby","image":["file://ublkpzmpvyfnraw8"],"src":"","alt":"","caption":"Assise sur le banc dans la charmille","link":"","width":"","position":""},"id":"2747a850-f19f-44fe-a017-bf3f9ba4a9e9","isHidden":false,"type":"image"},{"content":{"text":"<p>Je suis assise, et j'ai retiré mes écouteurs. Je suis sur ce banc, dans cette charmille. Je ne savais pas qu'il y avait autant d'oiseaux dans ce parc, et qu'on pouvait si bien les entendre. Ce son apaise un peu mes pensées. Et ce lieu est tellement réconfortant. Je me sens enveloppée dans ces feuilles, j'ai l'étrange impression qu'elles prennent soin de moi. Je me sens portée par ces dernières. Comme si elles recouvraient tout mon corps pour me soigner. Tout doucement, l'idée que cela puisse se faire réellement, commence a se former. Je les imagine une à une se coller contre moi et me créer une deuxième peau. Certaines pourraient être jaunâtres, d'autres avec des reflets rouges, certaines même pourraient avoir la même couleur que ma peau. Et alors toutes les feuilles de la charmille se décolleraient pour ne former qu'un. Rien qu’en y pensant, je me sens rassurée et protégée.</p>"},"id":"0dc64c8d-b490-4ae9-9db8-ee2bd6e5ec73","isHidden":false,"type":"text"},{"content":{"location":"kirby","image":["file://yk4i3gznft3k8mcs"],"src":"","alt":"","caption":"Dans mon imagination","link":"","width":"","position":""},"id":"41801548-58df-4b9f-9e5f-fccfc300d714","isHidden":false,"type":"image"},{"content":{"text":"<p>Mais c'est le bruit agaçant d'un gyrophare de police qui me ramène à la raison. En une fraction de seconde, cet état de bien-être a quitté mon corps. Je me sentais pourtant si bien... C'était <em>ce</em> sentiment que je ne retrouvais plus. J'étais enfin apaisée.</p><p>Il faut que je me reconcentre sur mon objectif premier : <a href=\"https://geoproject.fr/ba/markers/le-declic/\">faire les courses.</a></p>"},"id":"eaf743f3-75da-4aab-84ac-3a7d89c88d59","isHidden":false,"type":"text"}]
|
||||
|
||||
----
|
||||
|
||||
Latitude: 48.85065574496
|
||||
|
||||
----
|
||||
|
||||
Longitude: 2.3214011237332
|
||||
|
||||
----
|
||||
|
||||
Markericon:
|
||||
|
||||
----
|
||||
|
||||
Markericonsize: 40
|
||||
|
||||
----
|
||||
|
||||
Uuid: xne8cgpoal5t9fkn
|
||||
|
After Width: | Height: | Size: 1.7 MiB |
|
|
@ -0,0 +1 @@
|
|||
Uuid: tt7c6mvdavwxrx6r
|
||||
|
|
@ -2,10 +2,6 @@ Title: Le trajet des courses
|
|||
|
||||
----
|
||||
|
||||
Author:
|
||||
|
||||
----
|
||||
|
||||
Tags: chemin, paris, Rue de Sèvres
|
||||
|
||||
----
|
||||
|
|
@ -14,4 +10,19 @@ Text: <p>Dans le 7ème arrondissement, à l'arrêt de métro Saint-Placide, se t
|
|||
|
||||
----
|
||||
|
||||
Mapdata:
|
||||
|
||||
background:
|
||||
type: osm
|
||||
center:
|
||||
lat: 48.850072183852575
|
||||
lon: 2.32508823039484
|
||||
zoom: 16.154464167854993
|
||||
|
||||
----
|
||||
|
||||
Author:
|
||||
|
||||
----
|
||||
|
||||
Uuid: qdofat9jfhj50hqx
|
||||
|
|
@ -25,8 +25,8 @@ Customcss:
|
|||
body {
|
||||
font-family: "DM Sans", sans-serif;
|
||||
text-align: left;
|
||||
color: rgb(190, 9, 9);
|
||||
background: blue;
|
||||
color: black;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
p {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,11 @@ tabs:
|
|||
fields:
|
||||
type: fields
|
||||
fields:
|
||||
content:
|
||||
cover:
|
||||
label: Image de couverture
|
||||
type: files
|
||||
multiple: false
|
||||
text:
|
||||
label: Contenu
|
||||
type: blocks
|
||||
fieldsets:
|
||||
|
|
|
|||
46
public/site/config/config.php
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
use Kirby\Http\Response;
|
||||
|
||||
return [
|
||||
'debug' => true,
|
||||
'routes' => [
|
||||
[
|
||||
'pattern' => '(:all)/web',
|
||||
'action' => function ($path) {
|
||||
|
||||
$realPage = page($path);
|
||||
|
||||
if (!$realPage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return new Page([
|
||||
'slug' => $realPage->slug() . '-web',
|
||||
'template' => 'narrative-web',
|
||||
// 'content' => $realPage->content()->toArray(),
|
||||
'content' => array_merge(
|
||||
$realPage->content()->toArray(),
|
||||
['originalUri' => $realPage->uri()]
|
||||
),
|
||||
]);
|
||||
}
|
||||
]
|
||||
]
|
||||
// [
|
||||
// 'pattern' => '(:all)/web',
|
||||
// 'method' => 'GET',
|
||||
// 'before' => true,
|
||||
// 'action' => function (string $path) {
|
||||
|
||||
// $page = kirby()->page($path);
|
||||
|
||||
// if (!$page) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// return $page->render([], 'narrative-web');
|
||||
// }
|
||||
// ]
|
||||
// ]
|
||||
];
|
||||
485
public/site/templates/narrative-web.php
Normal file
|
|
@ -0,0 +1,485 @@
|
|||
<?php
|
||||
/**
|
||||
* Template: narrative-web.php
|
||||
* Affichage web storytelling d'une narrative et de ses sous-pages
|
||||
* Structure : narrative > geoformat > chapitre / map > marker
|
||||
*/
|
||||
|
||||
// Collect all subpages for lateral navigation
|
||||
$original = page($page->originalUri());
|
||||
$subpages = $original->children()->listed();
|
||||
$navItems = [];
|
||||
|
||||
foreach ($subpages as $subpage) {
|
||||
$item = [
|
||||
'id' => $subpage->uid(),
|
||||
'title' => $subpage->title()->value(),
|
||||
'template' => $subpage->intendedTemplate()->name(),
|
||||
'children' => [],
|
||||
];
|
||||
// Pour les cartes : inclure les marqueurs comme sous-items de navigation
|
||||
if ($subpage->intendedTemplate()->name() === 'map') {
|
||||
foreach ($subpage->children()->listed()->filterBy('intendedTemplate', 'marker') as $mk) {
|
||||
$item['children'][] = [
|
||||
'id' => $mk->uid(),
|
||||
'title' => $mk->title()->value(),
|
||||
];
|
||||
}
|
||||
}
|
||||
$navItems[] = $item;
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= e($original->isHomePage() != true, $original->title() . ' – ') . $site->title() ?></title>
|
||||
|
||||
<!-- Styles globaux existants -->
|
||||
<link rel="stylesheet" href="<?= url('assets/css/style.css') ?>">
|
||||
<!-- Styles narrative web -->
|
||||
<link rel="stylesheet" href="<?= url('assets/css/web.css') ?>">
|
||||
|
||||
<!-- MapLibre GL -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css">
|
||||
<script src="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js"></script>
|
||||
|
||||
<!-- Custom CSS éventuel depuis le panel -->
|
||||
<?php if ($original->customCss()->isNotEmpty()): ?>
|
||||
<style><?= $original->customCss() ?></style>
|
||||
<?php endif ?>
|
||||
|
||||
<!-- À SUPPRIMER EN PRODUCTION -->
|
||||
<meta name="robots" content="noindex, nofollow, noarchive">
|
||||
|
||||
<?php if ($kirby->user()): ?>
|
||||
<meta name="csrf" content="<?= csrf() ?>">
|
||||
<?php endif ?>
|
||||
</head>
|
||||
|
||||
<body data-template="<?= $original->template() ?>">
|
||||
|
||||
<!-- ═══════════════════════════════════════════
|
||||
NAVIGATION LATÉRALE (ancres)
|
||||
════════════════════════════════════════════ -->
|
||||
<?php if (!empty($navItems)): ?>
|
||||
<nav class="nw-sidenav" aria-label="Navigation sections">
|
||||
<ul class="nw-sidenav__list">
|
||||
<li class="nw-sidenav__item nw-sidenav__item--intro">
|
||||
<a class="nw-sidenav__link" href="#nw-intro">
|
||||
<span class="nw-sidenav__dot"></span>
|
||||
<span class="nw-sidenav__label">Introduction</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php foreach ($navItems as $item): ?>
|
||||
<li class="nw-sidenav__item nw-sidenav__item--<?= $item['template'] ?>">
|
||||
<a class="nw-sidenav__link" href="#section-<?= $item['id'] ?>">
|
||||
<span class="nw-sidenav__dot"></span>
|
||||
<span class="nw-sidenav__label"><?= html($item['title']) ?></span>
|
||||
</a>
|
||||
<?php if (!empty($item['children'])): ?>
|
||||
<ul class="nw-sidenav__sub">
|
||||
<?php foreach ($item['children'] as $child): ?>
|
||||
<li class="nw-sidenav__sub-item">
|
||||
<a class="nw-sidenav__sub-link" href="#marker-<?= $child['id'] ?>">
|
||||
<span class="nw-sidenav__sub-dot"></span>
|
||||
<span class="nw-sidenav__sub-label"><?= html($child['title']) ?></span>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
<?php endif ?>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</nav>
|
||||
<?php endif ?>
|
||||
|
||||
<!-- ═══════════════════════════════════════════
|
||||
HERO / INTRODUCTION
|
||||
════════════════════════════════════════════ -->
|
||||
<header class="nw-hero" id="nw-intro">
|
||||
<?php if ($original->cover()->isNotEmpty() && $cover = $original->cover()->toFile()): ?>
|
||||
<div class="nw-hero__bg" style="background-image: url('<?= $cover->url() ?>')"></div>
|
||||
<div class="nw-hero__overlay"></div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="nw-hero__content">
|
||||
<h1 class="nw-hero__title"><?= html($original->title()) ?></h1>
|
||||
<?php if ($original->author()->isNotEmpty()): ?>
|
||||
<p class="nw-hero__author">Par <span><?= html($original->author()) ?></span></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Introduction -->
|
||||
<?php if ($original->introduction()->isNotEmpty()): ?>
|
||||
<section class="nw-introduction">
|
||||
<div class="nw-container nw-container--narrow">
|
||||
<div class="nw-introduction__body">
|
||||
<?= $original->introduction() ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif ?>
|
||||
|
||||
<!-- ═══════════════════════════════════════════
|
||||
SOUS-PAGES
|
||||
════════════════════════════════════════════ -->
|
||||
<main class="nw-main">
|
||||
<?php foreach ($subpages as $subpage):
|
||||
$tpl = $subpage->intendedTemplate()->name();
|
||||
?>
|
||||
|
||||
<!-- ─────────────────────────────────────────
|
||||
CARTE (map.yaml > marker.yaml)
|
||||
───────────────────────────────────────── -->
|
||||
<?php if ($tpl === 'map'): ?>
|
||||
<section class="nw-section nw-section--map" id="section-<?= $subpage->uid() ?>">
|
||||
<div class="nw-container">
|
||||
<header class="nw-section__header">
|
||||
<h2 class="nw-section__title"><?= html($subpage->title()) ?></h2>
|
||||
<?php if ($subpage->tags()->isNotEmpty()): ?>
|
||||
<div class="nw-tags">
|
||||
<?php foreach ($subpage->tags()->split(',') as $tag): ?>
|
||||
<span class="nw-tag"><?= html(trim($tag)) ?></span>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</header>
|
||||
|
||||
<?php if ($subpage->text()->isNotEmpty()): ?>
|
||||
<div class="nw-prose"><?= $subpage->text() ?></div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
|
||||
<!-- Carte MapLibre inline -->
|
||||
<?php
|
||||
$markers = $subpage->children()->listed()->filterBy('intendedTemplate', 'marker');
|
||||
$mapId = 'map-' . $subpage->uid();
|
||||
?>
|
||||
<div class="nw-map-wrap">
|
||||
<div id="<?= $mapId ?>" class="nw-map"></div>
|
||||
</div>
|
||||
|
||||
<!-- Fichiers de la carte -->
|
||||
<?php if ($subpage->files()->isNotEmpty()): ?>
|
||||
<div class="nw-container">
|
||||
<div class="nw-files">
|
||||
<?php foreach ($subpage->files() as $file): ?>
|
||||
<a href="<?= $file->url() ?>" class="nw-file" target="_blank">
|
||||
<span class="nw-file__icon">↓</span>
|
||||
<span class="nw-file__name"><?= html($file->filename()) ?></span>
|
||||
</a>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<!-- ── Liste des marqueurs sous la carte ── -->
|
||||
<?php if ($markers->isNotEmpty()): ?>
|
||||
<div class="nw-map-marker-list nw-container">
|
||||
<?php $mNum = 1; foreach ($markers as $marker): ?>
|
||||
<article
|
||||
class="nw-map-marker"
|
||||
id="marker-<?= $marker->uid() ?>"
|
||||
data-lat="<?= $marker->latitude()->value() ?>"
|
||||
data-lng="<?= $marker->longitude()->value() ?>"
|
||||
>
|
||||
<?php if ($marker->cover()->isNotEmpty() && $mCover = $marker->cover()->toFile()): ?>
|
||||
<div class="nw-map-marker__cover">
|
||||
<img src="<?= $mCover->url() ?>" alt="<?= html($marker->title()) ?>">
|
||||
<span class="nw-map-marker__num"><?= $mNum ?></span>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<span class="nw-map-marker__num nw-map-marker__num--no-cover"><?= $mNum ?></span>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="nw-map-marker__body">
|
||||
<h3 class="nw-map-marker__title"><?= html($marker->title()) ?></h3>
|
||||
<?php if ($marker->text()->isNotEmpty()): ?>
|
||||
<div class="nw-map-marker__content">
|
||||
<?php foreach ($marker->text()->toBlocks() as $block): ?>
|
||||
<div class="block block-type-<?= $block->type() ?>">
|
||||
<?= $block ?>
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</article>
|
||||
<?php $mNum++; endforeach ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var markers = <?php
|
||||
$mData = [];
|
||||
foreach ($markers as $m) {
|
||||
if ($m->latitude()->isNotEmpty() && $m->longitude()->isNotEmpty()) {
|
||||
$coverUrl = '';
|
||||
if ($m->cover()->isNotEmpty() && $mCov = $m->cover()->toFile()) {
|
||||
$coverUrl = $mCov->url();
|
||||
}
|
||||
$iconUrl = '';
|
||||
$iconSize = (int)$m->markerIconSize()->or(40)->value();
|
||||
if ($m->markerIcon()->isNotEmpty() && $icon = $m->markerIcon()->toFile()) {
|
||||
$iconUrl = $icon->url();
|
||||
}
|
||||
$mData[] = [
|
||||
'uid' => $m->uid(),
|
||||
'lat' => (float)$m->latitude()->value(),
|
||||
'lng' => (float)$m->longitude()->value(),
|
||||
'title' => $m->title()->value(),
|
||||
'coverUrl' => $coverUrl,
|
||||
'iconUrl' => $iconUrl,
|
||||
'iconSize' => $iconSize,
|
||||
];
|
||||
}
|
||||
}
|
||||
echo json_encode($mData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
?>;
|
||||
|
||||
// Centre de la carte
|
||||
var mapdata = <?php
|
||||
$defaultCenter = [43.836699, 4.360054];
|
||||
$defaultZoom = 13;
|
||||
if ($subpage->mapdata()->isNotEmpty()) {
|
||||
$raw = $subpage->mapdata()->value();
|
||||
$decoded = json_decode($raw, true);
|
||||
if ($decoded) {
|
||||
echo json_encode($decoded);
|
||||
} else {
|
||||
echo json_encode(['center' => $defaultCenter, 'zoom' => $defaultZoom]);
|
||||
}
|
||||
} else {
|
||||
echo json_encode(['center' => $defaultCenter, 'zoom' => $defaultZoom]);
|
||||
}
|
||||
?>;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var center = mapdata.center || [<?= $defaultCenter[1] ?>, <?= $defaultCenter[0] ?>];
|
||||
var zoom = mapdata.zoom || 13;
|
||||
|
||||
// MapLibre attend [lng, lat]
|
||||
if (Array.isArray(center) && center.length === 2) {
|
||||
// Si stocké [lat, lng], inverser
|
||||
if (center[0] > 90 || center[0] < -90) {
|
||||
// déjà [lng, lat]
|
||||
} else {
|
||||
center = [center[1], center[0]];
|
||||
}
|
||||
}
|
||||
|
||||
var map = new maplibregl.Map({
|
||||
container: '<?= $mapId ?>',
|
||||
style: 'https://api.maptiler.com/maps/dataviz/style.json?key=get_your_own_OpIi9ZULNHzrESv6T2vL',
|
||||
center: center,
|
||||
zoom: zoom,
|
||||
attributionControl: true
|
||||
});
|
||||
|
||||
// Fallback style OSM si pas de clé MapTiler
|
||||
map.on('error', function() {
|
||||
map.setStyle({
|
||||
version: 8,
|
||||
sources: {
|
||||
osm: {
|
||||
type: 'raster',
|
||||
tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
|
||||
tileSize: 256,
|
||||
attribution: '© OpenStreetMap contributors'
|
||||
}
|
||||
},
|
||||
layers: [{ id: 'osm', type: 'raster', source: 'osm' }]
|
||||
});
|
||||
});
|
||||
|
||||
markers.forEach(function(m) {
|
||||
var el = null;
|
||||
if (m.iconUrl) {
|
||||
el = document.createElement('div');
|
||||
el.className = 'nw-marker-icon';
|
||||
el.style.backgroundImage = 'url(' + m.iconUrl + ')';
|
||||
el.style.width = m.iconSize + 'px';
|
||||
el.style.height = m.iconSize + 'px';
|
||||
}
|
||||
|
||||
var markerOpts = { color: 'var(--nw-accent, #c0392b)' };
|
||||
if (el) markerOpts.element = el;
|
||||
|
||||
// Construire le HTML de la popup : cover + titre + ancre vers le texte
|
||||
var popupHtml = '';
|
||||
if (m.coverUrl) {
|
||||
popupHtml += '<div class="nw-popup-cover"><img src="' + m.coverUrl + '" alt="' + m.title + '"></div>';
|
||||
}
|
||||
popupHtml += '<div class="nw-popup-body">';
|
||||
if (m.title) {
|
||||
popupHtml += '<strong class="nw-popup-title">' + m.title + '</strong>';
|
||||
}
|
||||
popupHtml += '<a class="nw-popup-anchor" href="#marker-' + m.uid + '">Lire la fiche ↓</a>';
|
||||
popupHtml += '</div>';
|
||||
|
||||
var marker = new maplibregl.Marker(markerOpts)
|
||||
.setLngLat([m.lng, m.lat])
|
||||
.setPopup(new maplibregl.Popup({ offset: 25, maxWidth: '280px' }).setHTML(popupHtml))
|
||||
.addTo(map);
|
||||
});
|
||||
|
||||
// Adapter les bounds si on a des markers
|
||||
if (markers.length > 1) {
|
||||
var bounds = new maplibregl.LngLatBounds();
|
||||
markers.forEach(function(m) { bounds.extend([m.lng, m.lat]); });
|
||||
map.fitBounds(bounds, { padding: 60, maxZoom: 16 });
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</section>
|
||||
|
||||
<!-- ─────────────────────────────────────────
|
||||
GÉOFORMAT (geoformat.yaml > chapitre.yaml)
|
||||
───────────────────────────────────────── -->
|
||||
<?php elseif ($tpl === 'geoformat'): ?>
|
||||
<section class="nw-section nw-section--geoformat" id="section-<?= $subpage->uid() ?>">
|
||||
|
||||
<!-- En-tête géoformat -->
|
||||
<div class="nw-geoformat-hero">
|
||||
<?php if ($subpage->cover()->isNotEmpty() && $gCover = $subpage->cover()->toFile()): ?>
|
||||
<div class="nw-geoformat-hero__bg" style="background-image: url('<?= $gCover->url() ?>')"></div>
|
||||
<div class="nw-geoformat-hero__overlay"></div>
|
||||
<?php endif ?>
|
||||
<div class="nw-geoformat-hero__content nw-container">
|
||||
<h2 class="nw-geoformat-hero__title"><?= html($subpage->title()) ?></h2>
|
||||
<?php if ($subpage->subtitle()->isNotEmpty()): ?>
|
||||
<p class="nw-geoformat-hero__subtitle"><?= html($subpage->subtitle()) ?></p>
|
||||
<?php endif ?>
|
||||
<?php if ($subpage->tags()->isNotEmpty()): ?>
|
||||
<div class="nw-tags">
|
||||
<?php foreach ($subpage->tags()->split(',') as $tag): ?>
|
||||
<span class="nw-tag"><?= html(trim($tag)) ?></span>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chapeau -->
|
||||
<?php if ($subpage->text()->isNotEmpty()): ?>
|
||||
<div class="nw-container nw-container--narrow">
|
||||
<div class="nw-chapeau"><?= $subpage->text() ?></div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<!-- Chapitres -->
|
||||
<?php
|
||||
$chapitres = $subpage->children()->listed()->filterBy('intendedTemplate', 'chapitre');
|
||||
$chapCount = $chapitres->count();
|
||||
if ($chapCount > 0): ?>
|
||||
|
||||
<!-- Navigation chapitres locale -->
|
||||
<?php if ($chapCount > 1): ?>
|
||||
<div class="nw-container">
|
||||
<nav class="nw-chap-nav" aria-label="Chapitres">
|
||||
<?php $ci = 1; foreach ($chapitres as $chap): ?>
|
||||
<a class="nw-chap-nav__link" href="#chap-<?= $chap->uid() ?>">
|
||||
<span class="nw-chap-nav__num"><?= $ci ?></span>
|
||||
<span class="nw-chap-nav__title"><?= html($chap->title()) ?></span>
|
||||
</a>
|
||||
<?php $ci++; endforeach ?>
|
||||
</nav>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<?php $chapNum = 1; foreach ($chapitres as $chap): ?>
|
||||
<article class="nw-chapitre" id="chap-<?= $chap->uid() ?>">
|
||||
<div class="nw-container nw-container--narrow">
|
||||
<header class="nw-chapitre__header">
|
||||
<span class="nw-chapitre__num">Chapitre <?= $chapNum ?></span>
|
||||
<h3 class="nw-chapitre__title"><?= html($chap->title()) ?></h3>
|
||||
</header>
|
||||
<div class="nw-chapitre__body">
|
||||
<?= $chap->text()->toBlocks() ?>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<?php $chapNum++; endforeach ?>
|
||||
<?php endif ?>
|
||||
|
||||
</section>
|
||||
<?php endif ?>
|
||||
|
||||
<?php endforeach ?>
|
||||
</main>
|
||||
|
||||
<!-- ═══════════════════════════════════════════
|
||||
FOOTER
|
||||
════════════════════════════════════════════ -->
|
||||
<?php snippet('footer') ?>
|
||||
|
||||
<!-- Script animations sections -->
|
||||
<script>
|
||||
(function() {
|
||||
var sections = document.querySelectorAll('.nw-section');
|
||||
if (!sections.length) return;
|
||||
var io = new IntersectionObserver(function(entries) {
|
||||
entries.forEach(function(e) {
|
||||
if (e.isIntersecting) {
|
||||
e.target.classList.add('is-visible');
|
||||
io.unobserve(e.target);
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.08 });
|
||||
sections.forEach(function(s) { io.observe(s); });
|
||||
})();
|
||||
</script>
|
||||
|
||||
<!-- Script navigation latérale (active state au scroll) -->
|
||||
<script>
|
||||
(function() {
|
||||
var links = document.querySelectorAll('.nw-sidenav__link');
|
||||
var subLinks = document.querySelectorAll('.nw-sidenav__sub-link');
|
||||
var targets = document.querySelectorAll('[id^="section-"], #nw-intro');
|
||||
var markerTargets = document.querySelectorAll('[id^="marker-"]');
|
||||
if (!links.length || !targets.length) return;
|
||||
|
||||
// Observer sections principales
|
||||
var observer = new IntersectionObserver(function(entries) {
|
||||
entries.forEach(function(entry) {
|
||||
if (entry.isIntersecting) {
|
||||
links.forEach(function(l) { l.classList.remove('is-active'); });
|
||||
document.querySelectorAll('.nw-sidenav__item').forEach(function(li) {
|
||||
li.classList.remove('is-parent-active');
|
||||
});
|
||||
var active = document.querySelector('.nw-sidenav__link[href="#' + entry.target.id + '"]');
|
||||
if (active) {
|
||||
active.classList.add('is-active');
|
||||
var parentLi = active.closest('.nw-sidenav__item');
|
||||
if (parentLi) parentLi.classList.add('is-parent-active');
|
||||
}
|
||||
}
|
||||
});
|
||||
}, { rootMargin: '-20% 0px -70% 0px' });
|
||||
|
||||
targets.forEach(function(t) { observer.observe(t); });
|
||||
|
||||
// Observer marqueurs pour les sous-liens
|
||||
if (markerTargets.length && subLinks.length) {
|
||||
var subObserver = new IntersectionObserver(function(entries) {
|
||||
entries.forEach(function(entry) {
|
||||
if (entry.isIntersecting) {
|
||||
subLinks.forEach(function(l) { l.classList.remove('is-active'); });
|
||||
var active = document.querySelector('.nw-sidenav__sub-link[href="#' + entry.target.id + '"]');
|
||||
if (active) active.classList.add('is-active');
|
||||
}
|
||||
});
|
||||
}, { rootMargin: '-15% 0px -65% 0px' });
|
||||
markerTargets.forEach(function(t) { subObserver.observe(t); });
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||