feat(dees-appui): Add collapsible/compact mode to AppUI sidebars (mainmenu & secondarymenu) with toggles, tooltips and improved z-index stacking

This commit is contained in:
2025-12-08 15:40:12 +00:00
parent 24d3afe85d
commit 85424d07cd
5 changed files with 372 additions and 9 deletions

View File

@@ -89,11 +89,16 @@ export class DeesAppuiSecondarymenu extends DeesElement {
@state()
accessor collapsedGroups: Set<string> = new Set();
/** Horizontal collapse state */
@property({ type: Boolean, reflect: true })
accessor collapsed: boolean = false;
public static styles = [
cssManager.defaultStyles,
css`
:host {
--sidebar-width: 240px;
--sidebar-width-expanded: 240px;
--sidebar-width-collapsed: 56px;
--sidebar-bg: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
--sidebar-fg: ${cssManager.bdTheme('#525252', '#a3a3a3')};
--sidebar-fg-muted: ${cssManager.bdTheme('#737373', '#737373')};
@@ -102,6 +107,8 @@ export class DeesAppuiSecondarymenu extends DeesElement {
--sidebar-hover: ${cssManager.bdTheme('rgba(0, 0, 0, 0.04)', 'rgba(255, 255, 255, 0.06)')};
--sidebar-active: ${cssManager.bdTheme('rgba(0, 0, 0, 0.06)', 'rgba(255, 255, 255, 0.08)')};
--sidebar-accent: ${cssManager.bdTheme('#0a0a0a', '#fafafa')};
--tooltip-bg: ${cssManager.bdTheme('#18181b', '#fafafa')};
--tooltip-fg: ${cssManager.bdTheme('#fafafa', '#18181b')};
/* Badge colors */
--badge-default-bg: ${cssManager.bdTheme('#f4f4f5', '#27272a')};
@@ -115,11 +122,16 @@ export class DeesAppuiSecondarymenu extends DeesElement {
display: block;
height: 100%;
width: var(--sidebar-width);
width: var(--sidebar-width-expanded);
background: var(--sidebar-bg);
border-right: 1px solid var(--sidebar-border);
font-family: 'Geist Sans', 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
user-select: none;
transition: width 0.25s ease;
}
:host([collapsed]) {
width: var(--sidebar-width-collapsed);
}
.maincontainer {
@@ -127,6 +139,43 @@ export class DeesAppuiSecondarymenu extends DeesElement {
flex-direction: column;
height: 100%;
overflow: hidden;
position: relative;
}
/* Floating collapse toggle button */
.collapse-toggle {
position: absolute;
right: -12px;
top: 24px;
transform: translateY(-50%);
width: 24px;
height: 24px;
border-radius: 50%;
background: ${cssManager.bdTheme('#ffffff', '#27272a')};
border: 1px solid ${cssManager.bdTheme('#e5e5e5', '#3f3f46')};
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
cursor: pointer;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
color: ${cssManager.bdTheme('#737373', '#a1a1aa')};
opacity: 0;
transition: opacity 0.2s ease, background 0.15s ease;
padding: 0;
}
.collapse-toggle:hover {
background: ${cssManager.bdTheme('#f4f4f5', '#3f3f46')};
color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')};
}
:host(:hover) .collapse-toggle {
opacity: 1;
}
.collapse-toggle dees-icon {
font-size: 14px;
}
/* Header Section */
@@ -142,12 +191,25 @@ export class DeesAppuiSecondarymenu extends DeesElement {
}
.header .heading {
flex: 1;
font-size: 14px;
font-weight: 600;
color: var(--sidebar-fg-active);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: opacity 0.2s ease, width 0.25s ease;
}
:host([collapsed]) .header {
justify-content: center;
padding: 0 8px;
}
:host([collapsed]) .header .heading {
opacity: 0;
width: 0;
overflow: hidden;
}
/* Scrollable Menu Section */
@@ -181,6 +243,10 @@ export class DeesAppuiSecondarymenu extends DeesElement {
margin-bottom: 4px;
}
:host([collapsed]) .menuGroup {
padding: 0 4px;
}
.groupHeader {
display: flex;
align-items: center;
@@ -188,7 +254,8 @@ export class DeesAppuiSecondarymenu extends DeesElement {
padding: 8px 8px;
cursor: pointer;
border-radius: 6px;
transition: background 0.15s ease;
transition: background 0.15s ease, opacity 0.2s ease, max-height 0.25s ease;
max-height: 40px;
}
.groupHeader:hover {
@@ -204,6 +271,8 @@ export class DeesAppuiSecondarymenu extends DeesElement {
color: var(--sidebar-fg-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
white-space: nowrap;
overflow: hidden;
}
.groupHeader .groupTitle dees-icon {
@@ -221,6 +290,15 @@ export class DeesAppuiSecondarymenu extends DeesElement {
transform: rotate(-90deg);
}
/* Hide group headers when horizontally collapsed */
:host([collapsed]) .groupHeader {
opacity: 0;
max-height: 0;
padding: 0;
margin: 0;
pointer-events: none;
}
/* Group Items Container */
.groupItems {
overflow: hidden;
@@ -234,6 +312,12 @@ export class DeesAppuiSecondarymenu extends DeesElement {
opacity: 0;
}
/* Always show items when horizontally collapsed (regardless of group collapse state) */
:host([collapsed]) .groupItems {
max-height: none;
opacity: 1;
}
/* Menu Item */
.menuItem {
position: relative;
@@ -292,6 +376,60 @@ export class DeesAppuiSecondarymenu extends DeesElement {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: opacity 0.2s ease, width 0.25s ease;
}
/* Collapsed menu item styles */
:host([collapsed]) .menuItem {
justify-content: center;
padding: 8px;
gap: 0;
}
:host([collapsed]) .menuItem .itemLabel {
opacity: 0;
width: 0;
position: absolute;
}
:host([collapsed]) .menuItem.selected::before {
left: -4px;
}
/* Tooltip for collapsed state */
.item-tooltip {
position: absolute;
left: 100%;
top: 50%;
transform: translateY(-50%);
margin-left: 12px;
padding: 6px 12px;
background: var(--tooltip-bg);
color: var(--tooltip-fg);
border-radius: 6px;
font-size: 13px;
font-weight: 500;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity 0.15s ease;
z-index: 1000;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.item-tooltip::before {
content: '';
position: absolute;
left: -4px;
top: 50%;
transform: translateY(-50%);
border: 4px solid transparent;
border-right-color: var(--tooltip-bg);
}
:host([collapsed]) .menuItem:hover .item-tooltip {
opacity: 1;
transition-delay: 1s;
}
/* Badge Styles */
@@ -328,6 +466,10 @@ export class DeesAppuiSecondarymenu extends DeesElement {
color: var(--badge-error-fg);
}
:host([collapsed]) .badge {
display: none;
}
/* Divider */
.divider {
height: 1px;
@@ -344,6 +486,9 @@ export class DeesAppuiSecondarymenu extends DeesElement {
public render(): TemplateResult {
return html`
<button class="collapse-toggle" @click="${() => this.toggleCollapse()}">
<dees-icon .icon="${this.collapsed ? 'lucide:chevronRight' : 'lucide:chevronLeft'}"></dees-icon>
</button>
<div class="maincontainer">
<div class="header">
<span class="heading">${this.heading}</span>
@@ -392,6 +537,7 @@ export class DeesAppuiSecondarymenu extends DeesElement {
${item.badge !== undefined ? html`
<span class="badge ${item.badgeVariant || 'default'}">${item.badge}</span>
` : ''}
<span class="item-tooltip">${item.key}</span>
</div>
`;
}
@@ -424,6 +570,15 @@ export class DeesAppuiSecondarymenu extends DeesElement {
this.collapsedGroups = newCollapsed;
}
public toggleCollapse(): void {
this.collapsed = !this.collapsed;
this.dispatchEvent(new CustomEvent('collapse-change', {
detail: { collapsed: this.collapsed },
bubbles: true,
composed: true
}));
}
private selectItem(item: interfaces.ISecondaryMenuItem, group?: interfaces.ISecondaryMenuGroup): void {
this.selectedItem = item;
item.action();