add profile menu
This commit is contained in:
@@ -391,7 +391,7 @@ app-shell main {
|
|||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 45px;
|
||||||
right: 0;
|
right: 0;
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
background-color: var(--color-bg);
|
background-color: var(--color-bg);
|
||||||
@@ -413,12 +413,20 @@ app-shell main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
display: block;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-sm);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: var(--space-sm) var(--space-md);
|
padding: var(--space-sm) var(--space-md);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item:hover {
|
.dropdown-item:hover {
|
||||||
@@ -429,3 +437,32 @@ app-shell main {
|
|||||||
background-color: var(--color-primary-light);
|
background-color: var(--color-primary-light);
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-item svg {
|
||||||
|
flex-shrink: 0;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item-danger {
|
||||||
|
color: var(--color-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item-danger:hover {
|
||||||
|
background-color: var(--color-error);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item-danger:hover svg {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-divider {
|
||||||
|
height: 1px;
|
||||||
|
background-color: var(--color-border);
|
||||||
|
margin: var(--space-xs) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu-right {
|
||||||
|
right: 0;
|
||||||
|
left: auto;
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ class AppHeader extends HTMLElement {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
this.langDropdownOpen = false
|
this.langDropdownOpen = false
|
||||||
|
this.profileDropdownOpen = false
|
||||||
this.handleOutsideClick = this.handleOutsideClick.bind(this)
|
this.handleOutsideClick = this.handleOutsideClick.bind(this)
|
||||||
this.handleKeydown = this.handleKeydown.bind(this)
|
this.handleKeydown = this.handleKeydown.bind(this)
|
||||||
this.handleScroll = this.handleScroll.bind(this)
|
this.handleScroll = this.handleScroll.bind(this)
|
||||||
@@ -38,7 +39,10 @@ class AppHeader extends HTMLElement {
|
|||||||
|
|
||||||
handleOutsideClick() {
|
handleOutsideClick() {
|
||||||
if (this.langDropdownOpen) {
|
if (this.langDropdownOpen) {
|
||||||
this.closeDropdown()
|
this.closeLangDropdown()
|
||||||
|
}
|
||||||
|
if (this.profileDropdownOpen) {
|
||||||
|
this.closeProfileDropdown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +55,7 @@ class AppHeader extends HTMLElement {
|
|||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case 'Escape':
|
case 'Escape':
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.closeDropdown()
|
this.closeLangDropdown()
|
||||||
this.querySelector('#lang-toggle')?.focus()
|
this.querySelector('#lang-toggle')?.focus()
|
||||||
break
|
break
|
||||||
case 'ArrowDown':
|
case 'ArrowDown':
|
||||||
@@ -67,7 +71,7 @@ class AppHeader extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
closeDropdown() {
|
closeLangDropdown() {
|
||||||
this.langDropdownOpen = false
|
this.langDropdownOpen = false
|
||||||
const langDropdown = this.querySelector('#lang-dropdown')
|
const langDropdown = this.querySelector('#lang-dropdown')
|
||||||
const langToggle = this.querySelector('#lang-toggle')
|
const langToggle = this.querySelector('#lang-toggle')
|
||||||
@@ -75,13 +79,29 @@ class AppHeader extends HTMLElement {
|
|||||||
langToggle?.setAttribute('aria-expanded', 'false')
|
langToggle?.setAttribute('aria-expanded', 'false')
|
||||||
}
|
}
|
||||||
|
|
||||||
openDropdown() {
|
openLangDropdown() {
|
||||||
this.langDropdownOpen = true
|
this.langDropdownOpen = true
|
||||||
const langDropdown = this.querySelector('#lang-dropdown')
|
const langDropdown = this.querySelector('#lang-dropdown')
|
||||||
const langToggle = this.querySelector('#lang-toggle')
|
const langToggle = this.querySelector('#lang-toggle')
|
||||||
langDropdown?.classList.add('open')
|
langDropdown?.classList.add('open')
|
||||||
langToggle?.setAttribute('aria-expanded', 'true')
|
langToggle?.setAttribute('aria-expanded', 'true')
|
||||||
this.querySelector('.dropdown-item.active')?.focus()
|
this.querySelector('#lang-dropdown .dropdown-item.active')?.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
closeProfileDropdown() {
|
||||||
|
this.profileDropdownOpen = false
|
||||||
|
const dropdown = this.querySelector('#profile-dropdown')
|
||||||
|
const toggle = this.querySelector('#profile-toggle')
|
||||||
|
dropdown?.classList.remove('open')
|
||||||
|
toggle?.setAttribute('aria-expanded', 'false')
|
||||||
|
}
|
||||||
|
|
||||||
|
openProfileDropdown() {
|
||||||
|
this.profileDropdownOpen = true
|
||||||
|
const dropdown = this.querySelector('#profile-dropdown')
|
||||||
|
const toggle = this.querySelector('#profile-toggle')
|
||||||
|
dropdown?.classList.add('open')
|
||||||
|
toggle?.setAttribute('aria-expanded', 'true')
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -151,13 +171,60 @@ class AppHeader extends HTMLElement {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
${auth.isLoggedIn() ? `
|
${auth.isLoggedIn() ? `
|
||||||
<button class="btn btn-outline btn-profile" id="profile-btn" title="${t('header.profile')}">
|
<div class="dropdown" id="profile-dropdown">
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<button
|
||||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
class="btn btn-icon btn-outline"
|
||||||
<circle cx="12" cy="7" r="4"></circle>
|
id="profile-toggle"
|
||||||
</svg>
|
aria-haspopup="menu"
|
||||||
<span class="btn-profile-text">${t('header.profile')}</span>
|
aria-expanded="${this.profileDropdownOpen}"
|
||||||
</button>
|
aria-label="${t('header.profile')}"
|
||||||
|
title="${t('header.profile')}"
|
||||||
|
>
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||||
|
<circle cx="12" cy="7" r="4"></circle>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-menu dropdown-menu-right" role="menu">
|
||||||
|
<a href="#/my-listings" class="dropdown-item" role="menuitem">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<rect x="3" y="3" width="7" height="7"></rect>
|
||||||
|
<rect x="14" y="3" width="7" height="7"></rect>
|
||||||
|
<rect x="14" y="14" width="7" height="7"></rect>
|
||||||
|
<rect x="3" y="14" width="7" height="7"></rect>
|
||||||
|
</svg>
|
||||||
|
${t('profile.myListings')}
|
||||||
|
</a>
|
||||||
|
<a href="#/messages" class="dropdown-item" role="menuitem">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
||||||
|
</svg>
|
||||||
|
${t('profile.messages')}
|
||||||
|
</a>
|
||||||
|
<a href="#/favorites" class="dropdown-item" role="menuitem">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
|
||||||
|
</svg>
|
||||||
|
${t('profile.favorites')}
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<a href="#/settings" class="dropdown-item" role="menuitem">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<circle cx="12" cy="12" r="3"></circle>
|
||||||
|
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
|
||||||
|
</svg>
|
||||||
|
${t('profile.settings')}
|
||||||
|
</a>
|
||||||
|
<button class="dropdown-item dropdown-item-danger" id="logout-btn" role="menuitem">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
|
||||||
|
<polyline points="16 17 21 12 16 7"></polyline>
|
||||||
|
<line x1="21" y1="12" x2="9" y2="12"></line>
|
||||||
|
</svg>
|
||||||
|
${t('auth.logout')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
` : `
|
` : `
|
||||||
<button class="btn btn-outline btn-login" id="login-btn" title="${t('auth.login')}">
|
<button class="btn btn-outline btn-login" id="login-btn" title="${t('auth.login')}">
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
@@ -182,9 +249,9 @@ class AppHeader extends HTMLElement {
|
|||||||
langToggle.addEventListener('click', (e) => {
|
langToggle.addEventListener('click', (e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (this.langDropdownOpen) {
|
if (this.langDropdownOpen) {
|
||||||
this.closeDropdown()
|
this.closeLangDropdown()
|
||||||
} else {
|
} else {
|
||||||
this.openDropdown()
|
this.openLangDropdown()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -204,10 +271,31 @@ class AppHeader extends HTMLElement {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Profile button
|
// Profile dropdown
|
||||||
const profileBtn = this.querySelector('#profile-btn')
|
const profileDropdown = this.querySelector('#profile-dropdown')
|
||||||
profileBtn?.addEventListener('click', () => {
|
const profileToggle = this.querySelector('#profile-toggle')
|
||||||
router.navigate('/profile')
|
|
||||||
|
profileToggle?.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
if (this.profileDropdownOpen) {
|
||||||
|
this.closeProfileDropdown()
|
||||||
|
} else {
|
||||||
|
this.openProfileDropdown()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Close dropdown when clicking menu items
|
||||||
|
profileDropdown?.querySelectorAll('.dropdown-item').forEach(item => {
|
||||||
|
item.addEventListener('click', () => {
|
||||||
|
this.closeProfileDropdown()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Logout button
|
||||||
|
const logoutBtn = this.querySelector('#logout-btn')
|
||||||
|
logoutBtn?.addEventListener('click', async () => {
|
||||||
|
await auth.logout()
|
||||||
|
router.navigate('/')
|
||||||
})
|
})
|
||||||
|
|
||||||
this.updateThemeIcon()
|
this.updateThemeIcon()
|
||||||
|
|||||||
@@ -180,6 +180,12 @@
|
|||||||
"attempts": "Versuche",
|
"attempts": "Versuche",
|
||||||
"error": "Fehler - erneut versuchen"
|
"error": "Fehler - erneut versuchen"
|
||||||
},
|
},
|
||||||
|
"profile": {
|
||||||
|
"myListings": "Meine Anzeigen",
|
||||||
|
"messages": "Nachrichten",
|
||||||
|
"favorites": "Favoriten",
|
||||||
|
"settings": "Einstellungen"
|
||||||
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"login": "Anmelden",
|
"login": "Anmelden",
|
||||||
"logout": "Abmelden",
|
"logout": "Abmelden",
|
||||||
|
|||||||
@@ -180,6 +180,12 @@
|
|||||||
"attempts": "attempts",
|
"attempts": "attempts",
|
||||||
"error": "Error - try again"
|
"error": "Error - try again"
|
||||||
},
|
},
|
||||||
|
"profile": {
|
||||||
|
"myListings": "My Listings",
|
||||||
|
"messages": "Messages",
|
||||||
|
"favorites": "Favorites",
|
||||||
|
"settings": "Settings"
|
||||||
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
|
|||||||
@@ -180,6 +180,12 @@
|
|||||||
"attempts": "tentatives",
|
"attempts": "tentatives",
|
||||||
"error": "Erreur - réessayer"
|
"error": "Erreur - réessayer"
|
||||||
},
|
},
|
||||||
|
"profile": {
|
||||||
|
"myListings": "Mes annonces",
|
||||||
|
"messages": "Messages",
|
||||||
|
"favorites": "Favoris",
|
||||||
|
"settings": "Paramètres"
|
||||||
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"login": "Connexion",
|
"login": "Connexion",
|
||||||
"logout": "Déconnexion",
|
"logout": "Déconnexion",
|
||||||
|
|||||||
Reference in New Issue
Block a user