feat: Add profile dropdown component and integrate with appbar for user menu

This commit is contained in:
Juergen Kunz
2025-06-17 09:55:28 +00:00
parent cd3c7c8e63
commit a8f0e5659e
6 changed files with 482 additions and 17 deletions

View File

@ -11,11 +11,13 @@ import {
import * as domtools from '@design.estate/dees-domtools';
import * as interfaces from './interfaces/index.js';
import * as plugins from './00plugins.js';
import { demoFunc } from './dees-appui-appbar.demo.js';
// Import required components
import './dees-icon.js';
import './dees-windowcontrols.js';
import './dees-appui-profiledropdown.js';
declare global {
interface HTMLElementTagNameMap {
@ -44,10 +46,14 @@ export class DeesAppuiBar extends DeesElement {
@property({ type: Object })
public user?: {
name: string;
email?: string;
avatar?: string;
status?: 'online' | 'offline' | 'busy' | 'away';
};
@property({ type: Array })
public profileMenuItems: (plugins.tsclass.website.IMenuItem & { shortcut?: string } | { divider: true })[] = [];
@property({ type: Boolean })
public showSearch: boolean = false;
@ -64,6 +70,9 @@ export class DeesAppuiBar extends DeesElement {
@state()
private focusedDropdownItem: number = -1;
@state()
private isProfileDropdownOpen: boolean = false;
public static styles = [
cssManager.defaultStyles,
css`
@ -102,7 +111,7 @@ export class DeesAppuiBar extends DeesElement {
border-radius: 4px;
-webkit-app-region: no-drag;
transition: all 0.2s ease;
cursor: pointer;
cursor: default;
outline: none;
display: flex;
align-items: center;
@ -162,7 +171,7 @@ export class DeesAppuiBar extends DeesElement {
.dropdown-item {
padding: 8px 16px;
cursor: pointer;
cursor: default;
display: flex;
align-items: center;
gap: 8px;
@ -206,7 +215,7 @@ export class DeesAppuiBar extends DeesElement {
.breadcrumb-item {
color: ${cssManager.bdTheme('#00000080', '#ffffff80')};
cursor: pointer;
cursor: default;
transition: color 0.2s;
}
@ -229,7 +238,7 @@ export class DeesAppuiBar extends DeesElement {
}
.search-icon {
cursor: pointer;
cursor: default;
opacity: 0.7;
transition: opacity 0.2s;
}
@ -242,7 +251,7 @@ export class DeesAppuiBar extends DeesElement {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
cursor: default;
padding: 4px 8px;
border-radius: 4px;
transition: background 0.2s;
@ -413,22 +422,31 @@ export class DeesAppuiBar extends DeesElement {
${this.showSearch ? html`
<dees-icon
class="search-icon"
.iconName=${'search'}
.icon=${'lucide:search'}
@click=${this.handleSearchClick}
></dees-icon>
` : ''}
${this.user ? html`
<div class="user-info" @click=${this.handleUserClick}>
<div class="user-avatar">
${this.user.avatar ?
html`<img src="${this.user.avatar}" alt="${this.user.name}">` :
html`${this.user.name.charAt(0).toUpperCase()}`
}
${this.user.status ? html`
<div class="user-status ${this.user.status}"></div>
` : ''}
<div style="position: relative;">
<div class="user-info" @click=${this.handleUserClick}>
<div class="user-avatar">
${this.user.avatar ?
html`<img src="${this.user.avatar}" alt="${this.user.name}">` :
html`${this.user.name.charAt(0).toUpperCase()}`
}
${this.user.status ? html`
<div class="user-status ${this.user.status}"></div>
` : ''}
</div>
<span>${this.user.name}</span>
</div>
<span>${this.user.name}</span>
<dees-appui-profiledropdown
.user=${this.user}
.menuItems=${this.profileMenuItems}
.isOpen=${this.isProfileDropdownOpen}
.position=${'top-right'}
@menu-select=${(e: CustomEvent) => this.handleProfileMenuSelect(e)}
></dees-appui-profiledropdown>
</div>
` : ''}
`;
@ -536,12 +554,26 @@ export class DeesAppuiBar extends DeesElement {
}
private handleUserClick() {
this.isProfileDropdownOpen = !this.isProfileDropdownOpen;
// Also emit the event for backward compatibility
this.dispatchEvent(new CustomEvent('user-menu-open', {
bubbles: true,
composed: true
}));
}
private handleProfileMenuSelect(e: CustomEvent) {
this.isProfileDropdownOpen = false;
// Re-emit the event
this.dispatchEvent(new CustomEvent('profile-menu-select', {
detail: e.detail,
bubbles: true,
composed: true
}));
}
// Lifecycle
async connectedCallback() {
await super.connectedCallback();
@ -564,6 +596,7 @@ export class DeesAppuiBar extends DeesElement {
// Close all dropdowns when clicking outside
this.activeMenu = null;
this.focusedDropdownItem = -1;
// Note: Profile dropdown handles its own outside clicks
}
private handleDropdownKeydown(e: KeyboardEvent, items: interfaces.IAppBarMenuItem[], _parentId: string) {