From 4575e3ce4682d70617ea51c7991e4820789bc413 Mon Sep 17 00:00:00 2001 From: Cesar Mendivil Date: Wed, 5 Nov 2025 17:00:41 -0700 Subject: [PATCH] feat: Mejora del componente Dropdown con soporte para encabezados no interactivos y transiciones de fondo --- .../src/components/Dropdown.module.css | 27 ++++++++++- .../src/components/Dropdown.tsx | 48 ++++++++++++++----- .../src/components/Header.module.css | 7 ++- .../broadcast-panel/src/components/Header.tsx | 48 ++++++++++++++++++- .../src/components/Sidebar.tsx | 5 +- 5 files changed, 117 insertions(+), 18 deletions(-) diff --git a/packages/broadcast-panel/src/components/Dropdown.module.css b/packages/broadcast-panel/src/components/Dropdown.module.css index a369283..5878e60 100644 --- a/packages/broadcast-panel/src/components/Dropdown.module.css +++ b/packages/broadcast-panel/src/components/Dropdown.module.css @@ -43,7 +43,16 @@ } .dropdownItem:hover { - background-color: rgba(255,255,255,0.03); + background-color: var(--border-light); +} + +/* ensure header (non-interactive) doesn't get hover background */ +.dropdownHeader:hover { + background: transparent; +} + +.dropdownItem { + transition: background-color 0.12s ease; } .dropdownItem .icon { @@ -53,6 +62,22 @@ color: var(--text-secondary); } +.dropdownHeader { + display: flex; + align-items: center; + gap: 12px; + padding: 10px 16px; + color: var(--text-primary); + font-size: 14px; + opacity: 0.90; /* user requested opacity */ + cursor: default; +} + +.dropdownHeaderLabel { + opacity: 0.90; + color: var(--text-primary); +} + .divider { height: 1px; background-color: rgba(255,255,255,0.03); diff --git a/packages/broadcast-panel/src/components/Dropdown.tsx b/packages/broadcast-panel/src/components/Dropdown.tsx index b7d2d5d..6ab1eec 100644 --- a/packages/broadcast-panel/src/components/Dropdown.tsx +++ b/packages/broadcast-panel/src/components/Dropdown.tsx @@ -4,8 +4,11 @@ import styles from './Dropdown.module.css'; interface DropdownItem { label: string; icon?: React.ReactNode; - onClick: () => void; + onClick?: () => void; divider?: boolean; + disabled?: boolean; // non-interactive header-like item + containerProps?: React.HTMLAttributes; + labelProps?: React.HTMLAttributes; } interface DropdownProps { @@ -44,16 +47,39 @@ export const Dropdown: React.FC = ({ trigger, items }) => { {items.map((item, index) => ( {item.divider &&
} - + {item.disabled ? ( +
+ {item.icon && {item.icon}} + { + // merge className for the header label + (() => { + const lp = item.labelProps || {}; + const { className: lpClassName, ...lpOther } = lp as any; + const classes = [styles.dropdownHeaderLabel, lpClassName].filter(Boolean).join(' '); + return {item.label}; + })() + } +
+ ) : ( + + )} ))}
diff --git a/packages/broadcast-panel/src/components/Header.module.css b/packages/broadcast-panel/src/components/Header.module.css index 7e8decc..b8628b5 100644 --- a/packages/broadcast-panel/src/components/Header.module.css +++ b/packages/broadcast-panel/src/components/Header.module.css @@ -114,7 +114,12 @@ } .userEmail { - opacity: 0.95; + opacity: 0.90; + font-size: 14px; + color: var(--text-secondary); +} + +.userTitleMenu { font-size: 14px; color: var(--text-secondary); } diff --git a/packages/broadcast-panel/src/components/Header.tsx b/packages/broadcast-panel/src/components/Header.tsx index 4179f16..fd49e37 100644 --- a/packages/broadcast-panel/src/components/Header.tsx +++ b/packages/broadcast-panel/src/components/Header.tsx @@ -13,11 +13,55 @@ const Header: React.FC = () => { window.location.href = '/auth/login' } + // Read mock user from localStorage (if set elsewhere in the app) + const storedUser = typeof window !== 'undefined' ? localStorage.getItem('mock_user') : null + let email = 'Usuario' + let avatarUrl: string | null = null + let displayName: string | null = null + try { + if (storedUser) { + const parsed = JSON.parse(storedUser) + email = parsed.email || storedUser || 'Usuario' + avatarUrl = parsed.avatar || parsed.photo || parsed.picture || null + displayName = parsed.name || parsed.fullname || null + } + } catch (e) { + email = storedUser || 'Usuario' + } + + const computeInitials = (nameOrEmail: string) => { + if (!nameOrEmail) return 'U' + const name = nameOrEmail.split('@')[0] + const parts = name.split(/[^A-Za-z0-9]+/).filter(Boolean) + if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase() + if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase() + return nameOrEmail.slice(0, 2).toUpperCase() + } + + const initials = computeInitials(displayName || email) + + const avatarElement = avatarUrl ? ( + avatar + ) : ( +
+ {initials} +
+ ) + const userMenuItems = [ + // non-interactive header with email + { + label: email, + icon: avatarElement, + disabled: true, + containerProps: { 'data-test': 'user-email', id: 'user-email' }, + labelProps: { style: { opacity: 0.7 } } + }, { label: 'Mi perfil', icon: , - onClick: () => console.log('Ir a perfil') + onClick: () => console.log('Ir a perfil'), + divider: true // separator after the header }, { label: 'Ayuda', @@ -76,7 +120,7 @@ const Header: React.FC = () => { - nextv.stream@gmail.com + Mi cuenta } items={userMenuItems} diff --git a/packages/broadcast-panel/src/components/Sidebar.tsx b/packages/broadcast-panel/src/components/Sidebar.tsx index f04db66..e2b10f8 100644 --- a/packages/broadcast-panel/src/components/Sidebar.tsx +++ b/packages/broadcast-panel/src/components/Sidebar.tsx @@ -42,7 +42,8 @@ const Sidebar: React.FC = ({ activeLink = 'inicio' }) => { ))} - {/* Secondary Navigation moved closer to storage */} + + {/* Secondary Navigation moved closer to storage */}
    {secondaryNavItems.map(item => ( @@ -55,8 +56,6 @@ const Sidebar: React.FC = ({ activeLink = 'inicio' }) => { ))}
- - {/* Storage Info */}