ajout dropdown de deconnection
This commit is contained in:
parent
50360a590b
commit
c14507a716
7 changed files with 199 additions and 16 deletions
|
|
@ -13,6 +13,8 @@
|
||||||
/* Backgrounds de navigation */
|
/* Backgrounds de navigation */
|
||||||
--nav-bg-light: rgba(255, 255, 255, 0.5);
|
--nav-bg-light: rgba(255, 255, 255, 0.5);
|
||||||
--nav-bg-dark: rgba(12, 10, 18, 0.4);
|
--nav-bg-dark: rgba(12, 10, 18, 0.4);
|
||||||
|
--nav-bg-light-so: rgba(255, 255, 255, 0.82);
|
||||||
|
--nav-bg-dark-so: rgba(12, 10, 18, 0.8);
|
||||||
|
|
||||||
/* Couleurs de texte */
|
/* Couleurs de texte */
|
||||||
--text-light: #1a1625;
|
--text-light: #1a1625;
|
||||||
|
|
@ -30,7 +32,7 @@
|
||||||
|
|
||||||
/* Effets de blur */
|
/* Effets de blur */
|
||||||
--blur-sm: 8px;
|
--blur-sm: 8px;
|
||||||
--blur-md: 20px;
|
--blur-md: 25px;
|
||||||
|
|
||||||
/* Durées de transition */
|
/* Durées de transition */
|
||||||
--duration-fast: 0.15s; /* Transforms rapides */
|
--duration-fast: 0.15s; /* Transforms rapides */
|
||||||
|
|
|
||||||
|
|
@ -73,13 +73,7 @@ header nav {
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── LIENS DE NAVIGATION — style calqué sur .login-btn ─────────── */
|
/* ── LIENS DE NAVIGATION ───────────────────────────────────────── */
|
||||||
/*
|
|
||||||
"all: unset" neutralise Bootstrap 5 entièrement sur .nav-link,
|
|
||||||
puis on réapplique exactement ce qu'on veut.
|
|
||||||
Les !important sur padding/border/background garantissent
|
|
||||||
qu'aucune règle Bootstrap résiduelle ne peut l'écraser.
|
|
||||||
*/
|
|
||||||
header nav .nav-link {
|
header nav .nav-link {
|
||||||
all: unset;
|
all: unset;
|
||||||
|
|
||||||
|
|
@ -90,7 +84,7 @@ header nav .nav-link {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: none !important;
|
text-decoration: none !important;
|
||||||
|
|
||||||
/* Dimensions — identiques au .login-btn */
|
/* Dimensions */
|
||||||
padding: 0.55rem 1.1rem !important;
|
padding: 0.55rem 1.1rem !important;
|
||||||
border-radius: 10px !important;
|
border-radius: 10px !important;
|
||||||
border: 1px solid var(--border-light) !important;
|
border: 1px solid var(--border-light) !important;
|
||||||
|
|
@ -153,6 +147,80 @@ header nav .nav-link.active:hover {
|
||||||
header nav .nav-link::before,
|
header nav .nav-link::before,
|
||||||
header nav .nav-link::after { display: none !important; }
|
header nav .nav-link::after { display: none !important; }
|
||||||
|
|
||||||
|
/* ── LIENS DROPDOWN ─────────── */
|
||||||
|
header .header-dropdown .dropdown-link {
|
||||||
|
all: unset;
|
||||||
|
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
gap: 0.45rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none !important;
|
||||||
|
|
||||||
|
/* Dimensions */
|
||||||
|
padding: 0.55rem 1.1rem !important;
|
||||||
|
border-radius: 10px !important;
|
||||||
|
border: 1px solid var(--border-light) !important;
|
||||||
|
background: transparent !important;
|
||||||
|
|
||||||
|
/* Typographie */
|
||||||
|
font-family: 'Syne', sans-serif !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
letter-spacing: 0.03em !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
color: var(--muted-light) !important;
|
||||||
|
|
||||||
|
transition:
|
||||||
|
color var(--duration-normal) ease,
|
||||||
|
background var(--duration-normal) ease,
|
||||||
|
border-color var(--duration-normal) ease,
|
||||||
|
box-shadow var(--duration-normal) ease,
|
||||||
|
transform var(--duration-fast) ease !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] header .header-dropdown .dropdown-link {
|
||||||
|
border-color: var(--border-dark) !important;
|
||||||
|
color: var(--muted-dark) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover */
|
||||||
|
header .header-dropdown .dropdown-link:hover {
|
||||||
|
color: var(--violet) !important;
|
||||||
|
background: rgba(0, 0, 0, 0.05) !important;
|
||||||
|
border-color: rgba(0, 0, 0, 0.15) !important;
|
||||||
|
transform: translateY(-1px) !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
[data-theme="dark"] header .header-dropdown .dropdown-link:hover {
|
||||||
|
color: var(--violet-light) !important;
|
||||||
|
background: rgba(255, 255, 255, 0.07) !important;
|
||||||
|
border-color: rgba(255, 255, 255, 0.15) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actif — fond violet léger + bordure violet */
|
||||||
|
header .header-dropdown .dropdown-link.active {
|
||||||
|
color: var(--violet) !important;
|
||||||
|
background: var(--pill-bg-light) !important;
|
||||||
|
border-color: rgba(124, 58, 237, 0.30) !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
}
|
||||||
|
[data-theme="dark"] header .header-dropdown .dropdown-link.active {
|
||||||
|
color: var(--violet-light) !important;
|
||||||
|
background: var(--pill-bg-dark) !important;
|
||||||
|
border-color: rgba(167, 139, 250, 0.30) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actif + hover — glow subtil */
|
||||||
|
header .header-dropdown .dropdown-link.active:hover {
|
||||||
|
box-shadow: 0 4px 16px rgba(124, 58, 237, 0.20) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pas de pseudo-élément soulignement */
|
||||||
|
header .header-dropdown .dropdown-link::before,
|
||||||
|
header .header-dropdown .dropdown-link::after { display: none !important; }
|
||||||
|
|
||||||
/* ── ACTIONS (droite) ──────────────────────────────────────────── */
|
/* ── ACTIONS (droite) ──────────────────────────────────────────── */
|
||||||
.header-actions {
|
.header-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -223,14 +291,15 @@ header nav .nav-link::after { display: none !important; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── DROPDOWN CONNEXION ────────────────────────────────────────── */
|
/* ── DROPDOWN CONNEXION ────────────────────────────────────────── */
|
||||||
.login-wrapper { position: relative; }
|
.header-wrapper { position: relative; }
|
||||||
|
|
||||||
.login-dropdown {
|
.header-dropdown {
|
||||||
|
z-index: 100;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(100% + 10px);
|
top: calc(100% + 10px);
|
||||||
right: 0;
|
right: 0;
|
||||||
width: 260px;
|
width: 260px;
|
||||||
background: var(--nav-bg-light);
|
background: var(--nav-bg-light-so);
|
||||||
backdrop-filter: blur(var(--blur-md));
|
backdrop-filter: blur(var(--blur-md));
|
||||||
-webkit-backdrop-filter: blur(var(--blur-md));
|
-webkit-backdrop-filter: blur(var(--blur-md));
|
||||||
border: 1px solid var(--border-light);
|
border: 1px solid var(--border-light);
|
||||||
|
|
@ -242,12 +311,12 @@ header nav .nav-link::after { display: none !important; }
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
animation: dropdown-in var(--duration-normal) ease;
|
animation: dropdown-in var(--duration-normal) ease;
|
||||||
}
|
}
|
||||||
[data-theme="dark"] .login-dropdown {
|
[data-theme="dark"] .header-dropdown {
|
||||||
background: var(--nav-bg-dark);
|
background: var(--nav-bg-dark-so);
|
||||||
border-color: var(--border-dark);
|
border-color: var(--border-dark);
|
||||||
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.4);
|
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
.login-dropdown.open { display: flex; }
|
.header-dropdown.open { display: flex; }
|
||||||
|
|
||||||
@keyframes dropdown-in {
|
@keyframes dropdown-in {
|
||||||
from { opacity: 0; transform: translateY(-6px); }
|
from { opacity: 0; transform: translateY(-6px); }
|
||||||
|
|
|
||||||
6
config/routes/routing.controllers.yaml
Normal file
6
config/routes/routing.controllers.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Load routes from all controllers in src/Controller/
|
||||||
|
controllers:
|
||||||
|
resource: ../../src/Controller/
|
||||||
|
type: attribute
|
||||||
|
|
||||||
|
|
||||||
40
src/EventSubscriber/TwigGlobalSubscriber.php
Normal file
40
src/EventSubscriber/TwigGlobalSubscriber.php
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\EventSubscriber;
|
||||||
|
|
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
use Symfony\Component\HttpKernel\Event\ControllerEvent;
|
||||||
|
use Symfony\Component\HttpKernel\KernelEvents;
|
||||||
|
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||||
|
use Twig\Environment;
|
||||||
|
|
||||||
|
class TwigGlobalSubscriber implements EventSubscriberInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly Environment $twig,
|
||||||
|
private readonly AuthenticationUtils $authUtils,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function onKernelController(ControllerEvent $event): void
|
||||||
|
{
|
||||||
|
if (!$event->isMainRequest()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->twig->addGlobal('error', $this->authUtils->getLastAuthenticationError());
|
||||||
|
$this->twig->addGlobal('last_username', $this->authUtils->getLastUsername());
|
||||||
|
} catch (\LogicException) {
|
||||||
|
// Route hors pare-feu (assets, profiler...) — on ignore
|
||||||
|
$this->twig->addGlobal('error', null);
|
||||||
|
$this->twig->addGlobal('last_username', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getSubscribedEvents(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
KernelEvents::CONTROLLER => 'onKernelController',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -38,7 +38,11 @@
|
||||||
{# ── Droite : Actions (thème + connexion) ─────────────────── #}
|
{# ── Droite : Actions (thème + connexion) ─────────────────── #}
|
||||||
<div class="header-actions">
|
<div class="header-actions">
|
||||||
{% include 'partials/_theme_toggle.html.twig' %}
|
{% include 'partials/_theme_toggle.html.twig' %}
|
||||||
|
{% if is_granted('ROLE_USER') %}
|
||||||
|
{% include 'partials/_logout_dropdown.html.twig' %}
|
||||||
|
{% else %}
|
||||||
{% include 'partials/_login_dropdown.html.twig' %}
|
{% include 'partials/_login_dropdown.html.twig' %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
|
|
|
||||||
62
templates/partials/_logout_dropdown.html.twig
Normal file
62
templates/partials/_logout_dropdown.html.twig
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
{# ══════════════════════════════════════════════════════════════
|
||||||
|
templates/partials/_login_dropdown.html.twig
|
||||||
|
Bouton + dropdown formulaire de connexion
|
||||||
|
══════════════════════════════════════════════════════════════ #}
|
||||||
|
|
||||||
|
<div class="header-wrapper" id="loginWrapper"
|
||||||
|
data-controller="header-dropdown"
|
||||||
|
data-header-dropdown-target="wrapper">
|
||||||
|
|
||||||
|
{# Bouton déclencheur #}
|
||||||
|
<button class="login-btn" id="loginToggle"
|
||||||
|
aria-haspopup="true" aria-expanded="false"
|
||||||
|
data-header-dropdown-target="toggle">
|
||||||
|
{% if app.user.uriPp is not null %}
|
||||||
|
<img src="{{ asset(app.user.uri_pp) }}" alt="Photo de profil de {{ app.user.pseudo }}" class="profile-pic">
|
||||||
|
{% else %}
|
||||||
|
<svg width="15" height="15" viewBox="0 0 24 24"
|
||||||
|
fill="none" stroke="currentColor" stroke-width="2"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
|
||||||
|
<circle cx="12" cy="7" r="4"/>
|
||||||
|
</svg>
|
||||||
|
{% endif %}
|
||||||
|
{{ app.user.pseudo }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{# Dropdown #}
|
||||||
|
<div class="header-dropdown" id="headerDropdown"
|
||||||
|
role="dialog" aria-label="Formulaire de connexion"
|
||||||
|
data-header-dropdown-target="dropdown">
|
||||||
|
|
||||||
|
<span class="dropdown-title">Bonjour {{ app.user.pseudo }}</span>
|
||||||
|
|
||||||
|
{% if is_granted("ROLE_ADMIN") %}
|
||||||
|
<a href="{{ path('app_accueil') }}"
|
||||||
|
class="dropdown-link {{ current_route == 'app_home' ? 'active' }}">
|
||||||
|
Administration
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<a href="{{ path('app_accueil') }}"
|
||||||
|
class="dropdown-link {{ current_route == 'app_home' ? 'active' }}">
|
||||||
|
Parametre
|
||||||
|
</a>
|
||||||
|
<form action="{{ path('app_logout') }}" method="post" >
|
||||||
|
<input type="hidden"
|
||||||
|
name="_csrf_token"
|
||||||
|
value="{{ csrf_token('authenticate') }}">
|
||||||
|
|
||||||
|
{# Retenir la page courante pour y revenir en cas d'erreur #}
|
||||||
|
<input type="hidden"
|
||||||
|
name="_failure_path"
|
||||||
|
value="{{ app.request.uri }}">
|
||||||
|
|
||||||
|
<button class="submit-btn" type="submit">Se déconnecter</button>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
Loading…
Reference in a new issue