diff --git a/.playwright-mcp/amber-gold-headers.png b/.playwright-mcp/amber-gold-headers.png new file mode 100644 index 0000000..beda94f Binary files /dev/null and b/.playwright-mcp/amber-gold-headers.png differ diff --git a/.playwright-mcp/amber-gold-hover.png b/.playwright-mcp/amber-gold-hover.png new file mode 100644 index 0000000..40dc3e3 Binary files /dev/null and b/.playwright-mcp/amber-gold-hover.png differ diff --git a/.playwright-mcp/filter-after-click.png b/.playwright-mcp/filter-after-click.png new file mode 100644 index 0000000..3b3031e Binary files /dev/null and b/.playwright-mcp/filter-after-click.png differ diff --git a/.playwright-mcp/filter-after-fix.png b/.playwright-mcp/filter-after-fix.png new file mode 100644 index 0000000..4c7fda0 Binary files /dev/null and b/.playwright-mcp/filter-after-fix.png differ diff --git a/.playwright-mcp/filter-before-click.png b/.playwright-mcp/filter-before-click.png new file mode 100644 index 0000000..7c243d9 Binary files /dev/null and b/.playwright-mcp/filter-before-click.png differ diff --git a/.playwright-mcp/muted-grey-headers.png b/.playwright-mcp/muted-grey-headers.png new file mode 100644 index 0000000..7bb85e7 Binary files /dev/null and b/.playwright-mcp/muted-grey-headers.png differ diff --git a/.playwright-mcp/muted-grey-hover.png b/.playwright-mcp/muted-grey-hover.png new file mode 100644 index 0000000..b93b885 Binary files /dev/null and b/.playwright-mcp/muted-grey-hover.png differ diff --git a/.playwright-mcp/orange-group-headers.png b/.playwright-mcp/orange-group-headers.png new file mode 100644 index 0000000..24cdc8c Binary files /dev/null and b/.playwright-mcp/orange-group-headers.png differ diff --git a/.playwright-mcp/rose-group-headers.png b/.playwright-mcp/rose-group-headers.png new file mode 100644 index 0000000..4790317 Binary files /dev/null and b/.playwright-mcp/rose-group-headers.png differ diff --git a/.playwright-mcp/rose-hover-effect.png b/.playwright-mcp/rose-hover-effect.png new file mode 100644 index 0000000..7740579 Binary files /dev/null and b/.playwright-mcp/rose-hover-effect.png differ diff --git a/.playwright-mcp/subtle-warm-grey.png b/.playwright-mcp/subtle-warm-grey.png new file mode 100644 index 0000000..c654cbb Binary files /dev/null and b/.playwright-mcp/subtle-warm-grey.png differ diff --git a/.playwright-mcp/teal-group-headers.png b/.playwright-mcp/teal-group-headers.png new file mode 100644 index 0000000..0e88c38 Binary files /dev/null and b/.playwright-mcp/teal-group-headers.png differ diff --git a/.playwright-mcp/teal-hover-effect.png b/.playwright-mcp/teal-hover-effect.png new file mode 100644 index 0000000..66d80be Binary files /dev/null and b/.playwright-mcp/teal-hover-effect.png differ diff --git a/ts_web/elements/00group-appui/dees-appui-activitylog/dees-appui-activitylog.ts b/ts_web/elements/00group-appui/dees-appui-activitylog/dees-appui-activitylog.ts index 0e4e679..793fa9c 100644 --- a/ts_web/elements/00group-appui/dees-appui-activitylog/dees-appui-activitylog.ts +++ b/ts_web/elements/00group-appui/dees-appui-activitylog/dees-appui-activitylog.ts @@ -39,23 +39,28 @@ export class DeesAppuiActivitylog extends DeesElement implements IActivityLogAPI themeDefaultStyles, cssManager.defaultStyles, css` - /* TODO: Migrate hardcoded values to --dees-* CSS variables */ :host { - color: ${cssManager.bdTheme('#09090b', '#fafafa')}; + /* CSS Variables aligned with secondary menu */ + --activitylog-bg: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; + --activitylog-fg: ${cssManager.bdTheme('#525252', '#a3a3a3')}; + --activitylog-fg-muted: ${cssManager.bdTheme('#737373', '#737373')}; + --activitylog-fg-active: ${cssManager.bdTheme('#0a0a0a', '#fafafa')}; + --activitylog-border: ${cssManager.bdTheme('#e5e5e5', '#1a1a1a')}; + --activitylog-hover: ${cssManager.bdTheme('rgba(0, 0, 0, 0.04)', 'rgba(255, 255, 255, 0.06)')}; + --activitylog-accent: ${cssManager.bdTheme('#78716c', '#b5a99a')}; + + color: var(--activitylog-fg); position: relative; display: block; width: 100%; max-width: 320px; height: 100%; - background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; - font-family: 'Geist Mono', monospace; - border-left: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; + background: var(--activitylog-bg); + font-family: 'Geist Sans', -apple-system, BlinkMacSystemFont, sans-serif; + border-left: 1px solid var(--activitylog-border); cursor: default; - box-shadow: ${cssManager.bdTheme( - '-4px 0 12px rgba(0, 0, 0, 0.02)', - '-4px 0 12px rgba(0, 0, 0, 0.2)' - )}; } + .maincontainer { position: absolute; top: 0px; @@ -64,35 +69,61 @@ export class DeesAppuiActivitylog extends DeesElement implements IActivityLogAPI width: 100%; } + /* Header with streaming indicator */ .topbar { position: absolute; top: 0px; height: 48px; width: 100%; - padding: 0px 16px; - background: ${cssManager.bdTheme('#ffffff', '#09090b')}; - border-bottom: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; + padding: 0px 12px; + background: var(--activitylog-bg); + border-bottom: 1px solid var(--activitylog-border); display: flex; align-items: center; + justify-content: space-between; box-sizing: border-box; } .topbar .heading { font-weight: 600; font-size: 14px; - font-family: 'Geist Sans', sans-serif; - color: ${cssManager.bdTheme('#09090b', '#fafafa')}; + color: var(--activitylog-fg-active); } + .live-indicator { + display: flex; + align-items: center; + gap: 6px; + font-size: 10px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--activitylog-fg-muted); + } + + .live-indicator .dot { + width: 6px; + height: 6px; + background: ${cssManager.bdTheme('#22c55e', '#22c55e')}; + border-radius: 50%; + animation: pulse 2s ease-in-out infinite; + } + + @keyframes pulse { + 0%, 100% { opacity: 0.5; transform: scale(0.9); } + 50% { opacity: 1; transform: scale(1.1); } + } + + /* Activity container */ .activityContainer { position: absolute; top: 48px; bottom: 48px; width: 100%; - padding: 12px 0px; + padding: 8px 0; overflow-y: auto; scrollbar-width: thin; - scrollbar-color: ${cssManager.bdTheme('#e5e7eb', '#27272a')} transparent; + scrollbar-color: ${cssManager.bdTheme('#d4d4d4', '#333333')} transparent; } .activityContainer::-webkit-scrollbar { @@ -104,82 +135,53 @@ export class DeesAppuiActivitylog extends DeesElement implements IActivityLogAPI } .activityContainer::-webkit-scrollbar-thumb { - background: ${cssManager.bdTheme('#e5e7eb', '#27272a')}; + background: ${cssManager.bdTheme('#d4d4d4', '#333333')}; border-radius: 3px; } .activityContainer::-webkit-scrollbar-thumb:hover { - background: ${cssManager.bdTheme('#d4d4d8', '#3f3f46')}; + background: ${cssManager.bdTheme('#a3a3a3', '#525252')}; } .empty-state { font-size: 13px; text-align: center; - padding: 32px 16px; - color: ${cssManager.bdTheme('#71717a', '#71717a')}; - font-family: 'Geist Sans', sans-serif; - } - - .streamingIndicator { - font-size: 11px; - text-align: center; - padding: 16px; - color: ${cssManager.bdTheme('#71717a', '#71717a')}; - font-family: 'Geist Sans', sans-serif; - text-transform: uppercase; - letter-spacing: 0.05em; - font-weight: 500; - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - } - - .streamingIndicator::before { - content: ''; - width: 6px; - height: 6px; - background: ${cssManager.bdTheme('#3b82f6', '#3b82f6')}; - border-radius: 50%; - animation: pulse 2s ease-in-out infinite; - } - - @keyframes pulse { - 0%, 100% { opacity: 0.4; transform: scale(0.8); } - 50% { opacity: 1; transform: scale(1.2); } + padding: 40px 16px; + color: var(--activitylog-fg-muted); } + /* Date separator - warm taupe styling */ .date-separator { - padding: 12px 16px 8px; - font-size: 11px; + padding: 12px 12px 6px; + font-size: 10px; font-weight: 600; text-transform: uppercase; - letter-spacing: 0.05em; - color: ${cssManager.bdTheme('#71717a', '#71717a')}; - background: ${cssManager.bdTheme('#f9fafb', '#09090b')}; - border-bottom: 1px solid ${cssManager.bdTheme('#f4f4f5', '#18181b')}; + letter-spacing: 0.5px; + color: var(--activitylog-accent); position: sticky; top: 0; z-index: 1; + background: var(--activitylog-bg); } + /* Activity entry - modern stacked layout */ .activityentry { - min-height: 36px; - font-size: 13px; - padding: 10px 16px; - border-bottom: 1px solid ${cssManager.bdTheme('#f4f4f5', '#18181b')}; - transition: all 0.15s ease; + font-size: 12px; + padding: 8px 12px; + margin: 2px 4px; + border-radius: 6px; + transition: background 0.15s ease; display: flex; - align-items: center; - gap: 8px; + align-items: flex-start; + gap: 10px; line-height: 1.4; - animation: fadeIn 0.3s ease-out; + animation: fadeIn 0.2s ease-out; } @keyframes fadeIn { from { opacity: 0; - transform: translateY(-4px); + transform: translateY(-2px); } to { opacity: 1; @@ -187,88 +189,109 @@ export class DeesAppuiActivitylog extends DeesElement implements IActivityLogAPI } } - .activityentry:last-of-type { - border-bottom: none; - } - .activityentry:hover { - background: ${cssManager.bdTheme('#f4f4f5', '#18181b')}; - } - - .timestamp { - color: ${cssManager.bdTheme('#71717a', '#71717a')}; - font-weight: 500; - font-size: 12px; - font-variant-numeric: tabular-nums; - flex-shrink: 0; - min-width: 45px; + background: var(--activitylog-hover); } .activity-icon { width: 28px; height: 28px; border-radius: 6px; - background: ${cssManager.bdTheme('#f4f4f5', '#18181b')}; + background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.04)', 'rgba(255, 255, 255, 0.06)')}; display: flex; align-items: center; justify-content: center; flex-shrink: 0; - font-size: 14px; + font-size: 13px; + color: var(--activitylog-fg-muted); + margin-top: 1px; } .activity-icon.login { - background: ${cssManager.bdTheme('rgba(34, 197, 94, 0.1)', 'rgba(34, 197, 94, 0.1)')}; - color: ${cssManager.bdTheme('#16a34a', '#22c55e')}; + background: ${cssManager.bdTheme('rgba(34, 197, 94, 0.08)', 'rgba(34, 197, 94, 0.12)')}; + color: ${cssManager.bdTheme('#16a34a', '#4ade80')}; } .activity-icon.logout { - background: ${cssManager.bdTheme('rgba(239, 68, 68, 0.1)', 'rgba(239, 68, 68, 0.1)')}; - color: ${cssManager.bdTheme('#dc2626', '#ef4444')}; + background: ${cssManager.bdTheme('rgba(239, 68, 68, 0.08)', 'rgba(239, 68, 68, 0.12)')}; + color: ${cssManager.bdTheme('#dc2626', '#f87171')}; } .activity-icon.view { - background: ${cssManager.bdTheme('rgba(59, 130, 246, 0.1)', 'rgba(59, 130, 246, 0.1)')}; - color: ${cssManager.bdTheme('#2563eb', '#3b82f6')}; + background: ${cssManager.bdTheme('rgba(59, 130, 246, 0.08)', 'rgba(59, 130, 246, 0.12)')}; + color: ${cssManager.bdTheme('#2563eb', '#60a5fa')}; } .activity-icon.create { - background: ${cssManager.bdTheme('rgba(168, 85, 247, 0.1)', 'rgba(168, 85, 247, 0.1)')}; - color: ${cssManager.bdTheme('#9333ea', '#a855f7')}; + background: ${cssManager.bdTheme('rgba(168, 85, 247, 0.08)', 'rgba(168, 85, 247, 0.12)')}; + color: ${cssManager.bdTheme('#9333ea', '#c084fc')}; } .activity-icon.update { - background: ${cssManager.bdTheme('rgba(251, 146, 60, 0.1)', 'rgba(251, 146, 60, 0.1)')}; + background: ${cssManager.bdTheme('rgba(251, 146, 60, 0.08)', 'rgba(251, 146, 60, 0.12)')}; color: ${cssManager.bdTheme('#ea580c', '#fb923c')}; } .activity-icon.delete { - background: ${cssManager.bdTheme('rgba(239, 68, 68, 0.1)', 'rgba(239, 68, 68, 0.1)')}; - color: ${cssManager.bdTheme('#dc2626', '#ef4444')}; + background: ${cssManager.bdTheme('rgba(239, 68, 68, 0.08)', 'rgba(239, 68, 68, 0.12)')}; + color: ${cssManager.bdTheme('#dc2626', '#f87171')}; } .activity-icon.custom { - background: ${cssManager.bdTheme('rgba(100, 116, 139, 0.1)', 'rgba(100, 116, 139, 0.1)')}; + background: ${cssManager.bdTheme('rgba(100, 116, 139, 0.08)', 'rgba(100, 116, 139, 0.12)')}; color: ${cssManager.bdTheme('#475569', '#94a3b8')}; } - .activity-text { + .activity-content { flex: 1; - color: ${cssManager.bdTheme('#18181b', '#e4e4e7')}; + min-width: 0; + display: flex; + flex-direction: column; + gap: 2px; + } + + .activity-header { + display: flex; + align-items: center; + gap: 6px; } .activity-user { font-weight: 600; - color: ${cssManager.bdTheme('#09090b', '#fafafa')}; + font-size: 12px; + color: var(--activitylog-fg-active); } + .activity-separator { + color: var(--activitylog-fg-muted); + font-size: 10px; + } + + .timestamp { + color: var(--activitylog-fg-muted); + font-weight: 400; + font-size: 11px; + font-variant-numeric: tabular-nums; + font-family: 'Geist Mono', monospace; + } + + .activity-message { + color: var(--activitylog-fg); + font-size: 12px; + line-height: 1.5; + word-break: break-word; + } + + /* Search box - refined styling */ .searchbox { position: absolute; bottom: 0px; width: 100%; height: 48px; - background: ${cssManager.bdTheme('#ffffff', '#09090b')}; - border-top: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; - padding: 8px; + background: var(--activitylog-bg); + border-top: 1px solid var(--activitylog-border); + padding: 8px 12px; + box-sizing: border-box; } .search-wrapper { @@ -282,64 +305,37 @@ export class DeesAppuiActivitylog extends DeesElement implements IActivityLogAPI left: 10px; top: 50%; transform: translateY(-50%); - color: ${cssManager.bdTheme('#71717a', '#71717a')}; - font-size: 14px; + color: var(--activitylog-fg-muted); + font-size: 13px; pointer-events: none; transition: color 0.15s ease; } .searchbox input { - color: ${cssManager.bdTheme('#09090b', '#fafafa')}; - background: ${cssManager.bdTheme('#f4f4f5', '#18181b')}; + color: var(--activitylog-fg-active); + background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.03)', 'rgba(255, 255, 255, 0.04)')}; width: 100%; height: 100%; - border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; + border: 1px solid ${cssManager.bdTheme('rgba(0, 0, 0, 0.08)', 'rgba(255, 255, 255, 0.08)')}; border-radius: 6px; - padding: 0 12px 0 36px; + padding: 0 12px 0 34px; font-family: 'Geist Sans', sans-serif; - font-size: 13px; + font-size: 12px; transition: all 0.15s ease; } .searchbox input::placeholder { - color: ${cssManager.bdTheme('#71717a', '#71717a')}; + color: var(--activitylog-fg-muted); } .searchbox input:focus { outline: none; - border-color: ${cssManager.bdTheme('#3b82f6', '#3b82f6')}; - box-shadow: 0 0 0 3px ${cssManager.bdTheme('rgba(59, 130, 246, 0.1)', 'rgba(59, 130, 246, 0.1)')}; + border-color: ${cssManager.bdTheme('rgba(0, 0, 0, 0.15)', 'rgba(255, 255, 255, 0.15)')}; + background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.02)', 'rgba(255, 255, 255, 0.06)')}; } - .searchbox input:focus ~ .search-icon, .search-wrapper:has(input:focus) .search-icon { - color: ${cssManager.bdTheme('#3b82f6', '#3b82f6')}; - } - - .bottomShadow { - position: absolute; - width: 100%; - height: 24px; - bottom: 48px; - background: ${cssManager.bdTheme( - 'linear-gradient(180deg, transparent 0%, #fafafa 100%)', - 'linear-gradient(180deg, transparent 0%, #0a0a0a 100%)' - )}; - pointer-events: none; - opacity: 0.8; - } - - .topShadow { - position: absolute; - width: 100%; - height: 24px; - top: 48px; - background: ${cssManager.bdTheme( - 'linear-gradient(0deg, transparent 0%, #fafafa 100%)', - 'linear-gradient(0deg, transparent 0%, #0a0a0a 100%)' - )}; - pointer-events: none; - opacity: 0.8; + color: var(--activitylog-fg); } `, ]; @@ -355,12 +351,11 @@ export class DeesAppuiActivitylog extends DeesElement implements IActivityLogAPI
Activity Log
+ ${filteredEntries.length > 0 + ? html`
Live
` + : ''}
- ${filteredEntries.length > 0 - ? html`
Live Updates
` - : ''} - ${filteredEntries.length === 0 ? html`
No activity entries
` : groupedEntries.map( @@ -381,8 +376,6 @@ export class DeesAppuiActivitylog extends DeesElement implements IActivityLogAPI />
-
-
`; } @@ -397,12 +390,16 @@ export class DeesAppuiActivitylog extends DeesElement implements IActivityLogAPI class="activityentry" @contextmenu=${(e: MouseEvent) => this.handleContextMenu(e, entry)} > - ${timeStr}
-
- ${entry.user} ${entry.message} +
+
+ ${entry.user} + ยท + ${timeStr} +
+
${entry.message}
`; diff --git a/ts_web/elements/00group-appui/dees-appui-appbar/component.ts b/ts_web/elements/00group-appui/dees-appui-appbar/component.ts index b4ce173..3d024e3 100644 --- a/ts_web/elements/00group-appui/dees-appui-appbar/component.ts +++ b/ts_web/elements/00group-appui/dees-appui-appbar/component.ts @@ -57,6 +57,16 @@ export class DeesAppuiBar extends DeesElement { @property({ type: Boolean }) accessor showSearch: boolean = false; + // Activity log toggle + @property({ type: Boolean }) + accessor showActivityLogToggle: boolean = false; + + @property({ type: Number }) + accessor activityLogCount: number = 0; + + @property({ type: Boolean }) + accessor activityLogActive: boolean = false; + // STATE @state() accessor activeMenu: string | null = null; @@ -177,8 +187,8 @@ export class DeesAppuiBar extends DeesElement { public renderAccountSection(): TemplateResult { return html` ${this.showSearch ? html` - @@ -206,6 +216,18 @@ export class DeesAppuiBar extends DeesElement { > ` : ''} + ${this.showActivityLogToggle ? html` +
+ + ${this.activityLogCount > 0 ? html` + ${this.activityLogCount > 99 ? '99+' : this.activityLogCount} + ` : ''} +
+ ` : ''} `; } @@ -304,9 +326,16 @@ export class DeesAppuiBar extends DeesElement { } private handleSearchClick() { - this.dispatchEvent(new CustomEvent('search-click', { + this.dispatchEvent(new CustomEvent('search-click', { bubbles: true, - composed: true + composed: true + })); + } + + private handleActivityToggle() { + this.dispatchEvent(new CustomEvent('activity-toggle', { + bubbles: true, + composed: true })); } diff --git a/ts_web/elements/00group-appui/dees-appui-appbar/styles.ts b/ts_web/elements/00group-appui/dees-appui-appbar/styles.ts index 790b190..c157b74 100644 --- a/ts_web/elements/00group-appui/dees-appui-appbar/styles.ts +++ b/ts_web/elements/00group-appui/dees-appui-appbar/styles.ts @@ -17,7 +17,7 @@ export const appuiAppbarStyles = [ color: ${cssManager.bdTheme('#00000080', '#ffffff80')}; font-size: var(--appbar-font-size); display: grid; - grid-template-columns: ${cssManager.cssGridColumns(3, 20)}; + grid-template-columns: auto 1fr auto; -webkit-app-region: drag; user-select: none; } @@ -233,6 +233,54 @@ export const appuiAppbarStyles = [ .user-status.away { background: #ff9800; } + + /* Activity log toggle button */ + .activity-toggle { + display: flex; + align-items: center; + gap: 2px; + height: 28px; + padding: 0 8px; + border-radius: 6px; + cursor: default; + -webkit-app-region: no-drag; + color: ${cssManager.bdTheme('#00000060', '#ffffff60')}; + border: 1px solid ${cssManager.bdTheme('rgba(0, 0, 0, 0.1)', 'rgba(255, 255, 255, 0.1)')}; + transition: all 0.15s ease; + } + + .activity-toggle:hover { + background: ${cssManager.bdTheme('#00000010', '#ffffff15')}; + color: ${cssManager.bdTheme('#000000', '#ffffff')}; + border-color: transparent; + } + + .activity-toggle.active { + background: ${cssManager.bdTheme('#00000015', '#ffffff20')}; + color: ${cssManager.bdTheme('#000000', '#ffffff')}; + border-color: transparent; + } + + .activity-toggle dees-icon { + font-size: 14px; + } + + .activity-badge { + position: relative; + margin-left: 4px; + min-width: 16px; + height: 16px; + padding: 0 4px; + background: ${cssManager.bdTheme('#525252', '#525252')}; + color: #fafafa; + font-size: 10px; + font-weight: 600; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + } `, ]; diff --git a/ts_web/elements/00group-appui/dees-appui-secondarymenu/dees-appui-secondarymenu.demo.ts b/ts_web/elements/00group-appui/dees-appui-secondarymenu/dees-appui-secondarymenu.demo.ts index 8206ddc..41c8b46 100644 --- a/ts_web/elements/00group-appui/dees-appui-secondarymenu/dees-appui-secondarymenu.demo.ts +++ b/ts_web/elements/00group-appui/dees-appui-secondarymenu/dees-appui-secondarymenu.demo.ts @@ -12,41 +12,102 @@ export const demoFunc = () => html` .demo-secondarymenu-container .spacer { flex: 1; background: #0f0f0f; + padding: 20px; + color: #a3a3a3; + font-family: 'Geist Sans', sans-serif; + } + .demo-secondarymenu-container .spacer h3 { + color: #fafafa; + margin-top: 0; + } + .demo-secondarymenu-container .spacer code { + background: #27272a; + padding: 2px 6px; + border-radius: 4px; + font-size: 12px; + } + .demo-secondarymenu-container .spacer ul { + line-height: 1.8; }
console.log('Frontend'), badge: 3, badgeVariant: 'warning' }, - { key: 'API Server', iconName: 'server', action: () => console.log('API'), badge: 'new', badgeVariant: 'success' }, - { key: 'Database', iconName: 'database', action: () => console.log('Database') }, - ] + { key: 'Dashboard', iconName: 'lucide:layoutDashboard', action: () => console.log('Dashboard clicked'), badge: 3, badgeVariant: 'warning' }, + { key: 'Projects', iconName: 'lucide:folder', action: () => console.log('Projects clicked'), badge: 'new', badgeVariant: 'success' }, + { key: 'Analytics', iconName: 'lucide:barChart2', action: () => console.log('Analytics clicked') }, + ] as interfaces.ISecondaryMenuItemTab[] }, + // Group 2: Actions { - name: 'Archived', - iconName: 'lucide:archive', + name: 'Actions', + iconName: 'lucide:zap', + items: [ + { type: 'action', key: 'Create New', iconName: 'lucide:plus', action: () => alert('Create New clicked!') }, + { type: 'action', key: 'Import Data', iconName: 'lucide:upload', action: () => alert('Import Data clicked!') }, + { type: 'divider' }, + { type: 'action', key: 'Delete All', iconName: 'lucide:trash2', variant: 'danger', confirmMessage: 'Are you sure you want to delete all items?', action: () => alert('Deleted!') }, + ] as interfaces.ISecondaryMenuItem[] + }, + // Group 3: Filters + { + name: 'Filters', + iconName: 'lucide:filter', + items: [ + { type: 'header', label: 'Status' }, + { type: 'filter', key: 'Show Active', iconName: 'lucide:checkCircle', active: true, onToggle: (active) => console.log('Show Active:', active) }, + { type: 'filter', key: 'Show Archived', iconName: 'lucide:archive', active: false, onToggle: (active) => console.log('Show Archived:', active) }, + { type: 'divider' }, + { type: 'multiFilter', key: 'Categories', iconName: 'lucide:tag', collapsed: false, options: [ + { key: 'frontend', label: 'Frontend', checked: true, iconName: 'lucide:monitor' }, + { key: 'backend', label: 'Backend', checked: true, iconName: 'lucide:server' }, + { key: 'devops', label: 'DevOps', checked: false, iconName: 'lucide:cloud' }, + { key: 'design', label: 'Design', checked: false, iconName: 'lucide:palette' }, + ], onChange: (keys) => console.log('Selected categories:', keys) }, + ] as interfaces.ISecondaryMenuItem[] + }, + // Group 4: Links and misc + { + name: 'Resources', + iconName: 'lucide:bookOpen', collapsed: true, items: [ - { key: 'Legacy System', iconName: 'box', action: () => console.log('Legacy') }, - { key: 'Old API', iconName: 'server', action: () => console.log('Old API') }, - ] - }, - { - name: 'Settings', - iconName: 'lucide:settings', - items: [ - { key: 'Configuration', iconName: 'sliders', action: () => console.log('Config') }, - { key: 'Integrations', iconName: 'plug', action: () => console.log('Integrations'), badge: 5, badgeVariant: 'error' }, - ] + { type: 'header', label: 'Documentation' }, + { type: 'link', key: 'API Reference', iconName: 'lucide:fileText', href: 'https://api.example.com/docs' }, + { type: 'link', key: 'User Guide', iconName: 'lucide:book', href: 'https://docs.example.com/guide' }, + { type: 'divider' }, + { type: 'header', label: 'Support' }, + { type: 'link', key: 'Help Center', iconName: 'lucide:helpCircle', href: '/help', external: false }, + { type: 'link', key: 'GitHub Issues', iconName: 'lucide:github', href: 'https://github.com/example/issues' }, + ] as interfaces.ISecondaryMenuItem[] } - ] as interfaces.IMenuGroup[]} - @item-select=${(e: CustomEvent) => console.log('Selected:', e.detail)} + ] as interfaces.ISecondaryMenuGroup[]} + @item-select=${(e: CustomEvent) => console.log('Tab selected:', e.detail)} + @action-click=${(e: CustomEvent) => console.log('Action clicked:', e.detail)} + @filter-toggle=${(e: CustomEvent) => console.log('Filter toggled:', e.detail)} + @multifilter-change=${(e: CustomEvent) => console.log('Multi-filter changed:', e.detail)} + @link-click=${(e: CustomEvent) => console.log('Link clicked:', e.detail)} > -
+
+

Secondary Menu Demo

+

This demo showcases all 8 item types:

+ +

Try the collapse toggle on the left edge!

+
`; diff --git a/ts_web/elements/00group-appui/dees-appui-secondarymenu/dees-appui-secondarymenu.ts b/ts_web/elements/00group-appui/dees-appui-secondarymenu/dees-appui-secondarymenu.ts index 1122b3e..2285dd3 100644 --- a/ts_web/elements/00group-appui/dees-appui-secondarymenu/dees-appui-secondarymenu.ts +++ b/ts_web/elements/00group-appui/dees-appui-secondarymenu/dees-appui-secondarymenu.ts @@ -19,7 +19,16 @@ import { themeDefaultStyles } from '../../00theme.js'; /** * Secondary navigation menu for sub-navigation within MainMenu views - * Supports collapsible groups, badges, and dynamic headings + * + * Supports 8 item types: + * 1. Tab - selectable, stays highlighted (default) + * 2. Action - executes without selection (blue) + * 3. Danger Action - red styling with optional confirmation + * 4. Filter - checkbox toggle + * 5. Multi-Filter - collapsible box with multiple checkboxes + * 6. Divider - visual separator + * 7. Header - non-interactive label + * 8. Link - opens URL */ @customElement('dees-appui-secondarymenu') export class DeesAppuiSecondarymenu extends DeesElement { @@ -31,22 +40,30 @@ export class DeesAppuiSecondarymenu extends DeesElement { @property({ type: String }) accessor heading: string = 'Menu'; - /** Grouped items with collapse support */ + /** Grouped items with collapse support - supports new ISecondaryMenuGroup */ @property({ type: Array }) - accessor groups: interfaces.IMenuGroup[] = []; + accessor groups: interfaces.ISecondaryMenuGroup[] = []; /** Legacy flat list support for backward compatibility */ @property({ type: Array }) accessor selectionOptions: (interfaces.IMenuItem | { divider: true })[] = []; - /** Currently selected item */ + /** Currently selected tab item */ @property({ type: Object }) - accessor selectedItem: interfaces.IMenuItem | null = null; + accessor selectedItem: interfaces.ISecondaryMenuItemTab | null = null; /** Internal state for collapsed groups */ @state() accessor collapsedGroups: Set = new Set(); + /** Internal state for collapsed multi-filters */ + @state() + accessor collapsedMultiFilters: Set = new Set(); + + /** Render counter to force re-renders when items are mutated */ + @state() + private accessor renderCounter: number = 0; + /** Horizontal collapse state */ @property({ type: Boolean, reflect: true }) accessor collapsed: boolean = false; @@ -80,6 +97,12 @@ export class DeesAppuiSecondarymenu extends DeesElement { --badge-error-bg: ${cssManager.bdTheme('#fee2e2', '#450a0a')}; --badge-error-fg: ${cssManager.bdTheme('#991b1b', '#f87171')}; + /* Action colors */ + --action-primary: ${cssManager.bdTheme('#2563eb', '#3b82f6')}; + --action-primary-hover: ${cssManager.bdTheme('#1d4ed8', '#60a5fa')}; + --action-danger: ${cssManager.bdTheme('#dc2626', '#ef4444')}; + --action-danger-hover: ${cssManager.bdTheme('#b91c1c', '#f87171')}; + position: relative; display: block; height: 100%; @@ -220,7 +243,7 @@ export class DeesAppuiSecondarymenu extends DeesElement { } .groupHeader:hover { - background: var(--sidebar-hover); + background: ${cssManager.bdTheme('rgba(140, 120, 100, 0.06)', 'rgba(180, 160, 140, 0.08)')}; } .groupHeader .groupTitle { @@ -229,7 +252,7 @@ export class DeesAppuiSecondarymenu extends DeesElement { gap: 8px; font-size: 11px; font-weight: 600; - color: var(--sidebar-fg-muted); + color: ${cssManager.bdTheme('#78716c', '#b5a99a')}; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap; @@ -238,13 +261,13 @@ export class DeesAppuiSecondarymenu extends DeesElement { .groupHeader .groupTitle dees-icon { font-size: 14px; - opacity: 0.7; + color: ${cssManager.bdTheme('#78716c', '#b5a99a')}; } .groupHeader .chevron { font-size: 12px; transition: transform 0.2s ease; - color: var(--sidebar-fg-muted); + color: ${cssManager.bdTheme('#78716c', '#b5a99a')}; } .groupHeader.collapsed .chevron { @@ -264,7 +287,7 @@ export class DeesAppuiSecondarymenu extends DeesElement { .groupItems { overflow: hidden; transition: max-height 0.25s ease, opacity 0.2s ease; - max-height: 500px; + max-height: 1000px; opacity: 1; } @@ -279,7 +302,7 @@ export class DeesAppuiSecondarymenu extends DeesElement { opacity: 1; } - /* Menu Item */ + /* Menu Item Base */ .menuItem { position: relative; display: flex; @@ -304,6 +327,12 @@ export class DeesAppuiSecondarymenu extends DeesElement { background: var(--sidebar-active); } + .menuItem.disabled { + opacity: 0.5; + cursor: not-allowed; + pointer-events: none; + } + .menuItem.selected { background: var(--sidebar-active); color: var(--sidebar-fg-active); @@ -340,6 +369,208 @@ export class DeesAppuiSecondarymenu extends DeesElement { transition: opacity 0.2s ease, width 0.25s ease; } + /* Action Item Styles */ + .menuItem.action-primary { + color: var(--action-primary); + } + + .menuItem.action-primary:hover { + color: var(--action-primary-hover); + background: ${cssManager.bdTheme('rgba(37, 99, 235, 0.08)', 'rgba(59, 130, 246, 0.12)')}; + } + + .menuItem.action-primary dees-icon { + opacity: 1; + } + + .menuItem.action-danger { + color: var(--action-danger); + } + + .menuItem.action-danger:hover { + color: var(--action-danger-hover); + background: ${cssManager.bdTheme('rgba(220, 38, 38, 0.08)', 'rgba(239, 68, 68, 0.12)')}; + } + + .menuItem.action-danger dees-icon { + opacity: 1; + } + + /* Filter Item Styles */ + .menuItem.filter { + justify-content: space-between; + } + + .menuItem.filter .filter-checkbox { + width: 16px; + height: 16px; + border: 2px solid ${cssManager.bdTheme('#d4d4d4', '#525252')}; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.15s ease; + flex-shrink: 0; + } + + .menuItem.filter .filter-checkbox.checked { + background: var(--sidebar-accent); + border-color: var(--sidebar-accent); + } + + .menuItem.filter .filter-checkbox dees-icon { + font-size: 12px; + color: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; + opacity: 1; + } + + .menuItem.filter.active { + color: var(--sidebar-fg-active); + } + + /* Multi-Filter Container */ + .multiFilter { + margin: 4px 0; + border: 1px solid var(--sidebar-border); + border-radius: 8px; + overflow: hidden; + background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.02)', 'rgba(255, 255, 255, 0.02)')}; + } + + .multiFilter-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 12px; + cursor: pointer; + transition: background 0.15s ease; + } + + .multiFilter-header:hover { + background: var(--sidebar-hover); + } + + .multiFilter-header .multiFilter-title { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + font-weight: 500; + color: var(--sidebar-fg-active); + } + + .multiFilter-header .multiFilter-title dees-icon { + font-size: 16px; + opacity: 0.7; + } + + .multiFilter-header .multiFilter-count { + font-size: 11px; + color: var(--sidebar-fg-muted); + background: var(--badge-default-bg); + padding: 2px 6px; + border-radius: 4px; + } + + .multiFilter-header .chevron { + font-size: 12px; + transition: transform 0.2s ease; + color: var(--sidebar-fg-muted); + } + + .multiFilter-header.collapsed .chevron { + transform: rotate(-90deg); + } + + .multiFilter-options { + border-top: 1px solid var(--sidebar-border); + overflow: hidden; + transition: max-height 0.25s ease, opacity 0.2s ease; + max-height: 500px; + opacity: 1; + } + + .multiFilter-options.collapsed { + max-height: 0; + opacity: 0; + border-top: none; + } + + .multiFilter-option { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 12px; + cursor: pointer; + transition: background 0.15s ease; + font-size: 13px; + color: var(--sidebar-fg); + } + + .multiFilter-option:hover { + background: var(--sidebar-hover); + color: var(--sidebar-fg-active); + } + + .multiFilter-option .option-checkbox { + width: 16px; + height: 16px; + border: 2px solid ${cssManager.bdTheme('#d4d4d4', '#525252')}; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.15s ease; + flex-shrink: 0; + } + + .multiFilter-option .option-checkbox.checked { + background: var(--sidebar-accent); + border-color: var(--sidebar-accent); + } + + .multiFilter-option .option-checkbox dees-icon { + font-size: 12px; + color: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; + } + + .multiFilter-option dees-icon.option-icon { + font-size: 14px; + opacity: 0.7; + } + + /* Divider */ + .menuDivider { + height: 1px; + background: var(--sidebar-border); + margin: 8px 12px; + } + + :host([collapsed]) .menuDivider { + margin: 8px 4px; + } + + /* Header/Label */ + .menuHeader { + padding: 12px 12px 4px 12px; + font-size: 10px; + font-weight: 600; + color: var(--sidebar-fg-muted); + text-transform: uppercase; + letter-spacing: 0.5px; + } + + :host([collapsed]) .menuHeader { + display: none; + } + + /* Link Item */ + .menuItem.link .external-icon { + font-size: 12px; + opacity: 0.5; + margin-left: auto; + } + /* Collapsed menu item styles */ :host([collapsed]) .menuItem { justify-content: center; @@ -357,6 +588,15 @@ export class DeesAppuiSecondarymenu extends DeesElement { left: -4px; } + :host([collapsed]) .menuItem .filter-checkbox, + :host([collapsed]) .menuItem .external-icon { + display: none; + } + + :host([collapsed]) .multiFilter { + display: none; + } + /* Tooltip for collapsed state */ .item-tooltip { position: absolute; @@ -431,17 +671,17 @@ export class DeesAppuiSecondarymenu extends DeesElement { display: none; } - /* Divider */ + /* Legacy options container */ + .legacyOptions { + padding: 0 8px; + } + + /* Divider (legacy) */ .divider { height: 1px; background: var(--sidebar-border); margin: 8px 12px; } - - /* Legacy options container */ - .legacyOptions { - padding: 0 8px; - } `, ]; @@ -472,28 +712,58 @@ export class DeesAppuiSecondarymenu extends DeesElement { @click="${() => this.toggleGroup(group.name)}" > - ${group.iconName ? html`` : ''} + ${group.iconName ? html`` : ''} ${group.name}
- ${group.items.map((item) => this.renderMenuItem(item, group))} + ${group.items.map((item) => this.renderItem(item, group))}
`)} `; } - private renderMenuItem(item: interfaces.IMenuItem, group?: interfaces.IMenuGroup): TemplateResult { + private renderItem(item: interfaces.ISecondaryMenuItem, group?: interfaces.ISecondaryMenuGroup): TemplateResult { + // Check for hidden items + if ('hidden' in item && item.hidden) { + return html``; + } + + // Determine item type + const itemType = 'type' in item ? item.type : 'tab'; + + switch (itemType) { + case 'action': + return this.renderActionItem(item as interfaces.ISecondaryMenuItemAction); + case 'filter': + return this.renderFilterItem(item as interfaces.ISecondaryMenuItemFilter); + case 'multiFilter': + return this.renderMultiFilterItem(item as interfaces.ISecondaryMenuItemMultiFilter); + case 'divider': + return this.renderDivider(); + case 'header': + return this.renderHeader(item as interfaces.ISecondaryMenuItemHeader); + case 'link': + return this.renderLinkItem(item as interfaces.ISecondaryMenuItemLink); + case 'tab': + default: + return this.renderTabItem(item as interfaces.ISecondaryMenuItemTab, group); + } + } + + private renderTabItem(item: interfaces.ISecondaryMenuItemTab, group?: interfaces.ISecondaryMenuGroup): TemplateResult { const isSelected = this.selectedItem?.key === item.key; + const isDisabled = item.disabled === true; + return html`