Ajouter les pages personnalisées au site public
This commit is contained in:
parent
9e05196440
commit
21ee1bec36
17 changed files with 428 additions and 65 deletions
|
|
@ -31,10 +31,10 @@
|
|||
<div class="col-lg-6">
|
||||
<section class="card h-100">
|
||||
<div class="card-body">
|
||||
<span class="badge bg-blue-lt mb-2">1.0.0-rc.33</span>
|
||||
<h3>Un site public mieux connecté</h3>
|
||||
<p class="text-muted">Les réseaux sociaux renseignés par l’association rejoignent la navigation,
|
||||
avec des liens discrets, sécurisés et accessibles.</p>
|
||||
<span class="badge bg-blue-lt mb-2">1.0.0-rc.34</span>
|
||||
<h3>Un site public qui s’adapte au refuge</h3>
|
||||
<p class="text-muted">Pages personnalisées, contenus longs, panneaux repliables et ordre du menu
|
||||
réorganisable rendent chaque site aussi simple ou aussi complet que nécessaire.</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ $requestPath = rtrim((string)(parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_
|
|||
$activePage = match($requestPath) {
|
||||
'/','/site' => 'home','/animaux','/site/animaux','/adoption','/site/animal' => 'animals','/presentation','/site/presentation' => 'about','/dons','/site/dons' => 'donation','/contact','/site/contact' => 'contact',default => ''
|
||||
};
|
||||
$activeCustomSlug = '';
|
||||
if (preg_match('#^/site/page/([a-z0-9-]+)$#', $requestPath, $match)) $activeCustomSlug = $match[1];
|
||||
elseif (PublicSiteController::usesRoot() && preg_match('#^/([a-z0-9-]+)$#', $requestPath, $match)) $activeCustomSlug = $match[1];
|
||||
$activeSpecies = (string)($selectedSpecies ?? ($animal['species'] ?? ''));
|
||||
$navClass = static fn (bool$active): string => $active ? ' class="active" aria-current="page"' : '';
|
||||
$fullAddress = implode(', ', array_filter([$settings['association_address'] ?? '',trim(($settings['association_postal_code'] ?? '') . ' ' . ($settings['association_city'] ?? ''))]));
|
||||
|
|
@ -18,6 +21,16 @@ $socialNetworks = [
|
|||
'tiktok' => ['TikTok','♪'], 'whatsapp' => ['WhatsApp','☎'], 'linkedin' => ['LinkedIn','in'],
|
||||
'x' => ['X','𝕏'], 'snapchat' => ['Snapchat','👻'],
|
||||
];
|
||||
$customPagesBySlug = [];
|
||||
foreach (($publicPages ?? []) as $publicPage) $customPagesBySlug[$publicPage['slug']] = $publicPage;
|
||||
$navigationOrder = json_decode((string)($settings['public_site_navigation_order'] ?? '[]'), true);
|
||||
$navigationOrder = is_array($navigationOrder) ? $navigationOrder : [];
|
||||
foreach (['home','animals','about','donation','contact'] as $defaultNavigationItem) {
|
||||
if (!in_array($defaultNavigationItem, $navigationOrder, true)) $navigationOrder[] = $defaultNavigationItem;
|
||||
}
|
||||
foreach (array_keys($customPagesBySlug) as $slug) {
|
||||
if (!in_array('custom:' . $slug, $navigationOrder, true)) $navigationOrder[] = 'custom:' . $slug;
|
||||
}
|
||||
if (!function_exists('public_age')) {
|
||||
function public_age(array$a): string
|
||||
{
|
||||
|
|
@ -514,17 +527,18 @@ if (!function_exists('public_age')) {
|
|||
href="<?= h($siteUrl()) ?>"><?php if (AssociationBrand::path()): ?><img src="<?= h($siteUrl('logo')) ?>"
|
||||
alt=""><?php endif; ?><span><?= h($siteName) ?></span></a>
|
||||
<nav class="nav" aria-label="<?= h(t('public_site.navigation')) ?>">
|
||||
<a<?= $navClass($activePage === 'home') ?> href="<?= h($siteUrl()) ?>">
|
||||
<?= h(t('public_site.home')) ?></a><?php foreach ($publicSpecies as $species): ?>
|
||||
<a<?= $navClass($activePage === 'animals' && $activeSpecies === $species['code']) ?>
|
||||
href="<?= h($siteUrl('animals', ['espece' => $species['code']])) ?>">
|
||||
<?= h(($species['icon'] ?: '🐾') . ' ' . $species['name']) ?></a><?php endforeach; ?><?php if (($settings['public_site_about_enabled'] ?? '0') === '1'): ?>
|
||||
<a<?= $navClass($activePage === 'about') ?> href="<?= h($siteUrl('about')) ?>">
|
||||
<?= h($settings['public_site_about_title']) ?></a><?php endif; ?>
|
||||
<a<?= $navClass($activePage === 'donation') ?> href="<?= h($siteUrl('donation')) ?>">
|
||||
<?= h(t('public_site.donation')) ?></a>
|
||||
<a<?= $navClass($activePage === 'contact') ?> href="<?= h($siteUrl('contact')) ?>">
|
||||
<?= h(t('public_site.contact')) ?></a><?php foreach ($socialNetworks as $network => [$label,$icon]):
|
||||
<?php foreach ($navigationOrder as $navigationItem): ?>
|
||||
<?php if ($navigationItem === 'home'): ?><a<?= $navClass($activePage === 'home') ?> href="<?= h($siteUrl()) ?>"><?= h(t('public_site.home')) ?></a>
|
||||
<?php elseif ($navigationItem === 'animals'): foreach ($publicSpecies as $species): ?><a<?= $navClass($activePage === 'animals' && $activeSpecies === $species['code']) ?> href="<?= h($siteUrl('animals', ['espece' => $species['code']])) ?>"><?= h(($species['icon'] ?: '🐾') . ' ' . $species['name']) ?></a><?php endforeach; ?>
|
||||
<?php elseif ($navigationItem === 'about' && ($settings['public_site_about_enabled'] ?? '0') === '1'): ?><a<?= $navClass($activePage === 'about') ?> href="<?= h($siteUrl('about')) ?>"><?= h($settings['public_site_about_title']) ?></a>
|
||||
<?php elseif ($navigationItem === 'donation'): ?><a<?= $navClass($activePage === 'donation') ?> href="<?= h($siteUrl('donation')) ?>"><?= h(t('public_site.donation')) ?></a>
|
||||
<?php elseif ($navigationItem === 'contact'): ?><a<?= $navClass($activePage === 'contact') ?> href="<?= h($siteUrl('contact')) ?>"><?= h(t('public_site.contact')) ?></a>
|
||||
<?php elseif (str_starts_with((string)$navigationItem, 'custom:')):
|
||||
$navigationSlug = substr((string)$navigationItem, 7);
|
||||
$navigationPage = $customPagesBySlug[$navigationSlug] ?? null;
|
||||
if ($navigationPage): ?><a<?= $navClass($activeCustomSlug === $navigationSlug) ?> href="<?= h($siteUrl('custom', ['slug' => $navigationSlug])) ?>"><?= h($navigationPage['title']) ?></a><?php endif; endif; ?>
|
||||
<?php endforeach; ?>
|
||||
<?php foreach ($socialNetworks as $network => [$label,$icon]):
|
||||
$socialUrl = trim((string)($settings['public_site_' . $network . '_url'] ?? ''));
|
||||
if ($socialUrl === '') continue; ?>
|
||||
<a class="nav-social-link nav-social-<?= h($network) ?> social-<?= h($network) ?>"
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<form method="post" action="/settings/public-site/save"><input type="hidden" name="csrf" value="<?= h(Auth::csrf()) ?>">
|
||||
<input type="hidden" id="public-site-navigation-order" name="public_site_navigation_order" value="<?= h($settings['public_site_navigation_order'] ?? '') ?>">
|
||||
<div class="card mb-3">
|
||||
<div class="card-status-top bg-green"></div>
|
||||
<div class="card-header">
|
||||
|
|
@ -78,10 +79,17 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-2">
|
||||
<div><h3 class="mb-0"><?= h(t('public_site.site_pages')) ?></h3><div class="text-muted small"><?= h(t('public_site.site_pages_help')) ?></div></div>
|
||||
<button class="btn btn-outline-primary" type="button" id="public-page-add">+ <?= h(t('public_site.add_page')) ?></button>
|
||||
</div>
|
||||
<div id="public-pages-sortable" class="mb-3">
|
||||
<details class="card mb-2 public-page-panel public-sortable-page" data-page-key="home" draggable="true" open>
|
||||
<summary class="card-header gap-2">
|
||||
<span class="public-page-drag" title="<?= h(t('public_site.drag_page')) ?>" aria-hidden="true">⠿</span>
|
||||
<h3 class="card-title">🏠 <?= h(t('public_site.home')) ?></h3>
|
||||
</div>
|
||||
<span class="ms-auto d-flex gap-1 public-page-actions"><button class="btn btn-sm btn-icon btn-outline-secondary public-page-up" type="button" aria-label="<?= h(t('public_site.move_up')) ?>">↑</button><button class="btn btn-sm btn-icon btn-outline-secondary public-page-down" type="button" aria-label="<?= h(t('public_site.move_down')) ?>">↓</button><span class="public-page-chevron" aria-hidden="true">⌄</span></span>
|
||||
</summary>
|
||||
<div class="card-body row g-3">
|
||||
<div class="col-12"><label class="form-label"><?= h(t('common.title')) ?></label><input class="form-control"
|
||||
name="public_site_home_title" value="<?= h($settings['public_site_home_title']) ?>" required></div>
|
||||
|
|
@ -89,11 +97,14 @@
|
|||
class="form-control public-rich-text" name="public_site_home_text"
|
||||
rows="4"><?= h($settings['public_site_home_text']) ?></textarea></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
</details>
|
||||
<div class="card mb-2 public-sortable-page" data-page-key="animals" draggable="true"><div class="card-header gap-2"><span class="public-page-drag" title="<?= h(t('public_site.drag_page')) ?>" aria-hidden="true">⠿</span><h3 class="card-title">🐾 <?= h(t('public_site.animals')) ?></h3><span class="ms-auto d-flex gap-1 public-page-actions"><button class="btn btn-sm btn-icon btn-outline-secondary public-page-up" type="button" aria-label="<?= h(t('public_site.move_up')) ?>">↑</button><button class="btn btn-sm btn-icon btn-outline-secondary public-page-down" type="button" aria-label="<?= h(t('public_site.move_down')) ?>">↓</button></span></div></div>
|
||||
<details class="card mb-2 public-page-panel public-sortable-page" data-page-key="about" draggable="true">
|
||||
<summary class="card-header gap-2">
|
||||
<span class="public-page-drag" title="<?= h(t('public_site.drag_page')) ?>" aria-hidden="true">⠿</span>
|
||||
<h3 class="card-title">📖 <?= h(t('public_site.free_page')) ?></h3>
|
||||
</div>
|
||||
<span class="ms-auto d-flex gap-1 public-page-actions"><button class="btn btn-sm btn-icon btn-outline-secondary public-page-up" type="button" aria-label="<?= h(t('public_site.move_up')) ?>">↑</button><button class="btn btn-sm btn-icon btn-outline-secondary public-page-down" type="button" aria-label="<?= h(t('public_site.move_down')) ?>">↓</button><span class="public-page-chevron" aria-hidden="true">⌄</span></span>
|
||||
</summary>
|
||||
<div class="card-body row g-3">
|
||||
<div class="col-12"><label class="form-check form-switch"><input class="form-check-input" type="checkbox"
|
||||
name="public_site_about_enabled" value="1"
|
||||
|
|
@ -106,11 +117,13 @@
|
|||
class="form-control public-rich-text" name="public_site_about_text"
|
||||
rows="5"><?= h($settings['public_site_about_text']) ?></textarea></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
</details>
|
||||
<details class="card mb-2 public-page-panel public-sortable-page" data-page-key="donation" draggable="true">
|
||||
<summary class="card-header gap-2">
|
||||
<span class="public-page-drag" title="<?= h(t('public_site.drag_page')) ?>" aria-hidden="true">⠿</span>
|
||||
<h3 class="card-title">💝 <?= h(t('public_site.donation')) ?></h3>
|
||||
</div>
|
||||
<span class="ms-auto d-flex gap-1 public-page-actions"><button class="btn btn-sm btn-icon btn-outline-secondary public-page-up" type="button" aria-label="<?= h(t('public_site.move_up')) ?>">↑</button><button class="btn btn-sm btn-icon btn-outline-secondary public-page-down" type="button" aria-label="<?= h(t('public_site.move_down')) ?>">↓</button><span class="public-page-chevron" aria-hidden="true">⌄</span></span>
|
||||
</summary>
|
||||
<div class="card-body row g-3">
|
||||
<div class="col-md-4"><label class="form-label"><?= h(t('common.title')) ?></label><input
|
||||
class="form-control" name="public_site_donation_title"
|
||||
|
|
@ -122,11 +135,13 @@
|
|||
class="form-control public-rich-text" name="public_site_donation_text"
|
||||
rows="4"><?= h($settings['public_site_donation_text']) ?></textarea></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
</details>
|
||||
<details class="card mb-2 public-page-panel public-sortable-page" data-page-key="contact" draggable="true">
|
||||
<summary class="card-header gap-2">
|
||||
<span class="public-page-drag" title="<?= h(t('public_site.drag_page')) ?>" aria-hidden="true">⠿</span>
|
||||
<h3 class="card-title">✉️ <?= h(t('public_site.contact')) ?></h3>
|
||||
</div>
|
||||
<span class="ms-auto d-flex gap-1 public-page-actions"><button class="btn btn-sm btn-icon btn-outline-secondary public-page-up" type="button" aria-label="<?= h(t('public_site.move_up')) ?>">↑</button><button class="btn btn-sm btn-icon btn-outline-secondary public-page-down" type="button" aria-label="<?= h(t('public_site.move_down')) ?>">↓</button><span class="public-page-chevron" aria-hidden="true">⌄</span></span>
|
||||
</summary>
|
||||
<div class="card-body row g-3">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-info py-2"><?= h(t('public_site.contact_reuse')) ?></div><label
|
||||
|
|
@ -143,11 +158,41 @@
|
|||
value="<?= h($settings['public_site_' . $network . '_url']) ?>" placeholder="https://..."></div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</details>
|
||||
<?php foreach (($customPages ?? []) as $index => $page): ?>
|
||||
<details class="card mb-2 public-page-panel public-custom-page public-sortable-page" data-page-key="custom:<?= h($page['slug']) ?>" draggable="true">
|
||||
<summary class="card-header gap-2">
|
||||
<span class="public-page-drag" title="<?= h(t('public_site.drag_page')) ?>" aria-hidden="true">⠿</span>
|
||||
<h3 class="card-title public-page-summary-title"><?= h($page['title']) ?></h3>
|
||||
<span class="ms-auto d-flex gap-1 public-page-actions">
|
||||
<button class="btn btn-sm btn-icon btn-outline-secondary public-page-up" type="button" title="<?= h(t('public_site.move_up')) ?>" aria-label="<?= h(t('public_site.move_up')) ?>">↑</button>
|
||||
<button class="btn btn-sm btn-icon btn-outline-secondary public-page-down" type="button" title="<?= h(t('public_site.move_down')) ?>" aria-label="<?= h(t('public_site.move_down')) ?>">↓</button>
|
||||
<span class="public-page-chevron" aria-hidden="true">⌄</span>
|
||||
</span>
|
||||
</summary>
|
||||
<div class="card-body row g-3">
|
||||
<input type="hidden" name="pages[<?= $index ?>][id]" value="<?= (int) $page['id'] ?>">
|
||||
<input class="public-page-position" type="hidden" name="pages[<?= $index ?>][position]" value="<?= $index ?>">
|
||||
<div class="col-12"><label class="form-check form-switch"><input class="form-check-input" type="checkbox" name="pages[<?= $index ?>][enabled]" value="1" <?= (int) $page['enabled'] === 1 ? 'checked' : '' ?>><span class="form-check-label"><?= h(t('public_site.page_published')) ?></span></label></div>
|
||||
<div class="col-md-7"><label class="form-label"><?= h(t('public_site.page_title')) ?></label><input class="form-control public-page-title" name="pages[<?= $index ?>][title]" value="<?= h($page['title']) ?>" required></div>
|
||||
<div class="col-md-5"><label class="form-label"><?= h(t('public_site.page_slug')) ?></label><input class="form-control public-page-slug" name="pages[<?= $index ?>][slug]" value="<?= h($page['slug']) ?>" required><div class="form-hint"><?= h(t('public_site.page_slug_help')) ?></div></div>
|
||||
<div class="col-12"><label class="form-label"><?= h(t('common.text')) ?></label><textarea class="form-control public-rich-text" name="pages[<?= $index ?>][content]" rows="8"><?= h($page['content']) ?></textarea></div>
|
||||
<div class="col-12"><label class="form-check text-danger"><input class="form-check-input" type="checkbox" name="pages[<?= $index ?>][delete]" value="1"><span class="form-check-label"><?= h(t('public_site.delete_page')) ?></span></label></div>
|
||||
</div>
|
||||
</details>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<template id="public-page-template">
|
||||
<details class="card mb-2 public-page-panel public-custom-page public-sortable-page" data-page-key="custom:" draggable="true" open>
|
||||
<summary class="card-header gap-2"><span class="public-page-drag" title="<?= h(t('public_site.drag_page')) ?>" aria-hidden="true">⠿</span><h3 class="card-title public-page-summary-title"><?= h(t('public_site.new_page')) ?></h3><span class="ms-auto d-flex gap-1 public-page-actions"><button class="btn btn-sm btn-icon btn-outline-secondary public-page-up" type="button" aria-label="<?= h(t('public_site.move_up')) ?>">↑</button><button class="btn btn-sm btn-icon btn-outline-secondary public-page-down" type="button" aria-label="<?= h(t('public_site.move_down')) ?>">↓</button><span class="public-page-chevron" aria-hidden="true">⌄</span></span></summary>
|
||||
<div class="card-body row g-3"><input class="public-page-position" type="hidden" name="pages[__INDEX__][position]" value="0"><div class="col-12"><label class="form-check form-switch"><input class="form-check-input" type="checkbox" name="pages[__INDEX__][enabled]" value="1" checked><span class="form-check-label"><?= h(t('public_site.page_published')) ?></span></label></div><div class="col-md-7"><label class="form-label"><?= h(t('public_site.page_title')) ?></label><input class="form-control public-page-title" name="pages[__INDEX__][title]" required></div><div class="col-md-5"><label class="form-label"><?= h(t('public_site.page_slug')) ?></label><input class="form-control public-page-slug" name="pages[__INDEX__][slug]" required><div class="form-hint"><?= h(t('public_site.page_slug_help')) ?></div></div><div class="col-12"><label class="form-label"><?= h(t('common.text')) ?></label><textarea class="form-control public-rich-text" name="pages[__INDEX__][content]" rows="8"></textarea></div><div class="col-12"><button class="btn btn-outline-danger public-page-remove" type="button"><?= h(t('public_site.delete_page')) ?></button></div></div>
|
||||
</details>
|
||||
</template>
|
||||
<details class="card mb-3 public-page-panel">
|
||||
<summary class="card-header">
|
||||
<h3 class="card-title">⚖️ <?= h(t('public_site.legal_privacy')) ?></h3>
|
||||
</div>
|
||||
<span class="public-page-chevron" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="card-body row g-3">
|
||||
<div class="col-md-6"><label class="form-label"><?= h(t('public_site.legal')) ?></label><textarea
|
||||
class="form-control" name="public_site_legal_text"
|
||||
|
|
@ -157,9 +202,12 @@
|
|||
rows="16"><?= h(PublicSiteController::privacyText($settings)) ?></textarea></div>
|
||||
<div class="col-12 form-hint"><?= h(t('public_site.legal_hint')) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
<div class="text-end mb-4"><button class="btn btn-primary"><?= h(t('common.save')) ?></button></div>
|
||||
</form>
|
||||
<style>
|
||||
.public-page-panel>summary{cursor:pointer;display:flex;align-items:center;list-style:none}.public-page-panel>summary::-webkit-details-marker{display:none}.public-page-chevron{display:inline-flex;transition:transform .2s;font-size:1.25rem}.public-page-panel[open]>summary .public-page-chevron{transform:rotate(180deg)}.public-page-drag{cursor:grab;font-size:1.35rem;color:var(--tblr-secondary);padding:.25rem}.public-custom-page.is-dragging{opacity:.45}.public-custom-page.drag-over{outline:2px dashed var(--tblr-primary);outline-offset:2px}.public-page-actions button{position:relative;z-index:2}
|
||||
</style>
|
||||
<script>
|
||||
let activePublicEditor = null;
|
||||
const imageLibrary = document.getElementById('public-image-library');
|
||||
|
|
@ -173,15 +221,20 @@ const insertImage = (button) => {
|
|||
textarea.setRangeText(`\n`, textarea.selectionStart, textarea.selectionEnd, 'end');
|
||||
textarea.focus();
|
||||
};
|
||||
document.querySelectorAll('textarea').forEach((textarea) => {
|
||||
const enhanceTextarea = (textarea) => {
|
||||
if (textarea.dataset.autoResize === '1') return;
|
||||
textarea.dataset.autoResize = '1';
|
||||
const resize = () => {
|
||||
textarea.style.height = 'auto';
|
||||
textarea.style.height = Math.max(140, textarea.scrollHeight + 8) + 'px';
|
||||
};
|
||||
textarea.addEventListener('input', resize);
|
||||
resize();
|
||||
});
|
||||
document.querySelectorAll('.public-rich-text').forEach((textarea) => {
|
||||
};
|
||||
const enhancePublicEditor = (textarea) => {
|
||||
enhanceTextarea(textarea);
|
||||
if (textarea.dataset.richEditor === '1') return;
|
||||
textarea.dataset.richEditor = '1';
|
||||
textarea.addEventListener('focus', () => { activePublicEditor = textarea; });
|
||||
const toolbar = document.createElement('div');
|
||||
toolbar.className = 'btn-list mb-2';
|
||||
|
|
@ -211,7 +264,9 @@ document.querySelectorAll('.public-rich-text').forEach((textarea) => {
|
|||
});
|
||||
toolbar.append(imageButton);
|
||||
textarea.before(toolbar);
|
||||
});
|
||||
};
|
||||
document.querySelectorAll('textarea').forEach(enhanceTextarea);
|
||||
document.querySelectorAll('.public-rich-text').forEach(enhancePublicEditor);
|
||||
document.querySelectorAll('.public-image-insert').forEach((button) => {
|
||||
button.addEventListener('click', () => insertImage(button));
|
||||
});
|
||||
|
|
@ -243,4 +298,86 @@ document.getElementById('public-image-upload').addEventListener('submit', async
|
|||
submit.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
const pagesContainer = document.getElementById('public-pages-sortable');
|
||||
const navigationOrderInput = document.getElementById('public-site-navigation-order');
|
||||
let publicPageIndex = <?= (int) count($customPages ?? []) ?>;
|
||||
const refreshPagePositions = () => {
|
||||
const pages = [...pagesContainer.querySelectorAll('.public-sortable-page')];
|
||||
pages.forEach((page, position) => {
|
||||
const input = page.querySelector('.public-page-position');
|
||||
if (input) input.value = String(position);
|
||||
page.querySelector('.public-page-up')?.toggleAttribute('disabled', position === 0);
|
||||
page.querySelector('.public-page-down')?.toggleAttribute('disabled', position === pages.length - 1);
|
||||
});
|
||||
navigationOrderInput.value = JSON.stringify(pages.map((page) => page.dataset.pageKey).filter((key) => key && key !== 'custom:'));
|
||||
};
|
||||
const slugify = (value) => value.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 80);
|
||||
const wirePage = (page) => {
|
||||
const title = page.querySelector('.public-page-title');
|
||||
const slug = page.querySelector('.public-page-slug');
|
||||
let slugEdited = Boolean(slug?.value);
|
||||
slug?.addEventListener('input', () => { slugEdited = Boolean(slug.value); });
|
||||
slug?.addEventListener('input', () => { page.dataset.pageKey = 'custom:' + slugify(slug.value); refreshPagePositions(); });
|
||||
title?.addEventListener('input', () => {
|
||||
page.querySelector('.public-page-summary-title').textContent = title.value.trim() || <?= json_encode(t('public_site.new_page'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||
if (!slugEdited && slug) {
|
||||
slug.value = slugify(title.value);
|
||||
page.dataset.pageKey = 'custom:' + slug.value;
|
||||
refreshPagePositions();
|
||||
}
|
||||
});
|
||||
page.querySelectorAll('.public-page-actions button').forEach((button) => button.addEventListener('click', (event) => event.stopPropagation()));
|
||||
page.querySelectorAll('input,textarea,button').forEach((control) => control.addEventListener('mousedown', () => { page.draggable = false; }));
|
||||
page.querySelector('.public-page-drag')?.addEventListener('mousedown', () => { page.draggable = true; });
|
||||
page.querySelector('.public-page-up')?.addEventListener('click', () => {
|
||||
if (page.previousElementSibling) pagesContainer.insertBefore(page, page.previousElementSibling);
|
||||
refreshPagePositions();
|
||||
});
|
||||
page.querySelector('.public-page-down')?.addEventListener('click', () => {
|
||||
if (page.nextElementSibling) pagesContainer.insertBefore(page.nextElementSibling, page);
|
||||
refreshPagePositions();
|
||||
});
|
||||
page.querySelector('.public-page-remove')?.addEventListener('click', () => { page.remove(); refreshPagePositions(); });
|
||||
page.querySelectorAll('textarea').forEach(enhanceTextarea);
|
||||
page.querySelectorAll('.public-rich-text').forEach(enhancePublicEditor);
|
||||
page.addEventListener('dragstart', (event) => {
|
||||
if (!event.target.closest('.public-page-drag') && document.activeElement !== page.querySelector('.public-page-drag')) {
|
||||
// The whole card remains draggable for browser compatibility.
|
||||
}
|
||||
page.classList.add('is-dragging');
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
});
|
||||
page.addEventListener('dragend', () => {
|
||||
page.draggable = true;
|
||||
page.classList.remove('is-dragging');
|
||||
pagesContainer.querySelectorAll('.drag-over').forEach((item) => item.classList.remove('drag-over'));
|
||||
refreshPagePositions();
|
||||
});
|
||||
};
|
||||
let savedNavigationOrder = [];
|
||||
try { savedNavigationOrder = JSON.parse(navigationOrderInput.value || '[]'); } catch (_) {}
|
||||
const initialPages = [...pagesContainer.querySelectorAll('.public-sortable-page')];
|
||||
const orderedPages = savedNavigationOrder.map((key) => initialPages.find((item) => item.dataset.pageKey === key)).filter(Boolean);
|
||||
[...orderedPages, ...initialPages.filter((item) => !orderedPages.includes(item))].forEach((page) => pagesContainer.append(page));
|
||||
pagesContainer.querySelectorAll('.public-sortable-page').forEach(wirePage);
|
||||
pagesContainer.addEventListener('dragover', (event) => {
|
||||
event.preventDefault();
|
||||
const dragging = pagesContainer.querySelector('.is-dragging');
|
||||
const target = event.target.closest('.public-sortable-page');
|
||||
if (!dragging || !target || dragging === target) return;
|
||||
pagesContainer.querySelectorAll('.drag-over').forEach((item) => item.classList.remove('drag-over'));
|
||||
target.classList.add('drag-over');
|
||||
const box = target.getBoundingClientRect();
|
||||
pagesContainer.insertBefore(dragging, event.clientY < box.top + box.height / 2 ? target : target.nextElementSibling);
|
||||
});
|
||||
document.getElementById('public-page-add').addEventListener('click', () => {
|
||||
const html = document.getElementById('public-page-template').innerHTML.replaceAll('__INDEX__', String(publicPageIndex++));
|
||||
pagesContainer.insertAdjacentHTML('beforeend', html);
|
||||
const page = pagesContainer.lastElementChild;
|
||||
wirePage(page);
|
||||
refreshPagePositions();
|
||||
page.querySelector('.public-page-title').focus();
|
||||
});
|
||||
refreshPagePositions();
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in a new issue