import * as plugins from '../../00plugins.js'; import * as interfaces from '../../interfaces/index.js'; import { zIndexLayers } from '../../00zindex.js'; import { DeesElement, type TemplateResult, property, customElement, html, css, cssManager, } from '@design.estate/dees-element'; import { DeesContextmenu } from '../../00group-overlay/dees-contextmenu/dees-contextmenu.js'; import { demoFunc } from './dees-appui-mainmenu.demo.js'; import { themeDefaultStyles } from '../../00theme.js'; /** * the most left menu * usually used as organization selector */ @customElement('dees-appui-mainmenu') export class DeesAppuiMainmenu extends DeesElement { public static demo = demoFunc; public static demoGroups = ['App UI']; // INSTANCE // Logo properties @property({ type: String }) accessor logoIcon: string = ''; @property({ type: String }) accessor logoText: string = ''; // Menu groups (new way) @property({ type: Array }) accessor menuGroups: interfaces.IMenuGroup[] = []; // Bottom tabs (pinned to bottom) @property({ type: Array }) accessor bottomTabs: interfaces.IMenuItem[] = []; // Legacy tabs property (for backward compatibility) @property({ type: Array }) accessor tabs: interfaces.IMenuItem[] = []; @property() accessor selectedTab!: interfaces.IMenuItem; @property({ type: Boolean, reflect: true }) accessor collapsed: boolean = false; public static styles = [ themeDefaultStyles, cssManager.defaultStyles, css` :host { --menu-width-expanded: 200px; --menu-width-collapsed: 56px; --tooltip-bg: var(--dees-color-tooltip-bg); --tooltip-fg: var(--dees-color-tooltip-fg); position: relative; display: block; height: 100%; } .mainContainer { color: var(--dees-color-text-secondary); z-index: ${zIndexLayers.fixed.appBar}; display: flex; flex-direction: column; position: relative; width: var(--menu-width-expanded); height: 100%; background: var(--dees-color-bg-secondary); user-select: none; border-right: 1px solid var(--dees-color-border-subtle); font-family: 'Geist Sans', 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; transition: width 0.25s ease; } :host([collapsed]) .mainContainer { width: var(--menu-width-collapsed); } /* Floating collapse toggle button */ .collapse-toggle { position: absolute; right: -12px; top: 24px; transform: translateY(-50%); width: 24px; height: 24px; border-radius: 50%; background: var(--dees-color-bg-primary); border: 1px solid var(--dees-color-border-strong); 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: var(--dees-color-text-muted); opacity: 0; transition: opacity 0.2s ease, background 0.15s ease; padding: 0; } .collapse-toggle:hover { background: var(--dees-color-bg-tertiary); color: var(--dees-color-text-primary); } :host(:hover) .collapse-toggle { opacity: 1; } .collapse-toggle dees-icon { font-size: 14px; } /* Logo Section */ .logoSection { display: flex; align-items: center; gap: 10px; height: 48px; padding: 0 14px; border-bottom: 1px solid var(--dees-color-border-subtle); flex-shrink: 0; box-sizing: border-box; } .logoSection .logoIcon { font-size: 22px; color: var(--dees-color-text-primary); flex-shrink: 0; } .logoSection .logoText { flex: 1; font-size: 15px; font-weight: 600; color: var(--dees-color-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: opacity 0.2s ease, width 0.25s ease; } :host([collapsed]) .logoSection { justify-content: center; padding: 0; gap: 0; } :host([collapsed]) .logoSection .logoText { display: none; } /* Middle Section (scrollable) */ .menuSection { flex: 1; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; padding: 8px 0; } .menuSection::-webkit-scrollbar { width: 6px; } .menuSection::-webkit-scrollbar-track { background: transparent; } .menuSection::-webkit-scrollbar-thumb { background: var(--dees-color-scrollbar-thumb); border-radius: 3px; } .menuSection::-webkit-scrollbar-thumb:hover { background: var(--dees-color-scrollbar-thumb-hover); } /* Menu Group */ .menuGroup { padding: 0 8px; margin-bottom: 8px; } .menuGroup:last-child { margin-bottom: 0; } .groupHeader { padding: 8px 12px 6px; font-size: 11px; font-weight: 600; color: var(--dees-color-text-muted); text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap; overflow: hidden; transition: opacity 0.2s ease, max-height 0.25s ease; max-height: 30px; } :host([collapsed]) .groupHeader { opacity: 0; max-height: 0; padding: 0; margin: 0; } .groupTabs { display: flex; flex-direction: column; gap: 2px; } :host([collapsed]) .menuGroup { padding: 0 4px; } /* Tab Item */ .tab { position: relative; display: flex; align-items: center; gap: 12px; padding: 10px 12px; font-size: 13px; font-weight: 500; border-radius: 6px; cursor: pointer; transition: all 0.15s ease; color: var(--dees-color-text-secondary); } .tab:hover { background: var(--dees-color-hover); color: ${cssManager.bdTheme('#262626', '#e5e5e5')}; } .tab:active { background: var(--dees-color-active); } .tab.selectedTab { background: var(--dees-color-active); color: var(--dees-color-text-primary); } .tab.selectedTab::before { content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 3px; height: 16px; background: var(--dees-color-text-primary); border-radius: 0 2px 2px 0; } .tab dees-icon { font-size: 18px; opacity: 0.85; flex-shrink: 0; } .tab.selectedTab dees-icon { opacity: 1; } .tab .tabLabel { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: opacity 0.2s ease, width 0.25s ease; } /* Collapsed tab styles */ :host([collapsed]) .tab { justify-content: center; padding: 10px; gap: 0; } :host([collapsed]) .tab .tabLabel { opacity: 0; width: 0; position: absolute; } :host([collapsed]) .tab.selectedTab::before { left: -4px; } /* Tooltip for collapsed state */ .tab-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); } .tab-tooltip::before { content: ''; position: absolute; left: -4px; top: 50%; transform: translateY(-50%); border: 4px solid transparent; border-right-color: var(--tooltip-bg); } :host([collapsed]) .tab:hover .tab-tooltip { opacity: 1; transition-delay: 1s; } /* Badge styles */ .badge { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 6px; font-size: 11px; font-weight: 600; border-radius: 9px; margin-left: auto; } .badge.default { background: var(--dees-color-badge-default-bg); color: var(--dees-color-badge-default-fg); } .badge.success { background: var(--dees-color-badge-success-bg); color: var(--dees-color-badge-success-fg); } .badge.warning { background: var(--dees-color-badge-warning-bg); color: var(--dees-color-badge-warning-fg); } .badge.error { background: var(--dees-color-badge-error-bg); color: var(--dees-color-badge-error-fg); } :host([collapsed]) .badge { display: none; } /* Bottom Section */ .bottomSection { flex-shrink: 0; padding: 8px; border-top: 1px solid var(--dees-color-border-subtle); display: flex; flex-direction: column; gap: 2px; } :host([collapsed]) .bottomSection { padding: 8px 4px; } `, ]; public render(): TemplateResult { // Get all tabs for selection (from groups or legacy tabs) const allTabs = this.getAllTabs(); return html`