@a.nemreen/dga-ui 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2615 @@
1
+ import*as i from"@angular/core";import{Directive as y,inject as g,ElementRef as B,input as n,booleanAttribute as I,model as w,output as u,signal as l,contentChild as M,Injector as Y,DOCUMENT as Q,DestroyRef as W,viewChild as P,computed as s,effect as L,untracked as z,afterNextRender as S,HostListener as b,ChangeDetectionStrategy as K,Component as G}from"@angular/core";import{APP_BASE_HREF as Z,PlatformLocation as X}from"@angular/common";import{dgaSafeHref as O,dgaCx as c,DgaAvatar as A,DgaBadge as E,DgaField as H,DgaFormControl as F,DgaFormEnd as $,DgaIcon as R,DgaIconBadge as j,DgaModal as q,DgaTooltip as V}from"@a.nemreen/dga-ui";function J(k,e="/"){if(typeof window>"u")return!1;const t=O(k.trim(),e);if(!t)return!1;try{const o=new URL(t,window.location.origin);return o.protocol!=="http:"&&o.protocol!=="https:"||o.origin!==window.location.origin?!1:(window.location.assign(`${o.pathname}${o.search}${o.hash}`),!0)}catch{return!1}}class h{static ɵfac=i.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.0.7",ngImport:i,type:h,deps:[],target:i.ɵɵFactoryTarget.Directive});static ɵdir=i.ɵɵngDeclareDirective({minVersion:"14.0.0",version:"22.0.7",type:h,isStandalone:!0,selector:"[dgaHeaderLoginBeforeForm]",ngImport:i})}i.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.0.7",ngImport:i,type:h,decorators:[{type:y,args:[{selector:"[dgaHeaderLoginBeforeForm]",standalone:!0}]}]});class x{static ɵfac=i.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.0.7",ngImport:i,type:x,deps:[],target:i.ɵɵFactoryTarget.Directive});static ɵdir=i.ɵɵngDeclareDirective({minVersion:"14.0.0",version:"22.0.7",type:x,isStandalone:!0,selector:"[dgaHeaderLoginAfterFields]",ngImport:i})}i.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.0.7",ngImport:i,type:x,decorators:[{type:y,args:[{selector:"[dgaHeaderLoginAfterFields]",standalone:!0}]}]});class p{static ɵfac=i.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.0.7",ngImport:i,type:p,deps:[],target:i.ɵɵFactoryTarget.Directive});static ɵdir=i.ɵɵngDeclareDirective({minVersion:"14.0.0",version:"22.0.7",type:p,isStandalone:!0,selector:"[dgaHeaderLoginFooter]",ngImport:i})}i.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.0.7",ngImport:i,type:p,decorators:[{type:y,args:[{selector:"[dgaHeaderLoginFooter]",standalone:!0}]}]});class m{static ɵfac=i.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.0.7",ngImport:i,type:m,deps:[],target:i.ɵɵFactoryTarget.Directive});static ɵdir=i.ɵɵngDeclareDirective({minVersion:"14.0.0",version:"22.0.7",type:m,isStandalone:!0,selector:"[dgaHeaderLoginForm]",ngImport:i})}i.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.0.7",ngImport:i,type:m,decorators:[{type:y,args:[{selector:"[dgaHeaderLoginForm]",standalone:!0}]}]});function ee(k,e){const t=k.trim();if(t.endsWith("rem")){const o=Number.parseFloat(t);return Number.isFinite(o)?o*16:e}if(t.endsWith("px")){const o=Number.parseFloat(t);return Number.isFinite(o)?o:e}return e}const T="group relative inline-flex h-(--size-header-portal) shrink-0 cursor-pointer items-center gap-md rounded-sm px-xl font-sans text-body-md font-medium text-fg hover:bg-neutral-light focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus",te="pointer-events-none absolute start-xl end-xl bottom-0 h-[5px] rounded-full",ie="inline-flex min-h-5xl w-full cursor-pointer flex-col justify-center gap-xxs rounded-sm px-xl py-sm text-start text-body-md font-normal text-fg transition-colors hover:bg-neutral-light focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus",oe="inline-flex min-h-11xl w-full cursor-pointer flex-col justify-center gap-xs rounded-md border border-border bg-card px-2xl py-xl text-start transition-colors hover:border-primary hover:bg-primary-faint focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus",ae="relative flex min-h-7xl w-full cursor-pointer items-center gap-lg rounded-md px-xl py-md text-start text-body-md font-medium text-fg transition-colors hover:bg-neutral-light active:bg-neutral-light focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus",ne="flex min-h-6xl w-full cursor-pointer flex-col justify-center gap-xxs rounded-md px-xl py-md text-start text-body-md text-fg transition-colors hover:bg-neutral-light active:bg-neutral-light focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus";class v{host=g(B);baseHref=g(Z,{optional:!0})??g(X).getBaseHrefFromDOM()??"/";closeTimer=null;dropdownSyncFrame=null;lastDropdownStyleKey="";collapseId="dga-nav-collapse";dropLink=ie;dropTile=oe;mobileLink=ne;brand=n("",...ngDevMode?[{debugName:"brand"}]:[]);brandSlogan=n("",...ngDevMode?[{debugName:"brandSlogan"}]:[]);brandLogo=n("",...ngDevMode?[{debugName:"brandLogo"}]:[]);brandLogoAlt=n("شعار الجهة",...ngDevMode?[{debugName:"brandLogoAlt"}]:[]);brandPlaceholder=n(!1,{...ngDevMode?{debugName:"brandPlaceholder"}:{},transform:I});brandHref=n("#",...ngDevMode?[{debugName:"brandHref"}]:[]);navLabel=n("التنقل الرئيسي",...ngDevMode?[{debugName:"navLabel"}]:[]);menuToggleLabel=n("فتح قائمة التنقل",...ngDevMode?[{debugName:"menuToggleLabel"}]:[]);menuCloseLabel=n("إغلاق قائمة التنقل",...ngDevMode?[{debugName:"menuCloseLabel"}]:[]);appearance=n("default",...ngDevMode?[{debugName:"appearance"}]:[]);mobileNav=n("sheet",...ngDevMode?[{debugName:"mobileNav"}]:[]);sticky=n(!0,{...ngDevMode?{debugName:"sticky"}:{},transform:I});items=n([],...ngDevMode?[{debugName:"items"}]:[]);actions=n([],...ngDevMode?[{debugName:"actions"}]:[]);search=n(null,...ngDevMode?[{debugName:"search"}]:[]);searchQuery=w("",...ngDevMode?[{debugName:"searchQuery"}]:[]);notifications=n(null,...ngDevMode?[{debugName:"notifications"}]:[]);login=n(null,...ngDevMode?[{debugName:"login"}]:[]);user=n(null,...ngDevMode?[{debugName:"user"}]:[]);authenticated=w(!1,...ngDevMode?[{debugName:"authenticated"}]:[]);loginSubmitting=w(!1,...ngDevMode?[{debugName:"loginSubmitting"}]:[]);loginError=w("",...ngDevMode?[{debugName:"loginError"}]:[]);actionClick=u();searchSubmit=u();searchSuggestionSelect=u();searchVoice=u();notificationAction=u();loginSubmit=u();logout=u();userLinkClick=u();navLinkClick=u();mobileOpen=l(!1,...ngDevMode?[{debugName:"mobileOpen"}]:[]);openDropdown=l(null,...ngDevMode?[{debugName:"openDropdown"}]:[]);searchOpen=l(!1,...ngDevMode?[{debugName:"searchOpen"}]:[]);notificationsOpen=l(!1,...ngDevMode?[{debugName:"notificationsOpen"}]:[]);loginOpen=l(!1,...ngDevMode?[{debugName:"loginOpen"}]:[]);userMenuOpen=l(!1,...ngDevMode?[{debugName:"userMenuOpen"}]:[]);voiceListening=l(!1,...ngDevMode?[{debugName:"voiceListening"}]:[]);expandedNotificationId=l(null,...ngDevMode?[{debugName:"expandedNotificationId"}]:[]);loginEmail=l("",...ngDevMode?[{debugName:"loginEmail"}]:[]);loginPassword=l("",...ngDevMode?[{debugName:"loginPassword"}]:[]);showLoginPassword=l(!1,...ngDevMode?[{debugName:"showLoginPassword"}]:[]);loginEmailError=l("",...ngDevMode?[{debugName:"loginEmailError"}]:[]);loginPasswordError=l("",...ngDevMode?[{debugName:"loginPasswordError"}]:[]);dropdownPanelStyle=l({},...ngDevMode?[{debugName:"dropdownPanelStyle"}]:[]);customLoginForm=M(m,...ngDevMode?[{debugName:"customLoginForm"}]:[]);customLoginFooter=M(p,...ngDevMode?[{debugName:"customLoginFooter"}]:[]);dismissedNotificationIds=l(new Set,...ngDevMode?[{debugName:"dismissedNotificationIds"}]:[]);readNotificationIds=l(new Set,...ngDevMode?[{debugName:"readNotificationIds"}]:[]);dropdownTrigger=null;dropdownActionId=null;injector=g(Y);doc=g(Q);destroyRef=g(W);searchInputRef=P("searchInput",...ngDevMode?[{debugName:"searchInputRef"}]:[]);utilLayerRef=P("utilLayer",...ngDevMode?[{debugName:"utilLayerRef"}]:[]);searchPanelId="dga-header-search-panel";notificationsPanelId="dga-header-notifications-panel";loginPanelId="dga-header-login-panel";userPanelId="dga-header-user-panel";isPortal=s(()=>this.appearance()==="portal",...ngDevMode?[{debugName:"isPortal"}]:[]);appHref(e){return O(e,this.baseHref)}searchConfig=s(()=>this.search(),...ngDevMode?[{debugName:"searchConfig"}]:[]);searchActionId=s(()=>this.search()?.actionId??"search",...ngDevMode?[{debugName:"searchActionId"}]:[]);notificationsConfig=s(()=>this.notifications(),...ngDevMode?[{debugName:"notificationsConfig"}]:[]);notificationsActionId=s(()=>this.notifications()?.actionId??"notifications",...ngDevMode?[{debugName:"notificationsActionId"}]:[]);loginConfig=s(()=>this.login(),...ngDevMode?[{debugName:"loginConfig"}]:[]);loginActionId=s(()=>this.login()?.actionId??"login",...ngDevMode?[{debugName:"loginActionId"}]:[]);userConfig=s(()=>this.user(),...ngDevMode?[{debugName:"userConfig"}]:[]);hasCustomLoginForm=s(()=>!!this.customLoginForm(),...ngDevMode?[{debugName:"hasCustomLoginForm"}]:[]);hasCustomLoginFooter=s(()=>!!this.customLoginFooter(),...ngDevMode?[{debugName:"hasCustomLoginFooter"}]:[]);utilityOverlayOpen=s(()=>this.notificationsOpen()||this.loginOpen()||this.userMenuOpen(),...ngDevMode?[{debugName:"utilityOverlayOpen"}]:[]);visibleNotifications=s(()=>{const e=this.notifications()?.items??[],t=this.dismissedNotificationIds(),o=this.readNotificationIds();return e.filter(a=>!t.has(a.id)).map(a=>({...a,read:!!a.read||o.has(a.id)}))},...ngDevMode?[{debugName:"visibleNotifications"}]:[]);unreadNotificationCount=s(()=>this.visibleNotifications().filter(e=>!e.read).length,...ngDevMode?[{debugName:"unreadNotificationCount"}]:[]);constructor(){this.destroyRef.onDestroy(()=>this.removePortaledUtilLayer()),L(()=>{this.mobileNav(),z(()=>{this.mobileOpen()&&this.closeAll()})}),L(()=>{if(!this.utilityOverlayOpen())return;this.utilLayerRef()?.nativeElement&&this.portalUtilLayer(),S(()=>this.portalUtilLayer(),{injector:this.injector})}),L(()=>{this.authenticated()&&this.loginOpen()&&z(()=>this.closeLogin())})}portalUtilLayer(){const e=this.utilLayerRef()?.nativeElement;!e||e.parentElement===this.doc.body||this.doc.body.appendChild(e)}removePortaledUtilLayer(){const e=this.utilLayerRef()?.nativeElement;e?.parentElement===this.doc.body&&e.remove()}activeWideDropdown=s(()=>{if(!this.hoverMenus()&&this.isMobileViewport())return null;const e=this.openDropdown();if(e===null)return null;const t=this.items()[e];if(!t?.dropdown)return null;const o=this.resolveVariant(t);return o==="menu"?null:{index:e,item:t,variant:o}},...ngDevMode?[{debugName:"activeWideDropdown"}]:[]);hostClasses=s(()=>c("relative z-header block overflow-visible border-b border-border-subtle bg-nav font-sans text-fg",this.sticky()&&"sticky top-0 shadow-md"),...ngDevMode?[{debugName:"hostClasses"}]:[]);innerClasses=s(()=>c("relative flex w-full gap-lg px-(--wrapper-padding)",this.appearance()==="default"&&"min-h-6xl items-center py-md",this.appearance()==="portal"&&"h-(--size-header-portal) items-stretch"),...ngDevMode?[{debugName:"innerClasses"}]:[]);desktopNavClasses=s(()=>c("min-w-0 flex-1",this.appearance()==="default"&&"flex flex-wrap items-center gap-lg self-center",this.appearance()==="portal"&&"hidden md:flex md:items-stretch"),...ngDevMode?[{debugName:"desktopNavClasses"}]:[]);actionsClasses=s(()=>c("ms-auto flex shrink-0 items-stretch empty:hidden",this.isPortal()&&this.items().length>0&&"max-md:hidden"),...ngDevMode?[{debugName:"actionsClasses"}]:[]);mobilePanelClass(){const e=this.mobileOpen(),t=this.mobileNav();return c("flex flex-col overflow-hidden bg-nav md:hidden",t==="sheet"&&"fixed top-0 bottom-0 end-0 z-20 w-[min(16rem,70vw)] max-w-[16rem] rounded-s-xl border-s border-border shadow-2xl",t==="fullscreen"&&"fixed inset-0 z-40 rounded-none border-0 shadow-none",t==="push"&&"absolute inset-x-0 top-full z-20 max-h-[min(70dvh,28rem)] border-b border-border shadow-lg",e?"dga-mobile-nav-open":"dga-mobile-nav-closed",e&&t==="sheet"&&"dga-mobile-sheet-open",e&&t==="fullscreen"&&"dga-mobile-fs-open",e&&t==="push"&&"dga-mobile-push-open")}hoverMenus(){return typeof matchMedia!="function"?!1:matchMedia("(hover: hover) and (pointer: fine)").matches}isMobileViewport(){return typeof matchMedia!="function"?!1:matchMedia("(max-width: 767px)").matches}resolveVariant(e){const t=e.dropdown;return t?t.variant?t.variant:t.featured?"featured":t.columns?.length||t.layout==="columns"||t.layout==="rows"?"mega":"menu":"menu"}resolveLayout(e){const t=e.dropdown;return t?t.layout?t.layout:t.columns?.length?"columns":"list":"list"}colCount(e){return e.dropdown?.columns?.length??0}flatLinks(e){const t=e.dropdown;return t?t.links?.length?t.links:t.columns?.flatMap(o=>o.links)??[]:[]}columnsGridClass(e){return c("grid w-full gap-2xl py-lg",e>1&&"sm:grid-cols-2",e>2&&"lg:grid-cols-3",e>3&&"xl:grid-cols-4")}featuredGridClass(e,t){return c("grid w-full gap-3xl",e==="featured"&&t&&"lg:grid-cols-[minmax(0,1fr)_minmax(14rem,18rem)]")}navItemClass(e,t=!1){return c(T,"justify-center",e&&"text-fg-primary",t&&"bg-neutral-light")}mobileItemClass(e,t=!1){return c(ae,e&&"bg-primary-faint",t&&"bg-neutral-light")}indicatorClass(e){return c(te,e?"bg-primary group-hover:bg-primary":"bg-transparent group-hover:bg-fg-disabled")}actionClass(e){return c(T,"relative justify-center")}badgeClass(){return c("dga-action-badge absolute top-lg end-md flex h-lg min-w-lg","items-center justify-center rounded-full bg-error px-xxs","text-body-2xs font-medium text-on-color")}isIconOnly(e){return e.iconOnly!==!1}isSearchAction(e){return!!this.search()&&e.id===this.searchActionId()}isNotificationsAction(e){return!!this.notifications()&&e.id===this.notificationsActionId()}isLoginAction(e){return!!this.login()&&e.id===this.loginActionId()}actionTooltip(e){return this.isLoginAction(e)&&this.authenticated()&&this.user()?.name||e.label}actionAriaLabel(e){return this.actionTooltip(e)}actionExpanded(e){return this.isSearchAction(e)?this.searchOpen():this.isNotificationsAction(e)?this.notificationsOpen():this.isLoginAction(e)?this.authenticated()?this.userMenuOpen():this.loginOpen():null}actionControls(e){return this.isSearchAction(e)?this.searchPanelId:this.isNotificationsAction(e)?this.notificationsPanelId:this.isLoginAction(e)?this.authenticated()?this.userPanelId:this.loginPanelId:null}actionIndicatorActive(e){return this.isSearchAction(e)?this.searchOpen():this.isNotificationsAction(e)?this.notificationsOpen():this.isLoginAction(e)?this.authenticated()?this.userMenuOpen():this.loginOpen():!1}clearCloseTimer(){this.closeTimer!==null&&(clearTimeout(this.closeTimer),this.closeTimer=null)}scheduleClose(){this.clearCloseTimer(),this.closeTimer=setTimeout(()=>{this.openDropdown.set(null),this.closeTimer=null},120)}setBodyScrollLocked(e){typeof document>"u"||document.documentElement.classList.toggle("dga-nav-locked",e)}unlockBodyIfIdle(){this.mobileOpen()||this.utilityOverlayOpen()||this.setBodyScrollLocked(!1)}onItemEnter(e){if(!this.isMobileViewport()){if(!this.items()[e]?.dropdown){this.hoverMenus()&&this.scheduleClose();return}this.clearCloseTimer(),this.hoverMenus()&&this.openDropdown.set(e)}}onPanelEnter(){this.clearCloseTimer()}onHostLeave(e){if(!this.hoverMenus()||this.isMobileViewport())return;const t=e.relatedTarget;t&&this.host.nativeElement.contains(t)||this.scheduleClose()}toggleMobile(){this.mobileOpen.update(e=>!e),this.openDropdown.set(null),this.setBodyScrollLocked(this.mobileOpen())}toggleDropdown(e,t){t.stopPropagation(),!(this.hoverMenus()&&!this.isMobileViewport()&&this.openDropdown()===e)&&(this.clearCloseTimer(),this.closeSearch(),this.closeNotifications(),this.closeLogin(),this.closeUserMenu(),this.openDropdown.update(o=>o===e?null:e))}closeAll(){this.clearCloseTimer(),this.openDropdown.set(null),this.mobileOpen.set(!1),this.closeSearch(!1),this.closeNotifications(),this.closeLogin(),this.closeUserMenu(),this.setBodyScrollLocked(!1)}onAction(e,t){const o=this.triggerFromEvent(t);if(this.isSearchAction(e)){this.closeNotifications(),this.closeLogin(),this.closeUserMenu(),this.toggleSearch(),this.actionClick.emit(e);return}if(this.isNotificationsAction(e)){this.closeSearch(),this.closeLogin(),this.closeUserMenu(),this.toggleNotifications(o),this.actionClick.emit(e);return}if(this.isLoginAction(e)){this.closeSearch(),this.closeNotifications(),this.authenticated()?(this.closeLogin(),this.toggleUserMenu(o)):(this.closeUserMenu(),this.toggleLogin(o)),this.actionClick.emit(e);return}this.closeSearch(),this.closeNotifications(),this.closeLogin(),this.closeUserMenu(),this.actionClick.emit(e)}onMobileAction(e,t){const o=this.triggerFromEvent(t);if(this.isSearchAction(e)){this.mobileOpen.set(!1),this.setBodyScrollLocked(!1),this.closeNotifications(),this.closeLogin(),this.closeUserMenu(),this.toggleSearch(),this.actionClick.emit(e);return}if(this.isNotificationsAction(e)){this.mobileOpen.set(!1),this.closeSearch(),this.closeLogin(),this.closeUserMenu(),this.toggleNotifications(o),this.isMobileViewport()&&this.setBodyScrollLocked(!0),this.actionClick.emit(e);return}if(this.isLoginAction(e)){this.mobileOpen.set(!1),this.closeSearch(),this.closeNotifications(),this.authenticated()?(this.closeLogin(),this.toggleUserMenu(o)):(this.closeUserMenu(),this.toggleLogin(o)),this.isMobileViewport()&&this.setBodyScrollLocked(!0),this.actionClick.emit(e);return}this.actionClick.emit(e),this.closeAll()}closeUtilityOverlays(){this.closeSearch(),this.closeNotifications(),this.closeLogin(),this.closeUserMenu()}closePortalChromeMenus(){typeof document>"u"||document.dispatchEvent(new CustomEvent("dga-close-portal-chrome-menus"))}onCloseHeaderOverlays(){this.closeUtilityOverlays()}toggleSearch(){if(this.searchOpen()){this.closeSearch();return}this.openDropdown.set(null),this.closeNotifications(),this.closeLogin(),this.closeUserMenu(),this.closePortalChromeMenus(),this.searchOpen.set(!0)}onSearchModalOpenChange(e){if(e){this.searchOpen.set(!0);return}this.closeSearch()}focusSearchInput(){S(()=>this.searchInputRef()?.nativeElement.focus(),{injector:this.injector})}closeSearch(e=!0){this.searchOpen.set(!1),e&&this.stopVoice()}toggleNotifications(e){if(this.notificationsOpen()){this.closeNotifications();return}this.openDropdown.set(null),this.closeSearch(),this.closeLogin(),this.closeUserMenu(),this.closePortalChromeMenus(),this.notificationsOpen.set(!0);const t=this.visibleNotifications()[0];this.expandedNotificationId.set(t?.id??null),this.scheduleDropdownSync(this.notificationsActionId(),e),this.isMobileViewport()&&this.setBodyScrollLocked(!0)}closeNotifications(){this.notificationsOpen.set(!1),this.expandedNotificationId.set(null),this.clearDropdownAnchor(),this.unlockBodyIfIdle()}toggleLogin(e){if(this.loginOpen()){this.closeLogin();return}this.openDropdown.set(null),this.closeSearch(),this.closeNotifications(),this.closeUserMenu(),this.closePortalChromeMenus(),this.loginOpen.set(!0),this.scheduleDropdownSync(this.loginActionId(),e),this.isMobileViewport()&&this.setBodyScrollLocked(!0)}closeLogin(){this.loginOpen.set(!1),this.loginSubmitting.set(!1),this.loginError.set(""),this.loginEmailError.set(""),this.loginPasswordError.set(""),this.loginEmail.set(""),this.loginPassword.set(""),this.showLoginPassword.set(!1),this.clearDropdownAnchor(),this.unlockBodyIfIdle()}toggleUserMenu(e){if(this.userMenuOpen()){this.closeUserMenu();return}this.openDropdown.set(null),this.closeSearch(),this.closeNotifications(),this.closeLogin(),this.closePortalChromeMenus(),this.userMenuOpen.set(!0),this.scheduleDropdownSync(this.loginActionId(),e),this.isMobileViewport()&&this.setBodyScrollLocked(!0)}closeUserMenu(){this.userMenuOpen.set(!1),this.clearDropdownAnchor(),this.unlockBodyIfIdle()}onLoginEmailInput(e){this.loginEmail.set(e),this.loginEmailError()&&this.loginEmailError.set("")}onLoginPasswordInput(e){this.loginPassword.set(e),this.loginPasswordError()&&this.loginPasswordError.set("")}showLoginRegisterRow(e){return!!(e.registerPrompt||e.registerLabel||e.registerHref)}loginPanelOuterStyle(){const e=this.dropdownPanelStyle();if(this.isMobileViewport())return e;const t=this.loginConfig()?.panelWidth??"24rem";return{...e,width:`min(${t}, calc(100vw - 2rem))`}}loginPanelInnerStyle(){return this.isMobileViewport()?{maxHeight:`min(90svh, ${this.loginConfig()?.maxHeight??"44rem"})`}:{}}onLoginSubmit(e){e.preventDefault();const t=this.loginConfig(),o=this.loginEmail().trim(),a=this.loginPassword();this.loginEmailError.set(""),this.loginPasswordError.set(""),this.loginError.set("");let r=!1;o?/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(o)||(this.loginEmailError.set(t?.emailInvalidMessage??"Enter a valid email address"),r=!0):(this.loginEmailError.set(t?.emailRequiredMessage??"Email is required"),r=!0),a||(this.loginPasswordError.set(t?.passwordRequiredMessage??"Password is required"),r=!0),!r&&(this.loginSubmitting.set(!0),this.loginSubmit.emit({email:o,password:a}))}onUserLink(e){if(this.userLinkClick.emit(e),e.id==="logout"||e.danger){this.authenticated.set(!1),this.logout.emit(),this.closeUserMenu();return}this.closeUserMenu()}toggleNotificationExpand(e){this.expandedNotificationId.update(t=>t===e.id?null:e.id),this.notificationAction.emit({item:e,action:"select"})}onNotificationAction(e,t){t==="read"&&this.readNotificationIds.update(o=>new Set([...o,e.id])),t==="dismiss"&&(this.dismissedNotificationIds.update(o=>new Set([...o,e.id])),this.expandedNotificationId.update(o=>o===e.id?null:o)),t==="view"&&e.href&&J(e.href,this.baseHref),this.notificationAction.emit({item:e,action:t})}markAllNotificationsRead(){const e=this.visibleNotifications().map(t=>t.id);this.readNotificationIds.update(t=>new Set([...t,...e]))}clearSearchQuery(){this.searchQuery.set(""),queueMicrotask(()=>this.searchInputRef()?.nativeElement.focus())}applySearchSuggestion(e){const t=e.value??e.label;this.searchQuery.set(t),this.searchSuggestionSelect.emit(e),queueMicrotask(()=>this.searchInputRef()?.nativeElement.focus())}onSearchFormSubmit(e){e.preventDefault();const t=this.searchQuery().trim();this.searchSubmit.emit(t);const o=this.search();if(o?.resultsHref){const a=o.queryParam??"q",r=new URL(o.resultsHref,typeof window<"u"?window.location.origin:"http://localhost");t?r.searchParams.set(a,t):r.searchParams.delete(a),typeof window<"u"&&window.location.assign(`${r.pathname}${r.search}${r.hash}`)}this.closeSearch()}onSearchVoice(){if(this.searchVoice.emit(),typeof window>"u")return;const e=window,t=e.SpeechRecognition||e.webkitSpeechRecognition;if(!t)return;this.stopVoice();const o=new t;o.lang=typeof document<"u"&&document.documentElement.lang?document.documentElement.lang:"ar-SA",o.interimResults=!1,o.maxAlternatives=1,this.voiceListening.set(!0),o.onresult=a=>{const r=a.results[0]?.[0]?.transcript?.trim();r&&this.searchQuery.set(r),this.voiceListening.set(!1)},o.onerror=()=>this.voiceListening.set(!1),o.onend=()=>this.voiceListening.set(!1),this.speechRecognition=o,o.start()}speechRecognition=null;stopVoice(){this.speechRecognition?.stop(),this.speechRecognition=null,this.voiceListening.set(!1)}triggerFromEvent(e){const t=e?.currentTarget;return t instanceof HTMLElement?t:null}scheduleDropdownSync(e,t){this.dropdownActionId=e,this.dropdownTrigger=t??null,S(()=>this.requestDropdownSync(t),{injector:this.injector})}requestDropdownSync(e){if(typeof requestAnimationFrame!="function"){this.syncDropdownPanel(e);return}this.dropdownSyncFrame===null&&(this.dropdownSyncFrame=requestAnimationFrame(()=>{this.dropdownSyncFrame=null,this.syncDropdownPanel(e)}))}clearDropdownAnchor(){this.notificationsOpen()||this.loginOpen()||this.userMenuOpen()||(this.dropdownTrigger=null,this.dropdownActionId=null,this.lastDropdownStyleKey="",this.dropdownPanelStyle.set({}))}findActionTrigger(e){const t=this.host.nativeElement.querySelectorAll(`[data-dga-action="${CSS.escape(e)}"]`);for(const o of Array.from(t)){const a=o;if(a.getClientRects().length>0)return a}return t[0]??null}activeUtilityPanelWidthPx(){if(typeof window>"u")return 448;const e=window.innerWidth-32;if(this.loginOpen()){const t=this.loginConfig()?.panelWidth??"24rem";return Math.min(ee(t,384),e)}return this.userMenuOpen()?Math.min(352,e):(this.notificationsOpen(),Math.min(448,e))}syncDropdownPanel(e){if(typeof window>"u")return;if(this.isMobileViewport()){const d={top:"auto",bottom:"max(0.75rem, env(safe-area-inset-bottom, 0px))",left:"0.75rem",right:"0.75rem",width:"auto",maxWidth:"none"},D=`sheet|${d.bottom}`;if(D===this.lastDropdownStyleKey)return;this.lastDropdownStyleKey=D,this.dropdownPanelStyle.set(d);return}const t=e??this.dropdownTrigger??(this.dropdownActionId?this.findActionTrigger(this.dropdownActionId):null);if(!t)return;this.dropdownTrigger=t;const o=t.getBoundingClientRect();if(o.width<1&&o.height<1)return;const a=getComputedStyle(this.host.nativeElement).direction,U=(typeof document<"u"?document.documentElement.getAttribute("dir"):null)==="rtl"||a==="rtl",_=8,N=this.activeUtilityPanelWidthPx(),f={top:`${Math.round(o.bottom+_)}px`};if(U){let d=Math.round(o.left);d=Math.max(16,Math.min(d,window.innerWidth-N-16)),f.left=`${d}px`,f.right="auto"}else{let d=Math.round(window.innerWidth-o.right);d=Math.max(16,Math.min(d,window.innerWidth-N-16)),f.right=`${d}px`,f.left="auto"}const C=`${f.top}|${f.left??""}|${f.right??""}`;C!==this.lastDropdownStyleKey&&(this.lastDropdownStyleKey=C,this.dropdownPanelStyle.set(f))}onKey(e){e.key==="Escape"&&this.closeAll()}onScroll(){(this.notificationsOpen()||this.loginOpen()||this.userMenuOpen())&&this.requestDropdownSync()}onResize(){typeof window>"u"||(window.matchMedia("(max-width: 767px)").matches||(this.mobileOpen.set(!1),this.setBodyScrollLocked(!1)),(this.notificationsOpen()||this.loginOpen()||this.userMenuOpen())&&(this.lastDropdownStyleKey="",this.requestDropdownSync()))}onNavLinkClick(e,t){this.navLinkClick.emit(e),e.href||t.preventDefault()}onDocClick(e){this.searchOpen()||this.notificationsOpen()||this.loginOpen()||this.userMenuOpen()||this.hoverMenus()&&!this.isMobileViewport()||this.mobileOpen()||this.host.nativeElement.contains(e.target)||this.openDropdown.set(null)}static ɵfac=i.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.0.7",ngImport:i,type:v,deps:[],target:i.ɵɵFactoryTarget.Component});static ɵcmp=i.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"22.0.7",type:v,isStandalone:!0,selector:"dga-header",inputs:{brand:{classPropertyName:"brand",publicName:"brand",isSignal:!0,isRequired:!1,transformFunction:null},brandSlogan:{classPropertyName:"brandSlogan",publicName:"brandSlogan",isSignal:!0,isRequired:!1,transformFunction:null},brandLogo:{classPropertyName:"brandLogo",publicName:"brandLogo",isSignal:!0,isRequired:!1,transformFunction:null},brandLogoAlt:{classPropertyName:"brandLogoAlt",publicName:"brandLogoAlt",isSignal:!0,isRequired:!1,transformFunction:null},brandPlaceholder:{classPropertyName:"brandPlaceholder",publicName:"brandPlaceholder",isSignal:!0,isRequired:!1,transformFunction:null},brandHref:{classPropertyName:"brandHref",publicName:"brandHref",isSignal:!0,isRequired:!1,transformFunction:null},navLabel:{classPropertyName:"navLabel",publicName:"navLabel",isSignal:!0,isRequired:!1,transformFunction:null},menuToggleLabel:{classPropertyName:"menuToggleLabel",publicName:"menuToggleLabel",isSignal:!0,isRequired:!1,transformFunction:null},menuCloseLabel:{classPropertyName:"menuCloseLabel",publicName:"menuCloseLabel",isSignal:!0,isRequired:!1,transformFunction:null},appearance:{classPropertyName:"appearance",publicName:"appearance",isSignal:!0,isRequired:!1,transformFunction:null},mobileNav:{classPropertyName:"mobileNav",publicName:"mobileNav",isSignal:!0,isRequired:!1,transformFunction:null},sticky:{classPropertyName:"sticky",publicName:"sticky",isSignal:!0,isRequired:!1,transformFunction:null},items:{classPropertyName:"items",publicName:"items",isSignal:!0,isRequired:!1,transformFunction:null},actions:{classPropertyName:"actions",publicName:"actions",isSignal:!0,isRequired:!1,transformFunction:null},search:{classPropertyName:"search",publicName:"search",isSignal:!0,isRequired:!1,transformFunction:null},searchQuery:{classPropertyName:"searchQuery",publicName:"searchQuery",isSignal:!0,isRequired:!1,transformFunction:null},notifications:{classPropertyName:"notifications",publicName:"notifications",isSignal:!0,isRequired:!1,transformFunction:null},login:{classPropertyName:"login",publicName:"login",isSignal:!0,isRequired:!1,transformFunction:null},user:{classPropertyName:"user",publicName:"user",isSignal:!0,isRequired:!1,transformFunction:null},authenticated:{classPropertyName:"authenticated",publicName:"authenticated",isSignal:!0,isRequired:!1,transformFunction:null},loginSubmitting:{classPropertyName:"loginSubmitting",publicName:"loginSubmitting",isSignal:!0,isRequired:!1,transformFunction:null},loginError:{classPropertyName:"loginError",publicName:"loginError",isSignal:!0,isRequired:!1,transformFunction:null}},outputs:{searchQuery:"searchQueryChange",authenticated:"authenticatedChange",loginSubmitting:"loginSubmittingChange",loginError:"loginErrorChange",actionClick:"actionClick",searchSubmit:"searchSubmit",searchSuggestionSelect:"searchSuggestionSelect",searchVoice:"searchVoice",notificationAction:"notificationAction",loginSubmit:"loginSubmit",logout:"logout",userLinkClick:"userLinkClick",navLinkClick:"navLinkClick"},host:{attributes:{role:"banner"},listeners:{mouseleave:"onHostLeave($event)","document:dga-close-header-overlays":"onCloseHeaderOverlays()","document:keydown":"onKey($event)","window:scroll":"onScroll()","window:resize":"onResize()","document:click":"onDocClick($event)"},properties:{class:"hostClasses()","attr.data-appearance":"appearance()","attr.data-mobile-nav":"mobileNav()","attr.data-mobile-open":"mobileOpen() || null"}},queries:[{propertyName:"customLoginForm",first:!0,predicate:m,descendants:!0,isSignal:!0},{propertyName:"customLoginFooter",first:!0,predicate:p,descendants:!0,isSignal:!0}],viewQueries:[{propertyName:"searchInputRef",first:!0,predicate:["searchInput"],descendants:!0,isSignal:!0},{propertyName:"utilLayerRef",first:!0,predicate:["utilLayer"],descendants:!0,isSignal:!0}],ngImport:i,template:`
2
+ <div [class]="innerClasses()">
3
+ <div class="relative flex min-w-0 shrink-0 items-center self-center">
4
+ <ng-content select="[dgaHeaderBrand]" />
5
+ @if (brand() || brandLogo()) {
6
+ <a
7
+ class="inline-flex h-(--size-header-logo) cursor-pointer items-center gap-lg font-sans
8
+ focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2
9
+ focus-visible:outline-focus"
10
+ [href]="appHref(brandHref())"
11
+ >
12
+ @if (brandLogo()) {
13
+ <img
14
+ class="h-(--size-header-logo) w-auto max-w-[min(100%,14rem)] shrink-0 object-contain"
15
+ [src]="brandLogo()"
16
+ [alt]="brandLogoAlt()"
17
+ height="58"
18
+ />
19
+ } @else if (appearance() === 'portal' && brandPlaceholder()) {
20
+ <span
21
+ class="inline-flex size-(--size-header-logo) shrink-0 items-center justify-center rounded-sm
22
+ bg-primary-faint text-fg-primary"
23
+ aria-hidden="true"
24
+ >
25
+ <dga-icon name="home" size="xl" tone="primary" />
26
+ </span>
27
+ }
28
+ @if (brand() && !brandLogo()) {
29
+ <span class="flex min-w-0 flex-col gap-xxs">
30
+ <span class="text-body-md font-bold text-display">{{ brand() }}</span>
31
+ @if (brandSlogan()) {
32
+ <span class="text-body-sm font-normal text-muted">{{ brandSlogan() }}</span>
33
+ }
34
+ </span>
35
+ }
36
+ </a>
37
+ }
38
+ </div>
39
+
40
+ @if (isPortal() && items().length) {
41
+ <button
42
+ type="button"
43
+ class="dga-burger relative z-30 ms-auto hidden h-full w-6xl shrink-0 cursor-pointer
44
+ items-center justify-center self-stretch rounded-md text-fg transition-colors
45
+ hover:bg-neutral-light max-md:inline-flex
46
+ focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2
47
+ focus-visible:outline-focus"
48
+ [class.dga-burger--open]="mobileOpen()"
49
+ [class.dga-burger--hidden]="mobileOpen() && mobileNav() !== 'push'"
50
+ [attr.aria-expanded]="mobileOpen()"
51
+ [attr.aria-controls]="collapseId"
52
+ [attr.aria-label]="mobileOpen() ? menuCloseLabel() : menuToggleLabel()"
53
+ (click)="toggleMobile()"
54
+ >
55
+ <span class="dga-burger__box" aria-hidden="true">
56
+ <span class="dga-burger__line"></span>
57
+ <span class="dga-burger__line"></span>
58
+ <span class="dga-burger__line"></span>
59
+ </span>
60
+ </button>
61
+ }
62
+
63
+ <!-- Desktop navigation -->
64
+ <nav
65
+ [class]="desktopNavClasses()"
66
+ [attr.aria-label]="navLabel()"
67
+ [attr.hidden]="null"
68
+ >
69
+ @if (items().length) {
70
+ <ul
71
+ class="m-0 flex list-none flex-row flex-nowrap items-stretch gap-none p-0"
72
+ [class.overflow-x-auto]="openDropdown() === null"
73
+ [class.overflow-visible]="openDropdown() !== null"
74
+ role="list"
75
+ >
76
+ @for (item of items(); track item.label; let i = $index) {
77
+ <li
78
+ class="relative"
79
+ [class.z-dropdown]="openDropdown() === i"
80
+ [attr.data-open]="openDropdown() === i || null"
81
+ [attr.data-variant]="item.dropdown ? resolveVariant(item) : null"
82
+ (mouseenter)="onItemEnter(i)"
83
+ (focusin)="onItemEnter(i)"
84
+ >
85
+ @if (item.dropdown) {
86
+ <button
87
+ type="button"
88
+ [class]="navItemClass(!!item.current, openDropdown() === i)"
89
+ [attr.aria-expanded]="openDropdown() === i"
90
+ [attr.aria-haspopup]="'true'"
91
+ (click)="toggleDropdown(i, $event)"
92
+ >
93
+ <span
94
+ aria-hidden="true"
95
+ [class]="indicatorClass(!!item.current || openDropdown() === i)"
96
+ ></span>
97
+ <span class="whitespace-nowrap">{{ item.label }}</span>
98
+ <dga-icon
99
+ name="chevronDown"
100
+ size="md"
101
+ [class]="
102
+ openDropdown() === i
103
+ ? 'rotate-180 transition-transform duration-200'
104
+ : 'transition-transform duration-200'
105
+ "
106
+ />
107
+ </button>
108
+
109
+ @if (openDropdown() === i && resolveVariant(item) === 'menu') {
110
+ <div
111
+ class="absolute start-0 top-full z-dropdown min-w-[16rem]
112
+ max-w-[min(100vw-2rem,20rem)]"
113
+ role="menu"
114
+ [attr.aria-label]="item.label"
115
+ >
116
+ <div class="h-xs" aria-hidden="true"></div>
117
+ <div
118
+ class="dga-panel-enter rounded-md border border-border bg-card p-sm
119
+ shadow-lg"
120
+ >
121
+ @if (item.dropdown.columns?.length) {
122
+ <div class="flex flex-col gap-lg p-md">
123
+ @for (col of item.dropdown.columns!; track col.title ?? $index) {
124
+ <div class="flex flex-col gap-xs">
125
+ @if (col.title) {
126
+ <p class="px-xl text-body-sm font-medium text-fg-primary">
127
+ {{ col.title }}
128
+ </p>
129
+ }
130
+ <ul class="m-0 flex list-none flex-col gap-none p-0" role="list">
131
+ @for (link of col.links; track link.label) {
132
+ <li role="none">
133
+ <a
134
+ role="menuitem"
135
+ [class]="dropLink"
136
+ [href]="appHref(link.href || '#')"
137
+ (click)="onNavLinkClick(link, $event)"
138
+ >{{ link.label }}</a
139
+ >
140
+ </li>
141
+ }
142
+ </ul>
143
+ </div>
144
+ }
145
+ </div>
146
+ } @else {
147
+ <ul class="m-0 flex list-none flex-col gap-none p-0" role="list">
148
+ @for (link of item.dropdown.links || []; track link.label) {
149
+ <li role="none">
150
+ <a
151
+ role="menuitem"
152
+ [class]="dropLink"
153
+ [href]="appHref(link.href || '#')"
154
+ (click)="onNavLinkClick(link, $event)"
155
+ >{{ link.label }}</a
156
+ >
157
+ </li>
158
+ }
159
+ </ul>
160
+ }
161
+ </div>
162
+ </div>
163
+ }
164
+ } @else {
165
+ <a
166
+ [class]="navItemClass(!!item.current)"
167
+ [attr.aria-current]="item.current ? 'page' : null"
168
+ [href]="appHref(item.href || '#')"
169
+ (click)="onNavLinkClick(item, $event)"
170
+ >
171
+ <span aria-hidden="true" [class]="indicatorClass(!!item.current)"></span>
172
+ <span class="whitespace-nowrap">{{ item.label }}</span>
173
+ </a>
174
+ }
175
+ </li>
176
+ }
177
+ </ul>
178
+ }
179
+ <div class="contents">
180
+ <ng-content />
181
+ </div>
182
+ </nav>
183
+
184
+ <div [class]="actionsClasses()">
185
+ @for (action of actions(); track action.id) {
186
+ <dga-tooltip
187
+ class="relative flex h-full"
188
+ [text]="actionTooltip(action)"
189
+ placement="bottom"
190
+ [showIcon]="false"
191
+ [disabled]="!isIconOnly(action) && !(isLoginAction(action) && authenticated())"
192
+ >
193
+ @if (action.href) {
194
+ <a
195
+ [class]="actionClass(action)"
196
+ [href]="appHref(action.href)"
197
+ [attr.aria-label]="actionAriaLabel(action)"
198
+ [attr.data-dga-action]="action.id"
199
+ >
200
+ <span aria-hidden="true" [class]="indicatorClass(false)"></span>
201
+ @if (action.icon) {
202
+ <dga-icon [name]="action.icon" size="xl" />
203
+ }
204
+ @if (!isIconOnly(action)) {
205
+ <span>{{ action.label }}</span>
206
+ }
207
+ @if (action.badge) {
208
+ <span [class]="badgeClass()">{{ action.badge }}</span>
209
+ }
210
+ </a>
211
+ } @else {
212
+ <button
213
+ type="button"
214
+ [class]="actionClass(action)"
215
+ [attr.aria-label]="actionAriaLabel(action)"
216
+ [attr.aria-expanded]="actionExpanded(action)"
217
+ [attr.aria-controls]="actionControls(action)"
218
+ [attr.data-dga-action]="action.id"
219
+ (click)="onAction(action, $event)"
220
+ >
221
+ <span
222
+ aria-hidden="true"
223
+ [class]="indicatorClass(actionIndicatorActive(action))"
224
+ ></span>
225
+ @if (isLoginAction(action) && authenticated() && userConfig(); as u) {
226
+ <dga-avatar [name]="u.name" [src]="u.avatarSrc" size="sm" />
227
+ <span class="hidden max-w-11xl truncate lg:inline">{{ u.name }}</span>
228
+ } @else {
229
+ @if (action.icon) {
230
+ <dga-icon [name]="action.icon" size="xl" />
231
+ }
232
+ @if (!isIconOnly(action)) {
233
+ <span>{{ action.label }}</span>
234
+ }
235
+ }
236
+ @if (action.badge) {
237
+ <span [class]="badgeClass()">{{ action.badge }}</span>
238
+ }
239
+ </button>
240
+ }
241
+ </dga-tooltip>
242
+ }
243
+ <ng-content select="[dgaHeaderEnd]" />
244
+ </div>
245
+ </div>
246
+
247
+ <!-- Desktop mega / featured -->
248
+ @if (activeWideDropdown(); as drop) {
249
+ <div
250
+ class="absolute start-0 end-0 top-full z-dropdown hidden w-full md:block"
251
+ role="region"
252
+ [attr.aria-label]="drop.item.label"
253
+ [attr.data-variant]="drop.variant"
254
+ (mouseenter)="onPanelEnter()"
255
+ >
256
+ <div
257
+ class="dga-panel-enter border-b border-border bg-card shadow-lg md:rounded-b-lg"
258
+ >
259
+ <div class="w-full px-(--wrapper-padding) py-2xl lg:py-3xl">
260
+ <div [class]="featuredGridClass(drop.variant, !!drop.item.dropdown!.featured)">
261
+ <div class="min-w-0">
262
+ @switch (resolveLayout(drop.item)) {
263
+ @case ('columns') {
264
+ <div [class]="columnsGridClass(colCount(drop.item))">
265
+ @for (
266
+ col of drop.item.dropdown!.columns!;
267
+ track col.title ?? $index
268
+ ) {
269
+ <div class="flex min-w-0 flex-col gap-md">
270
+ @if (col.title) {
271
+ <p
272
+ class="border-b border-border-subtle pb-sm text-body-lg
273
+ font-medium text-fg-primary"
274
+ >
275
+ {{ col.title }}
276
+ </p>
277
+ }
278
+ <ul class="m-0 flex list-none flex-col gap-none p-0" role="list">
279
+ @for (link of col.links; track link.label) {
280
+ <li>
281
+ <a
282
+ [class]="dropLink"
283
+ [href]="appHref(link.href || '#')"
284
+ (click)="onNavLinkClick(link, $event)"
285
+ >
286
+ <span>{{ link.label }}</span>
287
+ @if (link.description) {
288
+ <span class="text-body-sm text-muted">{{
289
+ link.description
290
+ }}</span>
291
+ }
292
+ </a>
293
+ </li>
294
+ }
295
+ </ul>
296
+ </div>
297
+ }
298
+ </div>
299
+ }
300
+ @case ('rows') {
301
+ <ul
302
+ class="m-0 grid w-full list-none grid-cols-1 gap-md p-0
303
+ sm:grid-cols-2 lg:grid-cols-4"
304
+ role="list"
305
+ >
306
+ @for (link of drop.item.dropdown!.links || []; track link.label) {
307
+ <li class="min-w-0">
308
+ <a
309
+ [class]="dropTile"
310
+ [href]="appHref(link.href || '#')"
311
+ (click)="onNavLinkClick(link, $event)"
312
+ >
313
+ <span class="text-body-md font-medium text-fg">{{
314
+ link.label
315
+ }}</span>
316
+ @if (link.description) {
317
+ <span class="text-body-sm text-muted">{{
318
+ link.description
319
+ }}</span>
320
+ }
321
+ </a>
322
+ </li>
323
+ }
324
+ </ul>
325
+ }
326
+ @default {
327
+ <ul
328
+ class="m-0 grid w-full max-w-(--container-5xl) list-none grid-cols-1 gap-xs
329
+ p-0 sm:grid-cols-2 lg:grid-cols-3"
330
+ role="list"
331
+ >
332
+ @for (link of flatLinks(drop.item); track link.label) {
333
+ <li class="min-w-0">
334
+ <a
335
+ [class]="dropLink"
336
+ [href]="appHref(link.href || '#')"
337
+ (click)="onNavLinkClick(link, $event)"
338
+ >
339
+ <span>{{ link.label }}</span>
340
+ @if (link.description) {
341
+ <span class="text-body-sm text-muted">{{
342
+ link.description
343
+ }}</span>
344
+ }
345
+ </a>
346
+ </li>
347
+ }
348
+ </ul>
349
+ }
350
+ }
351
+ </div>
352
+
353
+ @if (drop.variant === 'featured' && drop.item.dropdown!.featured; as feat) {
354
+ <aside
355
+ class="flex min-w-0 flex-col gap-lg overflow-hidden rounded-md
356
+ border border-border bg-primary-faint p-2xl lg:self-start"
357
+ >
358
+ @if (feat.imageUrl) {
359
+ <img
360
+ class="aspect-[16/10] h-auto w-full rounded-sm object-cover"
361
+ [src]="feat.imageUrl"
362
+ alt=""
363
+ />
364
+ }
365
+ <div class="flex min-w-0 flex-col gap-md">
366
+ <p class="text-body-lg font-semibold text-display">{{ feat.title }}</p>
367
+ @if (feat.description) {
368
+ <p class="text-body-sm leading-relaxed text-paragraph">
369
+ {{ feat.description }}
370
+ </p>
371
+ }
372
+ @if (feat.cta) {
373
+ <a
374
+ class="mt-xs inline-flex h-5xl cursor-pointer items-center justify-center gap-sm
375
+ rounded-sm bg-primary px-xl text-body-md font-medium text-on-color
376
+ transition-opacity hover:opacity-90
377
+ focus-visible:outline focus-visible:outline-2
378
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
379
+ [href]="appHref(feat.href || '#')"
380
+ >
381
+ <span>{{ feat.cta }}</span>
382
+ <dga-icon name="chevronLeft" size="sm" />
383
+ </a>
384
+ }
385
+ </div>
386
+ </aside>
387
+ }
388
+ </div>
389
+ </div>
390
+ </div>
391
+ </div>
392
+ }
393
+
394
+ <!-- Header search popup (dga-modal) -->
395
+ @if (searchConfig(); as cfg) {
396
+ <dga-modal
397
+ class="dga-header-search-modal"
398
+ [id]="searchPanelId"
399
+ [open]="searchOpen()"
400
+ (openChange)="onSearchModalOpenChange($event)"
401
+ [title]="cfg.title || cfg.submitLabel || 'Search'"
402
+ [description]="cfg.description || ''"
403
+ [closeLabel]="cfg.closeLabel || 'Close'"
404
+ size="lg"
405
+ animation="scale"
406
+ [scrollable]="false"
407
+ (opened)="focusSearchInput()"
408
+ >
409
+ <form
410
+ class="flex flex-col gap-xl"
411
+ role="search"
412
+ [attr.aria-label]="cfg.submitLabel || 'Search'"
413
+ (submit)="onSearchFormSubmit($event)"
414
+ >
415
+ <div class="flex w-full flex-col gap-md sm:flex-row sm:items-stretch sm:gap-lg">
416
+ <div class="min-w-0 flex-1">
417
+ <dga-form-control size="lg" startIcon="search" [filled]="!!searchQuery()">
418
+ <input
419
+ #searchInput
420
+ type="search"
421
+ name="q"
422
+ class="dga-search-input placeholder:text-muted"
423
+ [placeholder]="cfg.placeholder || 'Search...'"
424
+ [attr.aria-label]="cfg.placeholder || 'Search...'"
425
+ [value]="searchQuery()"
426
+ (input)="searchQuery.set($any($event.target).value)"
427
+ />
428
+ @if (searchQuery()) {
429
+ <button
430
+ type="button"
431
+ dgaEnd
432
+ [attr.aria-label]="cfg.clearLabel || 'Clear search'"
433
+ (click)="clearSearchQuery()"
434
+ >
435
+ <dga-icon name="close" size="sm" />
436
+ </button>
437
+ }
438
+ @if (cfg.voice !== false) {
439
+ <button
440
+ type="button"
441
+ dgaEnd
442
+ class="dga-search-voice"
443
+ [class.dga-search-voice--active]="voiceListening()"
444
+ [attr.aria-label]="cfg.voiceLabel || 'Voice input'"
445
+ [attr.aria-pressed]="voiceListening()"
446
+ (click)="onSearchVoice()"
447
+ >
448
+ <dga-icon
449
+ name="mic"
450
+ size="sm"
451
+ [tone]="voiceListening() ? 'current' : 'muted'"
452
+ />
453
+ </button>
454
+ }
455
+ </dga-form-control>
456
+ </div>
457
+ <button
458
+ type="submit"
459
+ class="inline-flex min-h-6xl shrink-0 cursor-pointer items-center justify-center gap-sm
460
+ rounded-md bg-primary px-xl text-body-md font-medium text-on-color shadow-sm
461
+ transition-opacity hover:opacity-90
462
+ focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2
463
+ focus-visible:outline-focus sm:px-2xl"
464
+ >
465
+ <dga-icon name="search" size="md" />
466
+ <span>{{ cfg.submitLabel || 'Search' }}</span>
467
+ </button>
468
+ </div>
469
+
470
+ @if (cfg.suggestions?.length) {
471
+ <div class="flex flex-col gap-md">
472
+ <span class="text-body-sm font-semibold text-display">
473
+ {{ cfg.suggestionsLabel || 'Suggestions:' }}
474
+ </span>
475
+ <div class="flex flex-wrap gap-sm">
476
+ @for (item of cfg.suggestions!; track item.value ?? item.label; let i = $index) {
477
+ <button
478
+ type="button"
479
+ class="dga-search-chip inline-flex h-4xl cursor-pointer items-center gap-xs
480
+ rounded-full border border-border bg-neutral-light px-lg text-body-sm
481
+ text-paragraph transition-[background-color,transform,border-color]
482
+ duration-150 ease-out hover:-translate-y-px hover:border-primary
483
+ hover:bg-sunken
484
+ focus-visible:outline focus-visible:outline-2
485
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
486
+ [style.animation-delay]="i * 40 + 'ms'"
487
+ (click)="applySearchSuggestion(item)"
488
+ >
489
+ <dga-icon [name]="item.icon || 'plus'" size="xs" />
490
+ <span>{{ item.label }}</span>
491
+ </button>
492
+ }
493
+ </div>
494
+ </div>
495
+ }
496
+ </form>
497
+ </dga-modal>
498
+ }
499
+
500
+ <!--
501
+ Utility overlays (notifications / login / user): portaled to body at z-urgent
502
+ so sticky announcement + chatbot/a11y FABs stay underneath.
503
+ -->
504
+ @if (utilityOverlayOpen()) {
505
+ <div #utilLayer class="dga-header-util-layer">
506
+ <button
507
+ type="button"
508
+ class="dga-util-backdrop absolute inset-0 cursor-default bg-overlay/50"
509
+ tabindex="-1"
510
+ aria-label="Close"
511
+ (click)="closeUtilityOverlays()"
512
+ ></button>
513
+
514
+ <!-- Header notifications panel -->
515
+ @if (notificationsOpen() && notificationsConfig(); as ncfg) {
516
+ <div
517
+ [id]="notificationsPanelId"
518
+ class="dga-header-util-panel fixed w-[min(28rem,calc(100vw-2rem))] max-md:inset-x-3
519
+ max-md:bottom-3 max-md:top-auto max-md:w-auto"
520
+ role="region"
521
+ [attr.aria-label]="ncfg.title || ncfg.viewAllLabel || 'Notifications'"
522
+ [style]="dropdownPanelStyle()"
523
+ >
524
+ <div
525
+ class="dga-panel-enter dga-panel-enter--anchor-end flex max-h-[min(85svh,40rem)]
526
+ flex-col overflow-hidden rounded-2xl border border-border bg-card shadow-xl
527
+ md:max-h-[none] md:rounded-md"
528
+ >
529
+ <div
530
+ class="flex shrink-0 flex-col gap-sm border-b border-border-subtle px-xl py-md
531
+ md:flex-row md:items-center md:justify-between md:gap-md"
532
+ >
533
+ <div class="flex min-w-0 items-center justify-between gap-sm md:justify-start">
534
+ <div class="flex min-w-0 items-center gap-sm">
535
+ <h2 class="m-0 truncate text-body-lg font-semibold text-display md:text-body-md">
536
+ {{ ncfg.title || 'Notifications' }}
537
+ </h2>
538
+ @if (unreadNotificationCount() > 0) {
539
+ <span
540
+ class="inline-flex min-w-4xl items-center justify-center rounded-full
541
+ bg-error px-sm py-xxs text-body-2xs font-semibold text-on-color"
542
+ >
543
+ {{ unreadNotificationCount() }}
544
+ </span>
545
+ }
546
+ </div>
547
+ <button
548
+ type="button"
549
+ class="inline-flex size-5xl shrink-0 cursor-pointer items-center justify-center
550
+ rounded-sm text-muted transition-colors hover:bg-neutral-light hover:text-fg
551
+ focus-visible:outline focus-visible:outline-2
552
+ focus-visible:outline-offset-2 focus-visible:outline-focus md:hidden"
553
+ [attr.aria-label]="ncfg.closeLabel || 'Close'"
554
+ (click)="closeNotifications()"
555
+ >
556
+ <dga-icon name="close" size="md" />
557
+ </button>
558
+ </div>
559
+ <div class="flex shrink-0 items-center gap-xxs max-md:justify-stretch">
560
+ @if (unreadNotificationCount() > 0) {
561
+ <button
562
+ type="button"
563
+ class="inline-flex h-4xl flex-1 cursor-pointer items-center justify-center
564
+ rounded-sm px-md text-body-sm font-medium text-fg-primary
565
+ transition-colors hover:bg-primary-faint
566
+ focus-visible:outline focus-visible:outline-2
567
+ focus-visible:outline-offset-2 focus-visible:outline-focus
568
+ md:flex-none"
569
+ (click)="markAllNotificationsRead()"
570
+ >
571
+ {{ ncfg.markAllReadLabel || 'Mark all as read' }}
572
+ </button>
573
+ }
574
+ <button
575
+ type="button"
576
+ class="hidden size-5xl cursor-pointer items-center justify-center rounded-sm
577
+ text-muted transition-colors hover:bg-neutral-light hover:text-fg
578
+ focus-visible:outline focus-visible:outline-2
579
+ focus-visible:outline-offset-2 focus-visible:outline-focus md:inline-flex"
580
+ [attr.aria-label]="ncfg.closeLabel || 'Close'"
581
+ (click)="closeNotifications()"
582
+ >
583
+ <dga-icon name="close" size="md" />
584
+ </button>
585
+ </div>
586
+ </div>
587
+
588
+ <div class="min-h-0 flex-1 overflow-y-auto overscroll-contain md:max-h-[min(48svh,28rem)]">
589
+ @if (visibleNotifications().length === 0) {
590
+ <div class="flex flex-col items-center gap-md px-xl py-3xl text-center">
591
+ <span
592
+ class="inline-flex size-6xl items-center justify-center rounded-full
593
+ bg-neutral-light text-muted"
594
+ aria-hidden="true"
595
+ >
596
+ <dga-icon name="bell" size="xl" tone="muted" />
597
+ </span>
598
+ <p class="m-0 text-body-sm text-muted">
599
+ {{ ncfg.emptyLabel || 'No notifications' }}
600
+ </p>
601
+ </div>
602
+ } @else {
603
+ <ul class="m-0 flex list-none flex-col p-0" role="list">
604
+ @for (item of visibleNotifications(); track item.id) {
605
+ <li
606
+ class="border-b border-border-subtle last:border-b-0"
607
+ [class]="item.read ? 'bg-surface' : 'bg-card'"
608
+ >
609
+ <button
610
+ type="button"
611
+ class="flex w-full cursor-pointer items-start gap-lg px-xl py-lg text-start
612
+ transition-colors hover:bg-neutral-light
613
+ focus-visible:outline focus-visible:outline-2
614
+ focus-visible:outline-offset-[-2px] focus-visible:outline-focus"
615
+ [attr.aria-expanded]="expandedNotificationId() === item.id"
616
+ (click)="toggleNotificationExpand(item)"
617
+ >
618
+ <span class="relative mt-xxs shrink-0">
619
+ <dga-icon-badge size="sm">
620
+ <dga-icon [name]="item.icon || 'bell'" size="sm" />
621
+ </dga-icon-badge>
622
+ @if (!item.read) {
623
+ <span
624
+ class="absolute -end-xxs -top-xxs size-[0.5rem] rounded-full bg-primary
625
+ ring-2 ring-white"
626
+ aria-hidden="true"
627
+ ></span>
628
+ }
629
+ </span>
630
+ <span class="min-w-0 flex-1">
631
+ <span class="mb-xs flex flex-wrap items-center gap-sm">
632
+ @if (item.statusLabel) {
633
+ <dga-badge [variant]="item.tone || 'neutral'" size="sm">
634
+ {{ item.statusLabel }}
635
+ </dga-badge>
636
+ }
637
+ <span
638
+ class="min-w-0 truncate text-body-md text-display"
639
+ [class.font-semibold]="!item.read"
640
+ [class.font-medium]="item.read"
641
+ [class.text-muted]="item.read"
642
+ >
643
+ {{ item.title }}
644
+ </span>
645
+ </span>
646
+ <span
647
+ class="text-body-sm text-paragraph"
648
+ [class.line-clamp-2]="expandedNotificationId() !== item.id"
649
+ >
650
+ {{ item.message }}
651
+ </span>
652
+ </span>
653
+ <dga-icon
654
+ class="mt-xs shrink-0 text-muted transition-transform duration-200"
655
+ [class.rotate-180]="expandedNotificationId() === item.id"
656
+ name="chevronDown"
657
+ size="sm"
658
+ />
659
+ </button>
660
+
661
+ @if (expandedNotificationId() === item.id) {
662
+ <div
663
+ class="dga-notif-actions flex flex-wrap gap-sm border-t border-border-subtle
664
+ bg-surface px-xl py-md"
665
+ >
666
+ @if (!item.read) {
667
+ <button
668
+ type="button"
669
+ class="inline-flex h-4xl cursor-pointer items-center gap-xs rounded-sm
670
+ px-md text-body-sm text-fg transition-colors hover:bg-neutral-light
671
+ focus-visible:outline focus-visible:outline-2
672
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
673
+ (click)="onNotificationAction(item, 'read'); $event.stopPropagation()"
674
+ >
675
+ <dga-icon name="success" size="sm" />
676
+ <span>{{ ncfg.markReadLabel || 'Mark as read' }}</span>
677
+ </button>
678
+ }
679
+ <button
680
+ type="button"
681
+ class="inline-flex h-4xl cursor-pointer items-center gap-xs rounded-sm
682
+ px-md text-body-sm text-fg transition-colors hover:bg-neutral-light
683
+ focus-visible:outline focus-visible:outline-2
684
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
685
+ (click)="onNotificationAction(item, 'view'); $event.stopPropagation()"
686
+ >
687
+ <dga-icon name="eye" size="sm" />
688
+ <span>{{ ncfg.viewLabel || 'View' }}</span>
689
+ </button>
690
+ <button
691
+ type="button"
692
+ class="inline-flex h-4xl cursor-pointer items-center gap-xs rounded-sm
693
+ px-md text-body-sm text-fg-error transition-colors hover:bg-error-light
694
+ focus-visible:outline focus-visible:outline-2
695
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
696
+ (click)="onNotificationAction(item, 'dismiss'); $event.stopPropagation()"
697
+ >
698
+ <dga-icon name="close" size="sm" />
699
+ <span>{{ ncfg.dismissLabel || 'Dismiss' }}</span>
700
+ </button>
701
+ </div>
702
+ }
703
+ </li>
704
+ }
705
+ </ul>
706
+ }
707
+ </div>
708
+
709
+ @if (ncfg.viewAllHref || ncfg.viewAllLabel) {
710
+ <div
711
+ class="shrink-0 border-t border-border
712
+ pb-[max(0px,env(safe-area-inset-bottom))] md:pb-0"
713
+ >
714
+ <a
715
+ class="inline-flex w-full cursor-pointer items-center justify-center gap-sm
716
+ px-xl py-lg text-body-sm font-medium text-fg-primary
717
+ transition-colors hover:bg-neutral-light
718
+ focus-visible:outline focus-visible:outline-2
719
+ focus-visible:outline-offset-[-2px] focus-visible:outline-focus"
720
+ [href]="appHref(ncfg.viewAllHref || '#')"
721
+ (click)="closeNotifications()"
722
+ >
723
+ <span>{{ ncfg.viewAllLabel || 'View all notifications' }}</span>
724
+ <dga-icon name="chevronLeft" size="sm" />
725
+ </a>
726
+ </div>
727
+ }
728
+ </div>
729
+ </div>
730
+ }
731
+
732
+ <!-- Header login panel (guest) -->
733
+ @if (loginOpen() && loginConfig(); as lcfg) {
734
+ <div
735
+ [id]="loginPanelId"
736
+ class="dga-header-util-panel fixed max-md:inset-x-3 max-md:bottom-3 max-md:top-auto
737
+ max-md:w-auto"
738
+ role="dialog"
739
+ [attr.aria-label]="lcfg.title || 'Login'"
740
+ [style]="loginPanelOuterStyle()"
741
+ >
742
+ <div
743
+ class="dga-panel-enter dga-panel-enter--anchor-end overflow-y-auto overscroll-contain
744
+ rounded-2xl border border-border bg-card shadow-xl md:overflow-visible
745
+ md:rounded-md"
746
+ [style]="loginPanelInnerStyle()"
747
+ >
748
+ <div class="flex items-start justify-between gap-md px-2xl pt-2xl max-md:pt-xl">
749
+ <div class="flex min-w-0 flex-col gap-xs">
750
+ <h2 class="m-0 text-body-lg font-semibold text-display">
751
+ {{ lcfg.title || 'Login' }}
752
+ </h2>
753
+ <p class="m-0 text-body-sm text-paragraph">
754
+ {{ lcfg.description || 'Sign in to access your account' }}
755
+ </p>
756
+ </div>
757
+ <button
758
+ type="button"
759
+ class="inline-flex size-5xl shrink-0 cursor-pointer items-center justify-center
760
+ rounded-sm text-muted transition-colors hover:bg-neutral-light hover:text-fg
761
+ focus-visible:outline focus-visible:outline-2
762
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
763
+ [attr.aria-label]="lcfg.closeLabel || lcfg.cancelLabel || 'Close'"
764
+ (click)="closeLogin()"
765
+ >
766
+ <dga-icon name="close" size="md" />
767
+ </button>
768
+ </div>
769
+
770
+ <ng-content select="[dgaHeaderLoginBeforeForm]" />
771
+
772
+ @if (hasCustomLoginForm()) {
773
+ <div
774
+ class="px-2xl pb-[max(1.5rem,env(safe-area-inset-bottom))] pt-xl md:pb-2xl"
775
+ >
776
+ <ng-content select="[dgaHeaderLoginForm]" />
777
+ </div>
778
+ } @else {
779
+ <form
780
+ class="flex flex-col gap-xl px-2xl pb-[max(1.5rem,env(safe-area-inset-bottom))]
781
+ pt-xl md:pb-2xl"
782
+ (submit)="onLoginSubmit($event)"
783
+ >
784
+ <dga-field
785
+ [label]="lcfg.emailLabel || 'Email'"
786
+ controlId="dga-header-login-email"
787
+ [hint]="loginEmailError() ? '' : lcfg.emailHint || ''"
788
+ [error]="loginEmailError()"
789
+ required
790
+ >
791
+ <dga-form-control startIcon="mail" [filled]="!!loginEmail()">
792
+ <input
793
+ id="dga-header-login-email"
794
+ type="email"
795
+ name="email"
796
+ autocomplete="username"
797
+ required
798
+ class="placeholder:text-muted"
799
+ [placeholder]="lcfg.emailPlaceholder || 'name@example.gov.sa'"
800
+ [value]="loginEmail()"
801
+ (input)="onLoginEmailInput($any($event.target).value)"
802
+ />
803
+ </dga-form-control>
804
+ </dga-field>
805
+
806
+ <dga-field
807
+ [label]="lcfg.passwordLabel || 'Password'"
808
+ controlId="dga-header-login-password"
809
+ [hint]="loginPasswordError() ? '' : lcfg.passwordHint || ''"
810
+ [error]="loginPasswordError()"
811
+ required
812
+ >
813
+ <dga-form-control [filled]="!!loginPassword()">
814
+ <input
815
+ id="dga-header-login-password"
816
+ [type]="showLoginPassword() ? 'text' : 'password'"
817
+ name="password"
818
+ autocomplete="current-password"
819
+ required
820
+ class="placeholder:text-muted"
821
+ [placeholder]="lcfg.passwordPlaceholder || 'Enter your password'"
822
+ [value]="loginPassword()"
823
+ (input)="onLoginPasswordInput($any($event.target).value)"
824
+ />
825
+ <button
826
+ type="button"
827
+ dgaEnd
828
+ [attr.aria-label]="
829
+ showLoginPassword()
830
+ ? lcfg.hidePasswordLabel || 'Hide password'
831
+ : lcfg.showPasswordLabel || 'Show password'
832
+ "
833
+ (click)="showLoginPassword.set(!showLoginPassword())"
834
+ >
835
+ <dga-icon [name]="showLoginPassword() ? 'eyeOff' : 'eye'" size="sm" />
836
+ </button>
837
+ </dga-form-control>
838
+ </dga-field>
839
+
840
+ @if (lcfg.forgotPasswordHref || lcfg.forgotPasswordLabel) {
841
+ <a
842
+ class="self-start text-body-sm font-medium text-fg-primary transition-opacity
843
+ hover:opacity-80
844
+ focus-visible:outline focus-visible:outline-2
845
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
846
+ [href]="appHref(lcfg.forgotPasswordHref || '#')"
847
+ >
848
+ {{ lcfg.forgotPasswordLabel || 'Forgot password?' }}
849
+ </a>
850
+ }
851
+
852
+ <ng-content select="[dgaHeaderLoginAfterFields]" />
853
+
854
+ @if (loginError() || lcfg.errorMessage) {
855
+ <p class="m-0 text-body-sm text-fg-error" role="alert">
856
+ {{ loginError() || lcfg.errorMessage }}
857
+ </p>
858
+ }
859
+
860
+ <div class="flex flex-col gap-md pt-xs">
861
+ <button
862
+ type="submit"
863
+ class="inline-flex h-5xl w-full cursor-pointer items-center justify-center gap-sm
864
+ rounded-sm bg-primary px-xl text-body-md font-medium text-on-color shadow-sm
865
+ transition-opacity hover:opacity-90 disabled:cursor-wait disabled:opacity-60
866
+ focus-visible:outline focus-visible:outline-2
867
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
868
+ [disabled]="loginSubmitting()"
869
+ >
870
+ @if (loginSubmitting()) {
871
+ <span
872
+ class="dga-login-spinner size-md rounded-full border-2 border-on-color/30
873
+ border-t-on-color"
874
+ aria-hidden="true"
875
+ ></span>
876
+ }
877
+ <span>
878
+ {{
879
+ loginSubmitting()
880
+ ? lcfg.submittingLabel || '…'
881
+ : lcfg.submitLabel || 'Login'
882
+ }}
883
+ </span>
884
+ </button>
885
+ @if (lcfg.showCancel !== false) {
886
+ <button
887
+ type="button"
888
+ class="inline-flex h-5xl w-full cursor-pointer items-center justify-center
889
+ rounded-sm px-xl text-body-md font-medium text-fg transition-colors
890
+ hover:bg-neutral-light
891
+ focus-visible:outline focus-visible:outline-2
892
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
893
+ (click)="closeLogin()"
894
+ >
895
+ {{ lcfg.cancelLabel || 'Cancel' }}
896
+ </button>
897
+ }
898
+ </div>
899
+ </form>
900
+ }
901
+
902
+ @if (hasCustomLoginFooter()) {
903
+ <div
904
+ class="border-t border-border-subtle px-2xl py-xl pb-[max(1.5rem,env(safe-area-inset-bottom))]"
905
+ >
906
+ <ng-content select="[dgaHeaderLoginFooter]" />
907
+ </div>
908
+ } @else if (showLoginRegisterRow(lcfg) || (lcfg.footerLinks?.length ?? 0) > 0) {
909
+ <div
910
+ class="flex flex-col gap-md border-t border-border-subtle px-2xl py-xl
911
+ pb-[max(1.5rem,env(safe-area-inset-bottom))]"
912
+ >
913
+ @if (showLoginRegisterRow(lcfg)) {
914
+ <p class="m-0 text-center text-body-sm text-muted">
915
+ {{ lcfg.registerPrompt || "Don't have an account?" }}
916
+ @if (lcfg.registerLabel || lcfg.registerHref) {
917
+ <a
918
+ class="ms-xs font-medium text-fg-primary transition-opacity hover:opacity-80
919
+ focus-visible:outline focus-visible:outline-2
920
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
921
+ [href]="appHref(lcfg.registerHref || '#')"
922
+ >
923
+ {{ lcfg.registerLabel || 'Sign up' }}
924
+ </a>
925
+ }
926
+ </p>
927
+ }
928
+ @if (lcfg.footerLinks?.length) {
929
+ <div class="flex flex-col gap-xs">
930
+ @for (link of lcfg.footerLinks; track link.id ?? link.label) {
931
+ <a
932
+ class="text-center text-body-sm font-medium text-fg-primary transition-opacity
933
+ hover:opacity-80
934
+ focus-visible:outline focus-visible:outline-2
935
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
936
+ [href]="appHref(link.href || '#')"
937
+ >
938
+ {{ link.label }}
939
+ </a>
940
+ }
941
+ </div>
942
+ }
943
+ </div>
944
+ }
945
+ </div>
946
+ </div>
947
+ }
948
+
949
+ <!-- Header user menu (authenticated) -->
950
+ @if (userMenuOpen() && userConfig(); as ucfg) {
951
+ <div
952
+ [id]="userPanelId"
953
+ class="dga-header-util-panel fixed w-[min(22rem,calc(100vw-2rem))] max-md:inset-x-3
954
+ max-md:bottom-3 max-md:top-auto max-md:w-auto"
955
+ role="menu"
956
+ [attr.aria-label]="ucfg.name"
957
+ [style]="dropdownPanelStyle()"
958
+ >
959
+ <div
960
+ class="dga-panel-enter dga-panel-enter--anchor-end max-h-[min(85svh,36rem)]
961
+ overflow-y-auto overscroll-contain rounded-2xl border border-border bg-card
962
+ shadow-xl md:max-h-[none] md:overflow-visible md:rounded-md"
963
+ >
964
+ <div class="flex items-center gap-lg border-b border-border-subtle px-xl py-lg">
965
+ <dga-avatar [name]="ucfg.name" [src]="ucfg.avatarSrc" size="md" />
966
+ <div class="flex min-w-0 flex-1 flex-col gap-xxs">
967
+ <span class="truncate text-body-md font-semibold text-display">{{ ucfg.name }}</span>
968
+ @if (ucfg.role) {
969
+ <span class="truncate text-body-sm text-muted">{{ ucfg.role }}</span>
970
+ }
971
+ @if (ucfg.email) {
972
+ <span class="truncate text-body-sm text-paragraph">{{ ucfg.email }}</span>
973
+ }
974
+ </div>
975
+ <button
976
+ type="button"
977
+ class="inline-flex size-5xl shrink-0 cursor-pointer items-center justify-center
978
+ rounded-sm text-muted transition-colors hover:bg-neutral-light hover:text-fg
979
+ focus-visible:outline focus-visible:outline-2
980
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
981
+ aria-label="Close"
982
+ (click)="closeUserMenu()"
983
+ >
984
+ <dga-icon name="close" size="md" />
985
+ </button>
986
+ </div>
987
+ <div class="flex flex-col gap-xxs p-sm">
988
+ @for (link of ucfg.links || []; track link.id) {
989
+ @if (link.id === 'logout') {
990
+ <div class="my-xs border-t border-border-subtle" role="separator"></div>
991
+ <button
992
+ type="button"
993
+ role="menuitem"
994
+ class="flex w-full cursor-pointer items-center gap-md rounded-sm px-lg py-sm
995
+ text-start text-body-md text-fg-error transition-colors hover:bg-error-light
996
+ focus-visible:outline focus-visible:outline-2
997
+ focus-visible:outline-offset-[-2px] focus-visible:outline-focus"
998
+ (click)="onUserLink(link)"
999
+ >
1000
+ @if (link.icon) {
1001
+ <dga-icon [name]="link.icon" size="md" />
1002
+ }
1003
+ <span>{{ link.label }}</span>
1004
+ </button>
1005
+ } @else if (link.href) {
1006
+ <a
1007
+ role="menuitem"
1008
+ class="flex w-full cursor-pointer items-center gap-md rounded-sm px-lg py-sm
1009
+ text-start text-body-md text-fg transition-colors hover:bg-neutral-light
1010
+ focus-visible:outline focus-visible:outline-2
1011
+ focus-visible:outline-offset-[-2px] focus-visible:outline-focus"
1012
+ [href]="appHref(link.href)"
1013
+ (click)="onUserLink(link)"
1014
+ >
1015
+ @if (link.icon) {
1016
+ <dga-icon [name]="link.icon" size="md" />
1017
+ }
1018
+ <span>{{ link.label }}</span>
1019
+ </a>
1020
+ } @else {
1021
+ <button
1022
+ type="button"
1023
+ role="menuitem"
1024
+ class="flex w-full cursor-pointer items-center gap-md rounded-sm px-lg py-sm
1025
+ text-start text-body-md text-fg transition-colors hover:bg-neutral-light
1026
+ focus-visible:outline focus-visible:outline-2
1027
+ focus-visible:outline-offset-[-2px] focus-visible:outline-focus"
1028
+ (click)="onUserLink(link)"
1029
+ >
1030
+ @if (link.icon) {
1031
+ <dga-icon [name]="link.icon" size="md" />
1032
+ }
1033
+ <span>{{ link.label }}</span>
1034
+ </button>
1035
+ }
1036
+ }
1037
+ </div>
1038
+ </div>
1039
+ </div>
1040
+ }
1041
+ </div>
1042
+ }
1043
+
1044
+ <!-- Mobile navigation: sheet | fullscreen | push -->
1045
+ @if (isPortal() && items().length) {
1046
+ @if (mobileOpen() && mobileNav() !== 'push') {
1047
+ <button
1048
+ type="button"
1049
+ class="fixed inset-0 cursor-default md:hidden"
1050
+ [class]="
1051
+ mobileNav() === 'fullscreen'
1052
+ ? 'z-[35] bg-overlay/60 backdrop-blur-[3px]'
1053
+ : 'z-10 bg-overlay/40 backdrop-blur-[1px]'
1054
+ "
1055
+ tabindex="-1"
1056
+ [attr.aria-label]="menuCloseLabel()"
1057
+ (click)="closeAll()"
1058
+ ></button>
1059
+ }
1060
+
1061
+ <div
1062
+ [id]="collapseId"
1063
+ [class]="mobilePanelClass()"
1064
+ [attr.data-mobile-nav]="mobileNav()"
1065
+ [attr.aria-hidden]="!mobileOpen()"
1066
+ [attr.inert]="mobileOpen() ? null : ''"
1067
+ >
1068
+ @if (mobileNav() === 'fullscreen') {
1069
+ <div
1070
+ class="flex h-(--size-header-portal) shrink-0 items-center justify-between
1071
+ gap-lg border-b border-border px-xl"
1072
+ >
1073
+ <div class="flex min-w-0 items-center gap-lg">
1074
+ @if (brandLogo()) {
1075
+ <img
1076
+ class="h-5xl w-auto max-w-[10rem] shrink-0 object-contain"
1077
+ [src]="brandLogo()"
1078
+ [alt]="brandLogoAlt()"
1079
+ />
1080
+ } @else {
1081
+ <span class="truncate text-body-md font-semibold text-display">{{
1082
+ brand() || brandLogoAlt() || navLabel()
1083
+ }}</span>
1084
+ }
1085
+ </div>
1086
+ <button
1087
+ type="button"
1088
+ class="inline-flex size-6xl shrink-0 cursor-pointer items-center justify-center
1089
+ rounded-md text-fg transition-colors hover:bg-neutral-light
1090
+ focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2
1091
+ focus-visible:outline-focus"
1092
+ [attr.aria-label]="menuCloseLabel()"
1093
+ (click)="closeAll()"
1094
+ >
1095
+ <dga-icon name="close" size="xl" />
1096
+ </button>
1097
+ </div>
1098
+ }
1099
+
1100
+ @if (mobileNav() === 'sheet') {
1101
+ <div
1102
+ class="flex min-h-(--size-header-portal) shrink-0 items-center justify-end
1103
+ border-b border-border-subtle px-md
1104
+ pt-[env(safe-area-inset-top,0px)]"
1105
+ >
1106
+ <button
1107
+ type="button"
1108
+ class="inline-flex size-6xl shrink-0 cursor-pointer items-center justify-center
1109
+ rounded-md text-fg transition-colors hover:bg-neutral-light
1110
+ focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2
1111
+ focus-visible:outline-focus"
1112
+ [attr.aria-label]="menuCloseLabel()"
1113
+ (click)="closeAll()"
1114
+ >
1115
+ <dga-icon name="close" size="xl" />
1116
+ </button>
1117
+ </div>
1118
+ }
1119
+
1120
+ @if (mobileNav() === 'push') {
1121
+ <div class="border-b border-border-subtle px-xl py-md">
1122
+ <p class="m-0 text-body-sm font-medium text-muted">{{ navLabel() }}</p>
1123
+ </div>
1124
+ }
1125
+
1126
+ <div
1127
+ class="min-h-0 flex-1 overflow-y-auto overscroll-contain px-xl py-lg
1128
+ pb-[max(1rem,env(safe-area-inset-bottom))]"
1129
+ >
1130
+ <ul class="m-0 flex list-none flex-col gap-none p-0" role="list">
1131
+ @for (item of items(); track item.label; let i = $index) {
1132
+ <li class="border-b border-border-subtle last:border-b-0">
1133
+ @if (item.dropdown) {
1134
+ <button
1135
+ type="button"
1136
+ [class]="mobileItemClass(!!item.current, openDropdown() === i)"
1137
+ [attr.aria-expanded]="openDropdown() === i"
1138
+ (click)="toggleDropdown(i, $event)"
1139
+ >
1140
+ <span class="min-w-0 flex-1" [class]="item.current ? 'text-fg-primary' : ''">{{
1141
+ item.label
1142
+ }}</span>
1143
+ <dga-icon
1144
+ name="chevronDown"
1145
+ size="md"
1146
+ class="shrink-0"
1147
+ [class]="
1148
+ openDropdown() === i
1149
+ ? 'rotate-180 text-fg-primary transition-transform duration-200'
1150
+ : 'text-muted transition-transform duration-200'
1151
+ "
1152
+ />
1153
+ </button>
1154
+
1155
+ @if (openDropdown() === i) {
1156
+ <div
1157
+ class="dga-panel-enter space-y-md border-s-2 border-primary/30 pb-lg ps-lg
1158
+ ms-sm"
1159
+ >
1160
+ @if (item.dropdown.featured; as feat) {
1161
+ <a
1162
+ class="flex flex-col gap-sm rounded-md border border-border
1163
+ bg-primary-faint p-xl text-start no-underline"
1164
+ [href]="appHref(feat.href || '#')"
1165
+ (click)="closeAll()"
1166
+ >
1167
+ <span class="text-body-md font-semibold text-display">{{
1168
+ feat.title
1169
+ }}</span>
1170
+ @if (feat.description) {
1171
+ <span class="text-body-sm text-paragraph">{{
1172
+ feat.description
1173
+ }}</span>
1174
+ }
1175
+ @if (feat.cta) {
1176
+ <span class="text-body-sm font-medium text-fg-primary">{{
1177
+ feat.cta
1178
+ }}</span>
1179
+ }
1180
+ </a>
1181
+ }
1182
+
1183
+ @if (item.dropdown.columns?.length) {
1184
+ @for (col of item.dropdown.columns!; track col.title ?? $index) {
1185
+ <div class="flex flex-col gap-xs">
1186
+ @if (col.title) {
1187
+ <p class="px-xl text-body-sm font-medium text-fg-primary">
1188
+ {{ col.title }}
1189
+ </p>
1190
+ }
1191
+ <ul class="m-0 flex list-none flex-col gap-none p-0">
1192
+ @for (link of col.links; track link.label) {
1193
+ <li>
1194
+ <a
1195
+ [class]="mobileLink"
1196
+ [href]="appHref(link.href || '#')"
1197
+ (click)="onNavLinkClick(link, $event); closeAll()"
1198
+ >
1199
+ <span>{{ link.label }}</span>
1200
+ @if (link.description) {
1201
+ <span class="text-body-sm text-muted">{{
1202
+ link.description
1203
+ }}</span>
1204
+ }
1205
+ </a>
1206
+ </li>
1207
+ }
1208
+ </ul>
1209
+ </div>
1210
+ }
1211
+ } @else {
1212
+ <ul class="m-0 flex list-none flex-col gap-none p-0">
1213
+ @for (link of flatLinks(item); track link.label) {
1214
+ <li>
1215
+ <a
1216
+ [class]="mobileLink"
1217
+ [href]="appHref(link.href || '#')"
1218
+ (click)="onNavLinkClick(link, $event); closeAll()"
1219
+ >
1220
+ <span>{{ link.label }}</span>
1221
+ @if (link.description) {
1222
+ <span class="text-body-sm text-muted">{{
1223
+ link.description
1224
+ }}</span>
1225
+ }
1226
+ </a>
1227
+ </li>
1228
+ }
1229
+ </ul>
1230
+ }
1231
+ </div>
1232
+ }
1233
+ } @else {
1234
+ <a
1235
+ [class]="mobileItemClass(!!item.current)"
1236
+ [attr.aria-current]="item.current ? 'page' : null"
1237
+ [href]="appHref(item.href || '#')"
1238
+ (click)="onNavLinkClick(item, $event); closeAll()"
1239
+ >
1240
+ @if (item.current) {
1241
+ <span
1242
+ class="absolute inset-y-md start-0 w-1 rounded-e-sm bg-primary"
1243
+ aria-hidden="true"
1244
+ ></span>
1245
+ }
1246
+ <span class="min-w-0 flex-1" [class]="item.current ? 'text-fg-primary' : ''">{{
1247
+ item.label
1248
+ }}</span>
1249
+ </a>
1250
+ }
1251
+ </li>
1252
+ }
1253
+ </ul>
1254
+ </div>
1255
+
1256
+ @if (actions().length) {
1257
+ <div
1258
+ class="flex shrink-0 items-center justify-around gap-sm border-t border-border
1259
+ bg-nav px-xl py-md pb-[max(0.75rem,env(safe-area-inset-bottom))]"
1260
+ >
1261
+ @for (action of actions(); track action.id) {
1262
+ @if (action.href) {
1263
+ <a
1264
+ class="relative inline-flex min-h-6xl min-w-6xl cursor-pointer flex-col
1265
+ items-center justify-center gap-xxs rounded-md px-md text-fg
1266
+ hover:bg-neutral-light
1267
+ focus-visible:outline focus-visible:outline-2
1268
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
1269
+ [href]="appHref(action.href)"
1270
+ [attr.aria-label]="action.label"
1271
+ [attr.data-dga-action]="action.id"
1272
+ (click)="closeAll()"
1273
+ >
1274
+ @if (action.icon) {
1275
+ <dga-icon [name]="action.icon" size="xl" />
1276
+ }
1277
+ <span class="max-w-11xl truncate text-body-2xs">{{ action.label }}</span>
1278
+ @if (action.badge) {
1279
+ <span [class]="badgeClass()">{{ action.badge }}</span>
1280
+ }
1281
+ </a>
1282
+ } @else {
1283
+ <button
1284
+ type="button"
1285
+ class="relative inline-flex min-h-6xl min-w-6xl cursor-pointer flex-col
1286
+ items-center justify-center gap-xxs rounded-md px-md text-fg
1287
+ hover:bg-neutral-light
1288
+ focus-visible:outline focus-visible:outline-2
1289
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
1290
+ [attr.aria-label]="action.label"
1291
+ [attr.data-dga-action]="action.id"
1292
+ (click)="onMobileAction(action, $event)"
1293
+ >
1294
+ @if (action.icon) {
1295
+ <dga-icon [name]="action.icon" size="xl" />
1296
+ }
1297
+ <span class="max-w-11xl truncate text-body-2xs">{{ action.label }}</span>
1298
+ @if (action.badge) {
1299
+ <span [class]="badgeClass()">{{ action.badge }}</span>
1300
+ }
1301
+ </button>
1302
+ }
1303
+ }
1304
+ </div>
1305
+ }
1306
+ </div>
1307
+ }
1308
+ `,isInline:!0,styles:["@keyframes dga-fade-in{0%{opacity:0}to{opacity:1}}@keyframes dga-panel-in{0%{opacity:0;transform:translateY(-.625rem) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes dga-chip-in{0%{opacity:0;transform:translateY(.25rem)}to{opacity:1;transform:translateY(0)}}@keyframes dga-notif-row-in{0%{opacity:0;transform:translateY(.35rem)}to{opacity:1;transform:translateY(0)}}@keyframes dga-expand-fade{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}@keyframes dga-spin{to{transform:rotate(360deg)}}@keyframes dga-badge-pulse{0%,to{transform:scale(1)}50%{transform:scale(1.12)}}@keyframes dga-push-in{0%{opacity:0;transform:translateY(-.75rem)}to{opacity:1;transform:translateY(0)}}@keyframes dga-sheet-in-ltr{0%{opacity:.85;transform:translate3d(-100%,0,0)}to{opacity:1;transform:translateZ(0)}}@keyframes dga-sheet-in-rtl{0%{opacity:.85;transform:translate3d(100%,0,0)}to{opacity:1;transform:translateZ(0)}}@keyframes dga-fs-in{0%{opacity:0;transform:translateY(.5rem)}to{opacity:1;transform:translateY(0)}}@keyframes dga-sheet-in{0%{opacity:0;transform:translateY(1.25rem)}to{opacity:1;transform:translateY(0)}}:host .dga-util-backdrop,.dga-header-util-layer .dga-util-backdrop{animation:dga-fade-in .22s ease both}.dga-header-util-layer{position:fixed;inset:0;z-index:var(--z-index-urgent, 400);pointer-events:none}.dga-header-util-layer>.dga-util-backdrop,.dga-header-util-layer>.dga-header-util-panel{pointer-events:auto}:host .dga-panel-enter,.dga-header-util-layer .dga-panel-enter{animation:dga-panel-in .28s cubic-bezier(.16,1,.3,1) both;transform-origin:top center;will-change:transform,opacity}:host .dga-panel-enter--anchor-end,.dga-header-util-layer .dga-panel-enter--anchor-end{transform-origin:top right}:host-context([dir=\"rtl\"]) .dga-panel-enter--anchor-end,:host([dir=\"rtl\"]) .dga-panel-enter--anchor-end,:host-context([dir=\"rtl\"]) .dga-header-util-layer .dga-panel-enter--anchor-end,[dir=rtl] .dga-header-util-layer .dga-panel-enter--anchor-end{transform-origin:top left}@media(max-width:767px){:host .dga-panel-enter--anchor-end,.dga-header-util-layer .dga-panel-enter--anchor-end{animation-name:dga-sheet-in;transform-origin:bottom center}}:host .dga-search-chip{animation:dga-chip-in .28s cubic-bezier(.16,1,.3,1) both}:host .dga-search-input{-webkit-appearance:none;appearance:none}:host .dga-search-input::-webkit-search-decoration,:host .dga-search-input::-webkit-search-cancel-button,:host .dga-search-input::-webkit-search-results-button,:host .dga-search-input::-webkit-search-results-decoration{-webkit-appearance:none;appearance:none;display:none}:host .dga-search-voice.dga-search-voice--active{color:var(--dga-text-primary, #1b8354)!important;background:var(--dga-bg-primary-faint, #f3faf6)!important}:host .dga-notif-item,.dga-header-util-layer .dga-notif-item{animation:dga-notif-row-in .26s cubic-bezier(.16,1,.3,1) both}:host .dga-notif-actions,.dga-header-util-layer .dga-notif-actions{animation:dga-expand-fade .2s ease both}:host .dga-login-spinner,.dga-header-util-layer .dga-login-spinner{animation:dga-spin .7s linear infinite}:host .dga-action-badge{animation:dga-badge-pulse 1.8s ease-in-out infinite;transform-origin:center}@media(prefers-reduced-motion:reduce){:host .dga-util-backdrop,:host .dga-panel-enter,:host .dga-search-chip,:host .dga-notif-item,:host .dga-notif-actions,.dga-header-util-layer .dga-util-backdrop,.dga-header-util-layer .dga-panel-enter{animation-duration:.01ms!important;animation-delay:0ms!important}}:host .dga-burger__box{position:relative;display:block;width:1.375rem;height:1rem}:host .dga-burger__line{position:absolute;inset-inline:0;height:2px;border-radius:1px;background:currentColor;transition:transform .22s cubic-bezier(.4,0,.2,1),opacity .16s ease,top .22s cubic-bezier(.4,0,.2,1)}:host .dga-burger__line:nth-child(1){top:0}:host .dga-burger__line:nth-child(2){top:calc(50% - 1px)}:host .dga-burger__line:nth-child(3){top:calc(100% - 2px)}:host .dga-burger--open{background:var(--color-neutral-light, #f3f4f6)}:host .dga-burger--hidden{visibility:hidden;pointer-events:none}:host .dga-burger--open .dga-burger__line:nth-child(1){top:calc(50% - 1px);transform:rotate(45deg)}:host .dga-burger--open .dga-burger__line:nth-child(2){opacity:0;transform:scaleX(.4)}:host .dga-burger--open .dga-burger__line:nth-child(3){top:calc(50% - 1px);transform:rotate(-45deg)}:host .dga-mobile-nav-closed{display:none}:host .dga-mobile-nav-open{display:flex}:host .dga-mobile-push-open{animation:dga-push-in .24s cubic-bezier(.4,0,.2,1) forwards}:host .dga-mobile-sheet-open{animation:dga-sheet-in-rtl .28s cubic-bezier(.4,0,.2,1) forwards}:host-context([dir=\"rtl\"]) .dga-mobile-sheet-open{animation:dga-sheet-in-ltr .28s cubic-bezier(.4,0,.2,1) forwards}:host .dga-mobile-fs-open{animation:dga-fs-in .24s cubic-bezier(.4,0,.2,1) forwards}@media(prefers-reduced-motion:reduce){:host .dga-action-badge,:host .dga-mobile-push-open,:host .dga-mobile-sheet-open,:host .dga-mobile-fs-open,:host-context([dir=\"rtl\"]) .dga-mobile-sheet-open,:host .dga-burger__line{animation:none;transition:none}}\n"],dependencies:[{kind:"component",type:A,selector:"dga-avatar",inputs:["src","name","size","alt"]},{kind:"component",type:E,selector:"dga-badge",inputs:["variant","size"]},{kind:"component",type:H,selector:"dga-field",inputs:["label","labelHint","hint","error","status","statusMessage","controlId","required"]},{kind:"component",type:F,selector:"dga-form-control",inputs:["size","status","disabled","filled","startIcon","endIcon"]},{kind:"directive",type:$,selector:"[dgaEnd]"},{kind:"component",type:R,selector:"dga-icon",inputs:["name","glyph","size","tone","label"]},{kind:"component",type:j,selector:"dga-icon-badge",inputs:["size","appearance"]},{kind:"component",type:q,selector:"dga-modal",inputs:["open","title","description","size","animation","closeLabel","closeOnBackdrop","closeOnEscape","showClose","scrollable","centered"],outputs:["openChange","closed","opened"]},{kind:"component",type:V,selector:"dga-tooltip",inputs:["text","title","placement","variant","showIcon","disabled","showDelay","hideDelay","dismissMs"]}],changeDetection:i.ChangeDetectionStrategy.OnPush})}i.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.0.7",ngImport:i,type:v,decorators:[{type:G,args:[{selector:"dga-header",standalone:!0,changeDetection:K.OnPush,imports:[A,E,H,F,$,R,j,q,V],template:`
1309
+ <div [class]="innerClasses()">
1310
+ <div class="relative flex min-w-0 shrink-0 items-center self-center">
1311
+ <ng-content select="[dgaHeaderBrand]" />
1312
+ @if (brand() || brandLogo()) {
1313
+ <a
1314
+ class="inline-flex h-(--size-header-logo) cursor-pointer items-center gap-lg font-sans
1315
+ focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2
1316
+ focus-visible:outline-focus"
1317
+ [href]="appHref(brandHref())"
1318
+ >
1319
+ @if (brandLogo()) {
1320
+ <img
1321
+ class="h-(--size-header-logo) w-auto max-w-[min(100%,14rem)] shrink-0 object-contain"
1322
+ [src]="brandLogo()"
1323
+ [alt]="brandLogoAlt()"
1324
+ height="58"
1325
+ />
1326
+ } @else if (appearance() === 'portal' && brandPlaceholder()) {
1327
+ <span
1328
+ class="inline-flex size-(--size-header-logo) shrink-0 items-center justify-center rounded-sm
1329
+ bg-primary-faint text-fg-primary"
1330
+ aria-hidden="true"
1331
+ >
1332
+ <dga-icon name="home" size="xl" tone="primary" />
1333
+ </span>
1334
+ }
1335
+ @if (brand() && !brandLogo()) {
1336
+ <span class="flex min-w-0 flex-col gap-xxs">
1337
+ <span class="text-body-md font-bold text-display">{{ brand() }}</span>
1338
+ @if (brandSlogan()) {
1339
+ <span class="text-body-sm font-normal text-muted">{{ brandSlogan() }}</span>
1340
+ }
1341
+ </span>
1342
+ }
1343
+ </a>
1344
+ }
1345
+ </div>
1346
+
1347
+ @if (isPortal() && items().length) {
1348
+ <button
1349
+ type="button"
1350
+ class="dga-burger relative z-30 ms-auto hidden h-full w-6xl shrink-0 cursor-pointer
1351
+ items-center justify-center self-stretch rounded-md text-fg transition-colors
1352
+ hover:bg-neutral-light max-md:inline-flex
1353
+ focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2
1354
+ focus-visible:outline-focus"
1355
+ [class.dga-burger--open]="mobileOpen()"
1356
+ [class.dga-burger--hidden]="mobileOpen() && mobileNav() !== 'push'"
1357
+ [attr.aria-expanded]="mobileOpen()"
1358
+ [attr.aria-controls]="collapseId"
1359
+ [attr.aria-label]="mobileOpen() ? menuCloseLabel() : menuToggleLabel()"
1360
+ (click)="toggleMobile()"
1361
+ >
1362
+ <span class="dga-burger__box" aria-hidden="true">
1363
+ <span class="dga-burger__line"></span>
1364
+ <span class="dga-burger__line"></span>
1365
+ <span class="dga-burger__line"></span>
1366
+ </span>
1367
+ </button>
1368
+ }
1369
+
1370
+ <!-- Desktop navigation -->
1371
+ <nav
1372
+ [class]="desktopNavClasses()"
1373
+ [attr.aria-label]="navLabel()"
1374
+ [attr.hidden]="null"
1375
+ >
1376
+ @if (items().length) {
1377
+ <ul
1378
+ class="m-0 flex list-none flex-row flex-nowrap items-stretch gap-none p-0"
1379
+ [class.overflow-x-auto]="openDropdown() === null"
1380
+ [class.overflow-visible]="openDropdown() !== null"
1381
+ role="list"
1382
+ >
1383
+ @for (item of items(); track item.label; let i = $index) {
1384
+ <li
1385
+ class="relative"
1386
+ [class.z-dropdown]="openDropdown() === i"
1387
+ [attr.data-open]="openDropdown() === i || null"
1388
+ [attr.data-variant]="item.dropdown ? resolveVariant(item) : null"
1389
+ (mouseenter)="onItemEnter(i)"
1390
+ (focusin)="onItemEnter(i)"
1391
+ >
1392
+ @if (item.dropdown) {
1393
+ <button
1394
+ type="button"
1395
+ [class]="navItemClass(!!item.current, openDropdown() === i)"
1396
+ [attr.aria-expanded]="openDropdown() === i"
1397
+ [attr.aria-haspopup]="'true'"
1398
+ (click)="toggleDropdown(i, $event)"
1399
+ >
1400
+ <span
1401
+ aria-hidden="true"
1402
+ [class]="indicatorClass(!!item.current || openDropdown() === i)"
1403
+ ></span>
1404
+ <span class="whitespace-nowrap">{{ item.label }}</span>
1405
+ <dga-icon
1406
+ name="chevronDown"
1407
+ size="md"
1408
+ [class]="
1409
+ openDropdown() === i
1410
+ ? 'rotate-180 transition-transform duration-200'
1411
+ : 'transition-transform duration-200'
1412
+ "
1413
+ />
1414
+ </button>
1415
+
1416
+ @if (openDropdown() === i && resolveVariant(item) === 'menu') {
1417
+ <div
1418
+ class="absolute start-0 top-full z-dropdown min-w-[16rem]
1419
+ max-w-[min(100vw-2rem,20rem)]"
1420
+ role="menu"
1421
+ [attr.aria-label]="item.label"
1422
+ >
1423
+ <div class="h-xs" aria-hidden="true"></div>
1424
+ <div
1425
+ class="dga-panel-enter rounded-md border border-border bg-card p-sm
1426
+ shadow-lg"
1427
+ >
1428
+ @if (item.dropdown.columns?.length) {
1429
+ <div class="flex flex-col gap-lg p-md">
1430
+ @for (col of item.dropdown.columns!; track col.title ?? $index) {
1431
+ <div class="flex flex-col gap-xs">
1432
+ @if (col.title) {
1433
+ <p class="px-xl text-body-sm font-medium text-fg-primary">
1434
+ {{ col.title }}
1435
+ </p>
1436
+ }
1437
+ <ul class="m-0 flex list-none flex-col gap-none p-0" role="list">
1438
+ @for (link of col.links; track link.label) {
1439
+ <li role="none">
1440
+ <a
1441
+ role="menuitem"
1442
+ [class]="dropLink"
1443
+ [href]="appHref(link.href || '#')"
1444
+ (click)="onNavLinkClick(link, $event)"
1445
+ >{{ link.label }}</a
1446
+ >
1447
+ </li>
1448
+ }
1449
+ </ul>
1450
+ </div>
1451
+ }
1452
+ </div>
1453
+ } @else {
1454
+ <ul class="m-0 flex list-none flex-col gap-none p-0" role="list">
1455
+ @for (link of item.dropdown.links || []; track link.label) {
1456
+ <li role="none">
1457
+ <a
1458
+ role="menuitem"
1459
+ [class]="dropLink"
1460
+ [href]="appHref(link.href || '#')"
1461
+ (click)="onNavLinkClick(link, $event)"
1462
+ >{{ link.label }}</a
1463
+ >
1464
+ </li>
1465
+ }
1466
+ </ul>
1467
+ }
1468
+ </div>
1469
+ </div>
1470
+ }
1471
+ } @else {
1472
+ <a
1473
+ [class]="navItemClass(!!item.current)"
1474
+ [attr.aria-current]="item.current ? 'page' : null"
1475
+ [href]="appHref(item.href || '#')"
1476
+ (click)="onNavLinkClick(item, $event)"
1477
+ >
1478
+ <span aria-hidden="true" [class]="indicatorClass(!!item.current)"></span>
1479
+ <span class="whitespace-nowrap">{{ item.label }}</span>
1480
+ </a>
1481
+ }
1482
+ </li>
1483
+ }
1484
+ </ul>
1485
+ }
1486
+ <div class="contents">
1487
+ <ng-content />
1488
+ </div>
1489
+ </nav>
1490
+
1491
+ <div [class]="actionsClasses()">
1492
+ @for (action of actions(); track action.id) {
1493
+ <dga-tooltip
1494
+ class="relative flex h-full"
1495
+ [text]="actionTooltip(action)"
1496
+ placement="bottom"
1497
+ [showIcon]="false"
1498
+ [disabled]="!isIconOnly(action) && !(isLoginAction(action) && authenticated())"
1499
+ >
1500
+ @if (action.href) {
1501
+ <a
1502
+ [class]="actionClass(action)"
1503
+ [href]="appHref(action.href)"
1504
+ [attr.aria-label]="actionAriaLabel(action)"
1505
+ [attr.data-dga-action]="action.id"
1506
+ >
1507
+ <span aria-hidden="true" [class]="indicatorClass(false)"></span>
1508
+ @if (action.icon) {
1509
+ <dga-icon [name]="action.icon" size="xl" />
1510
+ }
1511
+ @if (!isIconOnly(action)) {
1512
+ <span>{{ action.label }}</span>
1513
+ }
1514
+ @if (action.badge) {
1515
+ <span [class]="badgeClass()">{{ action.badge }}</span>
1516
+ }
1517
+ </a>
1518
+ } @else {
1519
+ <button
1520
+ type="button"
1521
+ [class]="actionClass(action)"
1522
+ [attr.aria-label]="actionAriaLabel(action)"
1523
+ [attr.aria-expanded]="actionExpanded(action)"
1524
+ [attr.aria-controls]="actionControls(action)"
1525
+ [attr.data-dga-action]="action.id"
1526
+ (click)="onAction(action, $event)"
1527
+ >
1528
+ <span
1529
+ aria-hidden="true"
1530
+ [class]="indicatorClass(actionIndicatorActive(action))"
1531
+ ></span>
1532
+ @if (isLoginAction(action) && authenticated() && userConfig(); as u) {
1533
+ <dga-avatar [name]="u.name" [src]="u.avatarSrc" size="sm" />
1534
+ <span class="hidden max-w-11xl truncate lg:inline">{{ u.name }}</span>
1535
+ } @else {
1536
+ @if (action.icon) {
1537
+ <dga-icon [name]="action.icon" size="xl" />
1538
+ }
1539
+ @if (!isIconOnly(action)) {
1540
+ <span>{{ action.label }}</span>
1541
+ }
1542
+ }
1543
+ @if (action.badge) {
1544
+ <span [class]="badgeClass()">{{ action.badge }}</span>
1545
+ }
1546
+ </button>
1547
+ }
1548
+ </dga-tooltip>
1549
+ }
1550
+ <ng-content select="[dgaHeaderEnd]" />
1551
+ </div>
1552
+ </div>
1553
+
1554
+ <!-- Desktop mega / featured -->
1555
+ @if (activeWideDropdown(); as drop) {
1556
+ <div
1557
+ class="absolute start-0 end-0 top-full z-dropdown hidden w-full md:block"
1558
+ role="region"
1559
+ [attr.aria-label]="drop.item.label"
1560
+ [attr.data-variant]="drop.variant"
1561
+ (mouseenter)="onPanelEnter()"
1562
+ >
1563
+ <div
1564
+ class="dga-panel-enter border-b border-border bg-card shadow-lg md:rounded-b-lg"
1565
+ >
1566
+ <div class="w-full px-(--wrapper-padding) py-2xl lg:py-3xl">
1567
+ <div [class]="featuredGridClass(drop.variant, !!drop.item.dropdown!.featured)">
1568
+ <div class="min-w-0">
1569
+ @switch (resolveLayout(drop.item)) {
1570
+ @case ('columns') {
1571
+ <div [class]="columnsGridClass(colCount(drop.item))">
1572
+ @for (
1573
+ col of drop.item.dropdown!.columns!;
1574
+ track col.title ?? $index
1575
+ ) {
1576
+ <div class="flex min-w-0 flex-col gap-md">
1577
+ @if (col.title) {
1578
+ <p
1579
+ class="border-b border-border-subtle pb-sm text-body-lg
1580
+ font-medium text-fg-primary"
1581
+ >
1582
+ {{ col.title }}
1583
+ </p>
1584
+ }
1585
+ <ul class="m-0 flex list-none flex-col gap-none p-0" role="list">
1586
+ @for (link of col.links; track link.label) {
1587
+ <li>
1588
+ <a
1589
+ [class]="dropLink"
1590
+ [href]="appHref(link.href || '#')"
1591
+ (click)="onNavLinkClick(link, $event)"
1592
+ >
1593
+ <span>{{ link.label }}</span>
1594
+ @if (link.description) {
1595
+ <span class="text-body-sm text-muted">{{
1596
+ link.description
1597
+ }}</span>
1598
+ }
1599
+ </a>
1600
+ </li>
1601
+ }
1602
+ </ul>
1603
+ </div>
1604
+ }
1605
+ </div>
1606
+ }
1607
+ @case ('rows') {
1608
+ <ul
1609
+ class="m-0 grid w-full list-none grid-cols-1 gap-md p-0
1610
+ sm:grid-cols-2 lg:grid-cols-4"
1611
+ role="list"
1612
+ >
1613
+ @for (link of drop.item.dropdown!.links || []; track link.label) {
1614
+ <li class="min-w-0">
1615
+ <a
1616
+ [class]="dropTile"
1617
+ [href]="appHref(link.href || '#')"
1618
+ (click)="onNavLinkClick(link, $event)"
1619
+ >
1620
+ <span class="text-body-md font-medium text-fg">{{
1621
+ link.label
1622
+ }}</span>
1623
+ @if (link.description) {
1624
+ <span class="text-body-sm text-muted">{{
1625
+ link.description
1626
+ }}</span>
1627
+ }
1628
+ </a>
1629
+ </li>
1630
+ }
1631
+ </ul>
1632
+ }
1633
+ @default {
1634
+ <ul
1635
+ class="m-0 grid w-full max-w-(--container-5xl) list-none grid-cols-1 gap-xs
1636
+ p-0 sm:grid-cols-2 lg:grid-cols-3"
1637
+ role="list"
1638
+ >
1639
+ @for (link of flatLinks(drop.item); track link.label) {
1640
+ <li class="min-w-0">
1641
+ <a
1642
+ [class]="dropLink"
1643
+ [href]="appHref(link.href || '#')"
1644
+ (click)="onNavLinkClick(link, $event)"
1645
+ >
1646
+ <span>{{ link.label }}</span>
1647
+ @if (link.description) {
1648
+ <span class="text-body-sm text-muted">{{
1649
+ link.description
1650
+ }}</span>
1651
+ }
1652
+ </a>
1653
+ </li>
1654
+ }
1655
+ </ul>
1656
+ }
1657
+ }
1658
+ </div>
1659
+
1660
+ @if (drop.variant === 'featured' && drop.item.dropdown!.featured; as feat) {
1661
+ <aside
1662
+ class="flex min-w-0 flex-col gap-lg overflow-hidden rounded-md
1663
+ border border-border bg-primary-faint p-2xl lg:self-start"
1664
+ >
1665
+ @if (feat.imageUrl) {
1666
+ <img
1667
+ class="aspect-[16/10] h-auto w-full rounded-sm object-cover"
1668
+ [src]="feat.imageUrl"
1669
+ alt=""
1670
+ />
1671
+ }
1672
+ <div class="flex min-w-0 flex-col gap-md">
1673
+ <p class="text-body-lg font-semibold text-display">{{ feat.title }}</p>
1674
+ @if (feat.description) {
1675
+ <p class="text-body-sm leading-relaxed text-paragraph">
1676
+ {{ feat.description }}
1677
+ </p>
1678
+ }
1679
+ @if (feat.cta) {
1680
+ <a
1681
+ class="mt-xs inline-flex h-5xl cursor-pointer items-center justify-center gap-sm
1682
+ rounded-sm bg-primary px-xl text-body-md font-medium text-on-color
1683
+ transition-opacity hover:opacity-90
1684
+ focus-visible:outline focus-visible:outline-2
1685
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
1686
+ [href]="appHref(feat.href || '#')"
1687
+ >
1688
+ <span>{{ feat.cta }}</span>
1689
+ <dga-icon name="chevronLeft" size="sm" />
1690
+ </a>
1691
+ }
1692
+ </div>
1693
+ </aside>
1694
+ }
1695
+ </div>
1696
+ </div>
1697
+ </div>
1698
+ </div>
1699
+ }
1700
+
1701
+ <!-- Header search popup (dga-modal) -->
1702
+ @if (searchConfig(); as cfg) {
1703
+ <dga-modal
1704
+ class="dga-header-search-modal"
1705
+ [id]="searchPanelId"
1706
+ [open]="searchOpen()"
1707
+ (openChange)="onSearchModalOpenChange($event)"
1708
+ [title]="cfg.title || cfg.submitLabel || 'Search'"
1709
+ [description]="cfg.description || ''"
1710
+ [closeLabel]="cfg.closeLabel || 'Close'"
1711
+ size="lg"
1712
+ animation="scale"
1713
+ [scrollable]="false"
1714
+ (opened)="focusSearchInput()"
1715
+ >
1716
+ <form
1717
+ class="flex flex-col gap-xl"
1718
+ role="search"
1719
+ [attr.aria-label]="cfg.submitLabel || 'Search'"
1720
+ (submit)="onSearchFormSubmit($event)"
1721
+ >
1722
+ <div class="flex w-full flex-col gap-md sm:flex-row sm:items-stretch sm:gap-lg">
1723
+ <div class="min-w-0 flex-1">
1724
+ <dga-form-control size="lg" startIcon="search" [filled]="!!searchQuery()">
1725
+ <input
1726
+ #searchInput
1727
+ type="search"
1728
+ name="q"
1729
+ class="dga-search-input placeholder:text-muted"
1730
+ [placeholder]="cfg.placeholder || 'Search...'"
1731
+ [attr.aria-label]="cfg.placeholder || 'Search...'"
1732
+ [value]="searchQuery()"
1733
+ (input)="searchQuery.set($any($event.target).value)"
1734
+ />
1735
+ @if (searchQuery()) {
1736
+ <button
1737
+ type="button"
1738
+ dgaEnd
1739
+ [attr.aria-label]="cfg.clearLabel || 'Clear search'"
1740
+ (click)="clearSearchQuery()"
1741
+ >
1742
+ <dga-icon name="close" size="sm" />
1743
+ </button>
1744
+ }
1745
+ @if (cfg.voice !== false) {
1746
+ <button
1747
+ type="button"
1748
+ dgaEnd
1749
+ class="dga-search-voice"
1750
+ [class.dga-search-voice--active]="voiceListening()"
1751
+ [attr.aria-label]="cfg.voiceLabel || 'Voice input'"
1752
+ [attr.aria-pressed]="voiceListening()"
1753
+ (click)="onSearchVoice()"
1754
+ >
1755
+ <dga-icon
1756
+ name="mic"
1757
+ size="sm"
1758
+ [tone]="voiceListening() ? 'current' : 'muted'"
1759
+ />
1760
+ </button>
1761
+ }
1762
+ </dga-form-control>
1763
+ </div>
1764
+ <button
1765
+ type="submit"
1766
+ class="inline-flex min-h-6xl shrink-0 cursor-pointer items-center justify-center gap-sm
1767
+ rounded-md bg-primary px-xl text-body-md font-medium text-on-color shadow-sm
1768
+ transition-opacity hover:opacity-90
1769
+ focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2
1770
+ focus-visible:outline-focus sm:px-2xl"
1771
+ >
1772
+ <dga-icon name="search" size="md" />
1773
+ <span>{{ cfg.submitLabel || 'Search' }}</span>
1774
+ </button>
1775
+ </div>
1776
+
1777
+ @if (cfg.suggestions?.length) {
1778
+ <div class="flex flex-col gap-md">
1779
+ <span class="text-body-sm font-semibold text-display">
1780
+ {{ cfg.suggestionsLabel || 'Suggestions:' }}
1781
+ </span>
1782
+ <div class="flex flex-wrap gap-sm">
1783
+ @for (item of cfg.suggestions!; track item.value ?? item.label; let i = $index) {
1784
+ <button
1785
+ type="button"
1786
+ class="dga-search-chip inline-flex h-4xl cursor-pointer items-center gap-xs
1787
+ rounded-full border border-border bg-neutral-light px-lg text-body-sm
1788
+ text-paragraph transition-[background-color,transform,border-color]
1789
+ duration-150 ease-out hover:-translate-y-px hover:border-primary
1790
+ hover:bg-sunken
1791
+ focus-visible:outline focus-visible:outline-2
1792
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
1793
+ [style.animation-delay]="i * 40 + 'ms'"
1794
+ (click)="applySearchSuggestion(item)"
1795
+ >
1796
+ <dga-icon [name]="item.icon || 'plus'" size="xs" />
1797
+ <span>{{ item.label }}</span>
1798
+ </button>
1799
+ }
1800
+ </div>
1801
+ </div>
1802
+ }
1803
+ </form>
1804
+ </dga-modal>
1805
+ }
1806
+
1807
+ <!--
1808
+ Utility overlays (notifications / login / user): portaled to body at z-urgent
1809
+ so sticky announcement + chatbot/a11y FABs stay underneath.
1810
+ -->
1811
+ @if (utilityOverlayOpen()) {
1812
+ <div #utilLayer class="dga-header-util-layer">
1813
+ <button
1814
+ type="button"
1815
+ class="dga-util-backdrop absolute inset-0 cursor-default bg-overlay/50"
1816
+ tabindex="-1"
1817
+ aria-label="Close"
1818
+ (click)="closeUtilityOverlays()"
1819
+ ></button>
1820
+
1821
+ <!-- Header notifications panel -->
1822
+ @if (notificationsOpen() && notificationsConfig(); as ncfg) {
1823
+ <div
1824
+ [id]="notificationsPanelId"
1825
+ class="dga-header-util-panel fixed w-[min(28rem,calc(100vw-2rem))] max-md:inset-x-3
1826
+ max-md:bottom-3 max-md:top-auto max-md:w-auto"
1827
+ role="region"
1828
+ [attr.aria-label]="ncfg.title || ncfg.viewAllLabel || 'Notifications'"
1829
+ [style]="dropdownPanelStyle()"
1830
+ >
1831
+ <div
1832
+ class="dga-panel-enter dga-panel-enter--anchor-end flex max-h-[min(85svh,40rem)]
1833
+ flex-col overflow-hidden rounded-2xl border border-border bg-card shadow-xl
1834
+ md:max-h-[none] md:rounded-md"
1835
+ >
1836
+ <div
1837
+ class="flex shrink-0 flex-col gap-sm border-b border-border-subtle px-xl py-md
1838
+ md:flex-row md:items-center md:justify-between md:gap-md"
1839
+ >
1840
+ <div class="flex min-w-0 items-center justify-between gap-sm md:justify-start">
1841
+ <div class="flex min-w-0 items-center gap-sm">
1842
+ <h2 class="m-0 truncate text-body-lg font-semibold text-display md:text-body-md">
1843
+ {{ ncfg.title || 'Notifications' }}
1844
+ </h2>
1845
+ @if (unreadNotificationCount() > 0) {
1846
+ <span
1847
+ class="inline-flex min-w-4xl items-center justify-center rounded-full
1848
+ bg-error px-sm py-xxs text-body-2xs font-semibold text-on-color"
1849
+ >
1850
+ {{ unreadNotificationCount() }}
1851
+ </span>
1852
+ }
1853
+ </div>
1854
+ <button
1855
+ type="button"
1856
+ class="inline-flex size-5xl shrink-0 cursor-pointer items-center justify-center
1857
+ rounded-sm text-muted transition-colors hover:bg-neutral-light hover:text-fg
1858
+ focus-visible:outline focus-visible:outline-2
1859
+ focus-visible:outline-offset-2 focus-visible:outline-focus md:hidden"
1860
+ [attr.aria-label]="ncfg.closeLabel || 'Close'"
1861
+ (click)="closeNotifications()"
1862
+ >
1863
+ <dga-icon name="close" size="md" />
1864
+ </button>
1865
+ </div>
1866
+ <div class="flex shrink-0 items-center gap-xxs max-md:justify-stretch">
1867
+ @if (unreadNotificationCount() > 0) {
1868
+ <button
1869
+ type="button"
1870
+ class="inline-flex h-4xl flex-1 cursor-pointer items-center justify-center
1871
+ rounded-sm px-md text-body-sm font-medium text-fg-primary
1872
+ transition-colors hover:bg-primary-faint
1873
+ focus-visible:outline focus-visible:outline-2
1874
+ focus-visible:outline-offset-2 focus-visible:outline-focus
1875
+ md:flex-none"
1876
+ (click)="markAllNotificationsRead()"
1877
+ >
1878
+ {{ ncfg.markAllReadLabel || 'Mark all as read' }}
1879
+ </button>
1880
+ }
1881
+ <button
1882
+ type="button"
1883
+ class="hidden size-5xl cursor-pointer items-center justify-center rounded-sm
1884
+ text-muted transition-colors hover:bg-neutral-light hover:text-fg
1885
+ focus-visible:outline focus-visible:outline-2
1886
+ focus-visible:outline-offset-2 focus-visible:outline-focus md:inline-flex"
1887
+ [attr.aria-label]="ncfg.closeLabel || 'Close'"
1888
+ (click)="closeNotifications()"
1889
+ >
1890
+ <dga-icon name="close" size="md" />
1891
+ </button>
1892
+ </div>
1893
+ </div>
1894
+
1895
+ <div class="min-h-0 flex-1 overflow-y-auto overscroll-contain md:max-h-[min(48svh,28rem)]">
1896
+ @if (visibleNotifications().length === 0) {
1897
+ <div class="flex flex-col items-center gap-md px-xl py-3xl text-center">
1898
+ <span
1899
+ class="inline-flex size-6xl items-center justify-center rounded-full
1900
+ bg-neutral-light text-muted"
1901
+ aria-hidden="true"
1902
+ >
1903
+ <dga-icon name="bell" size="xl" tone="muted" />
1904
+ </span>
1905
+ <p class="m-0 text-body-sm text-muted">
1906
+ {{ ncfg.emptyLabel || 'No notifications' }}
1907
+ </p>
1908
+ </div>
1909
+ } @else {
1910
+ <ul class="m-0 flex list-none flex-col p-0" role="list">
1911
+ @for (item of visibleNotifications(); track item.id) {
1912
+ <li
1913
+ class="border-b border-border-subtle last:border-b-0"
1914
+ [class]="item.read ? 'bg-surface' : 'bg-card'"
1915
+ >
1916
+ <button
1917
+ type="button"
1918
+ class="flex w-full cursor-pointer items-start gap-lg px-xl py-lg text-start
1919
+ transition-colors hover:bg-neutral-light
1920
+ focus-visible:outline focus-visible:outline-2
1921
+ focus-visible:outline-offset-[-2px] focus-visible:outline-focus"
1922
+ [attr.aria-expanded]="expandedNotificationId() === item.id"
1923
+ (click)="toggleNotificationExpand(item)"
1924
+ >
1925
+ <span class="relative mt-xxs shrink-0">
1926
+ <dga-icon-badge size="sm">
1927
+ <dga-icon [name]="item.icon || 'bell'" size="sm" />
1928
+ </dga-icon-badge>
1929
+ @if (!item.read) {
1930
+ <span
1931
+ class="absolute -end-xxs -top-xxs size-[0.5rem] rounded-full bg-primary
1932
+ ring-2 ring-white"
1933
+ aria-hidden="true"
1934
+ ></span>
1935
+ }
1936
+ </span>
1937
+ <span class="min-w-0 flex-1">
1938
+ <span class="mb-xs flex flex-wrap items-center gap-sm">
1939
+ @if (item.statusLabel) {
1940
+ <dga-badge [variant]="item.tone || 'neutral'" size="sm">
1941
+ {{ item.statusLabel }}
1942
+ </dga-badge>
1943
+ }
1944
+ <span
1945
+ class="min-w-0 truncate text-body-md text-display"
1946
+ [class.font-semibold]="!item.read"
1947
+ [class.font-medium]="item.read"
1948
+ [class.text-muted]="item.read"
1949
+ >
1950
+ {{ item.title }}
1951
+ </span>
1952
+ </span>
1953
+ <span
1954
+ class="text-body-sm text-paragraph"
1955
+ [class.line-clamp-2]="expandedNotificationId() !== item.id"
1956
+ >
1957
+ {{ item.message }}
1958
+ </span>
1959
+ </span>
1960
+ <dga-icon
1961
+ class="mt-xs shrink-0 text-muted transition-transform duration-200"
1962
+ [class.rotate-180]="expandedNotificationId() === item.id"
1963
+ name="chevronDown"
1964
+ size="sm"
1965
+ />
1966
+ </button>
1967
+
1968
+ @if (expandedNotificationId() === item.id) {
1969
+ <div
1970
+ class="dga-notif-actions flex flex-wrap gap-sm border-t border-border-subtle
1971
+ bg-surface px-xl py-md"
1972
+ >
1973
+ @if (!item.read) {
1974
+ <button
1975
+ type="button"
1976
+ class="inline-flex h-4xl cursor-pointer items-center gap-xs rounded-sm
1977
+ px-md text-body-sm text-fg transition-colors hover:bg-neutral-light
1978
+ focus-visible:outline focus-visible:outline-2
1979
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
1980
+ (click)="onNotificationAction(item, 'read'); $event.stopPropagation()"
1981
+ >
1982
+ <dga-icon name="success" size="sm" />
1983
+ <span>{{ ncfg.markReadLabel || 'Mark as read' }}</span>
1984
+ </button>
1985
+ }
1986
+ <button
1987
+ type="button"
1988
+ class="inline-flex h-4xl cursor-pointer items-center gap-xs rounded-sm
1989
+ px-md text-body-sm text-fg transition-colors hover:bg-neutral-light
1990
+ focus-visible:outline focus-visible:outline-2
1991
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
1992
+ (click)="onNotificationAction(item, 'view'); $event.stopPropagation()"
1993
+ >
1994
+ <dga-icon name="eye" size="sm" />
1995
+ <span>{{ ncfg.viewLabel || 'View' }}</span>
1996
+ </button>
1997
+ <button
1998
+ type="button"
1999
+ class="inline-flex h-4xl cursor-pointer items-center gap-xs rounded-sm
2000
+ px-md text-body-sm text-fg-error transition-colors hover:bg-error-light
2001
+ focus-visible:outline focus-visible:outline-2
2002
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
2003
+ (click)="onNotificationAction(item, 'dismiss'); $event.stopPropagation()"
2004
+ >
2005
+ <dga-icon name="close" size="sm" />
2006
+ <span>{{ ncfg.dismissLabel || 'Dismiss' }}</span>
2007
+ </button>
2008
+ </div>
2009
+ }
2010
+ </li>
2011
+ }
2012
+ </ul>
2013
+ }
2014
+ </div>
2015
+
2016
+ @if (ncfg.viewAllHref || ncfg.viewAllLabel) {
2017
+ <div
2018
+ class="shrink-0 border-t border-border
2019
+ pb-[max(0px,env(safe-area-inset-bottom))] md:pb-0"
2020
+ >
2021
+ <a
2022
+ class="inline-flex w-full cursor-pointer items-center justify-center gap-sm
2023
+ px-xl py-lg text-body-sm font-medium text-fg-primary
2024
+ transition-colors hover:bg-neutral-light
2025
+ focus-visible:outline focus-visible:outline-2
2026
+ focus-visible:outline-offset-[-2px] focus-visible:outline-focus"
2027
+ [href]="appHref(ncfg.viewAllHref || '#')"
2028
+ (click)="closeNotifications()"
2029
+ >
2030
+ <span>{{ ncfg.viewAllLabel || 'View all notifications' }}</span>
2031
+ <dga-icon name="chevronLeft" size="sm" />
2032
+ </a>
2033
+ </div>
2034
+ }
2035
+ </div>
2036
+ </div>
2037
+ }
2038
+
2039
+ <!-- Header login panel (guest) -->
2040
+ @if (loginOpen() && loginConfig(); as lcfg) {
2041
+ <div
2042
+ [id]="loginPanelId"
2043
+ class="dga-header-util-panel fixed max-md:inset-x-3 max-md:bottom-3 max-md:top-auto
2044
+ max-md:w-auto"
2045
+ role="dialog"
2046
+ [attr.aria-label]="lcfg.title || 'Login'"
2047
+ [style]="loginPanelOuterStyle()"
2048
+ >
2049
+ <div
2050
+ class="dga-panel-enter dga-panel-enter--anchor-end overflow-y-auto overscroll-contain
2051
+ rounded-2xl border border-border bg-card shadow-xl md:overflow-visible
2052
+ md:rounded-md"
2053
+ [style]="loginPanelInnerStyle()"
2054
+ >
2055
+ <div class="flex items-start justify-between gap-md px-2xl pt-2xl max-md:pt-xl">
2056
+ <div class="flex min-w-0 flex-col gap-xs">
2057
+ <h2 class="m-0 text-body-lg font-semibold text-display">
2058
+ {{ lcfg.title || 'Login' }}
2059
+ </h2>
2060
+ <p class="m-0 text-body-sm text-paragraph">
2061
+ {{ lcfg.description || 'Sign in to access your account' }}
2062
+ </p>
2063
+ </div>
2064
+ <button
2065
+ type="button"
2066
+ class="inline-flex size-5xl shrink-0 cursor-pointer items-center justify-center
2067
+ rounded-sm text-muted transition-colors hover:bg-neutral-light hover:text-fg
2068
+ focus-visible:outline focus-visible:outline-2
2069
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
2070
+ [attr.aria-label]="lcfg.closeLabel || lcfg.cancelLabel || 'Close'"
2071
+ (click)="closeLogin()"
2072
+ >
2073
+ <dga-icon name="close" size="md" />
2074
+ </button>
2075
+ </div>
2076
+
2077
+ <ng-content select="[dgaHeaderLoginBeforeForm]" />
2078
+
2079
+ @if (hasCustomLoginForm()) {
2080
+ <div
2081
+ class="px-2xl pb-[max(1.5rem,env(safe-area-inset-bottom))] pt-xl md:pb-2xl"
2082
+ >
2083
+ <ng-content select="[dgaHeaderLoginForm]" />
2084
+ </div>
2085
+ } @else {
2086
+ <form
2087
+ class="flex flex-col gap-xl px-2xl pb-[max(1.5rem,env(safe-area-inset-bottom))]
2088
+ pt-xl md:pb-2xl"
2089
+ (submit)="onLoginSubmit($event)"
2090
+ >
2091
+ <dga-field
2092
+ [label]="lcfg.emailLabel || 'Email'"
2093
+ controlId="dga-header-login-email"
2094
+ [hint]="loginEmailError() ? '' : lcfg.emailHint || ''"
2095
+ [error]="loginEmailError()"
2096
+ required
2097
+ >
2098
+ <dga-form-control startIcon="mail" [filled]="!!loginEmail()">
2099
+ <input
2100
+ id="dga-header-login-email"
2101
+ type="email"
2102
+ name="email"
2103
+ autocomplete="username"
2104
+ required
2105
+ class="placeholder:text-muted"
2106
+ [placeholder]="lcfg.emailPlaceholder || 'name@example.gov.sa'"
2107
+ [value]="loginEmail()"
2108
+ (input)="onLoginEmailInput($any($event.target).value)"
2109
+ />
2110
+ </dga-form-control>
2111
+ </dga-field>
2112
+
2113
+ <dga-field
2114
+ [label]="lcfg.passwordLabel || 'Password'"
2115
+ controlId="dga-header-login-password"
2116
+ [hint]="loginPasswordError() ? '' : lcfg.passwordHint || ''"
2117
+ [error]="loginPasswordError()"
2118
+ required
2119
+ >
2120
+ <dga-form-control [filled]="!!loginPassword()">
2121
+ <input
2122
+ id="dga-header-login-password"
2123
+ [type]="showLoginPassword() ? 'text' : 'password'"
2124
+ name="password"
2125
+ autocomplete="current-password"
2126
+ required
2127
+ class="placeholder:text-muted"
2128
+ [placeholder]="lcfg.passwordPlaceholder || 'Enter your password'"
2129
+ [value]="loginPassword()"
2130
+ (input)="onLoginPasswordInput($any($event.target).value)"
2131
+ />
2132
+ <button
2133
+ type="button"
2134
+ dgaEnd
2135
+ [attr.aria-label]="
2136
+ showLoginPassword()
2137
+ ? lcfg.hidePasswordLabel || 'Hide password'
2138
+ : lcfg.showPasswordLabel || 'Show password'
2139
+ "
2140
+ (click)="showLoginPassword.set(!showLoginPassword())"
2141
+ >
2142
+ <dga-icon [name]="showLoginPassword() ? 'eyeOff' : 'eye'" size="sm" />
2143
+ </button>
2144
+ </dga-form-control>
2145
+ </dga-field>
2146
+
2147
+ @if (lcfg.forgotPasswordHref || lcfg.forgotPasswordLabel) {
2148
+ <a
2149
+ class="self-start text-body-sm font-medium text-fg-primary transition-opacity
2150
+ hover:opacity-80
2151
+ focus-visible:outline focus-visible:outline-2
2152
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
2153
+ [href]="appHref(lcfg.forgotPasswordHref || '#')"
2154
+ >
2155
+ {{ lcfg.forgotPasswordLabel || 'Forgot password?' }}
2156
+ </a>
2157
+ }
2158
+
2159
+ <ng-content select="[dgaHeaderLoginAfterFields]" />
2160
+
2161
+ @if (loginError() || lcfg.errorMessage) {
2162
+ <p class="m-0 text-body-sm text-fg-error" role="alert">
2163
+ {{ loginError() || lcfg.errorMessage }}
2164
+ </p>
2165
+ }
2166
+
2167
+ <div class="flex flex-col gap-md pt-xs">
2168
+ <button
2169
+ type="submit"
2170
+ class="inline-flex h-5xl w-full cursor-pointer items-center justify-center gap-sm
2171
+ rounded-sm bg-primary px-xl text-body-md font-medium text-on-color shadow-sm
2172
+ transition-opacity hover:opacity-90 disabled:cursor-wait disabled:opacity-60
2173
+ focus-visible:outline focus-visible:outline-2
2174
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
2175
+ [disabled]="loginSubmitting()"
2176
+ >
2177
+ @if (loginSubmitting()) {
2178
+ <span
2179
+ class="dga-login-spinner size-md rounded-full border-2 border-on-color/30
2180
+ border-t-on-color"
2181
+ aria-hidden="true"
2182
+ ></span>
2183
+ }
2184
+ <span>
2185
+ {{
2186
+ loginSubmitting()
2187
+ ? lcfg.submittingLabel || '…'
2188
+ : lcfg.submitLabel || 'Login'
2189
+ }}
2190
+ </span>
2191
+ </button>
2192
+ @if (lcfg.showCancel !== false) {
2193
+ <button
2194
+ type="button"
2195
+ class="inline-flex h-5xl w-full cursor-pointer items-center justify-center
2196
+ rounded-sm px-xl text-body-md font-medium text-fg transition-colors
2197
+ hover:bg-neutral-light
2198
+ focus-visible:outline focus-visible:outline-2
2199
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
2200
+ (click)="closeLogin()"
2201
+ >
2202
+ {{ lcfg.cancelLabel || 'Cancel' }}
2203
+ </button>
2204
+ }
2205
+ </div>
2206
+ </form>
2207
+ }
2208
+
2209
+ @if (hasCustomLoginFooter()) {
2210
+ <div
2211
+ class="border-t border-border-subtle px-2xl py-xl pb-[max(1.5rem,env(safe-area-inset-bottom))]"
2212
+ >
2213
+ <ng-content select="[dgaHeaderLoginFooter]" />
2214
+ </div>
2215
+ } @else if (showLoginRegisterRow(lcfg) || (lcfg.footerLinks?.length ?? 0) > 0) {
2216
+ <div
2217
+ class="flex flex-col gap-md border-t border-border-subtle px-2xl py-xl
2218
+ pb-[max(1.5rem,env(safe-area-inset-bottom))]"
2219
+ >
2220
+ @if (showLoginRegisterRow(lcfg)) {
2221
+ <p class="m-0 text-center text-body-sm text-muted">
2222
+ {{ lcfg.registerPrompt || "Don't have an account?" }}
2223
+ @if (lcfg.registerLabel || lcfg.registerHref) {
2224
+ <a
2225
+ class="ms-xs font-medium text-fg-primary transition-opacity hover:opacity-80
2226
+ focus-visible:outline focus-visible:outline-2
2227
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
2228
+ [href]="appHref(lcfg.registerHref || '#')"
2229
+ >
2230
+ {{ lcfg.registerLabel || 'Sign up' }}
2231
+ </a>
2232
+ }
2233
+ </p>
2234
+ }
2235
+ @if (lcfg.footerLinks?.length) {
2236
+ <div class="flex flex-col gap-xs">
2237
+ @for (link of lcfg.footerLinks; track link.id ?? link.label) {
2238
+ <a
2239
+ class="text-center text-body-sm font-medium text-fg-primary transition-opacity
2240
+ hover:opacity-80
2241
+ focus-visible:outline focus-visible:outline-2
2242
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
2243
+ [href]="appHref(link.href || '#')"
2244
+ >
2245
+ {{ link.label }}
2246
+ </a>
2247
+ }
2248
+ </div>
2249
+ }
2250
+ </div>
2251
+ }
2252
+ </div>
2253
+ </div>
2254
+ }
2255
+
2256
+ <!-- Header user menu (authenticated) -->
2257
+ @if (userMenuOpen() && userConfig(); as ucfg) {
2258
+ <div
2259
+ [id]="userPanelId"
2260
+ class="dga-header-util-panel fixed w-[min(22rem,calc(100vw-2rem))] max-md:inset-x-3
2261
+ max-md:bottom-3 max-md:top-auto max-md:w-auto"
2262
+ role="menu"
2263
+ [attr.aria-label]="ucfg.name"
2264
+ [style]="dropdownPanelStyle()"
2265
+ >
2266
+ <div
2267
+ class="dga-panel-enter dga-panel-enter--anchor-end max-h-[min(85svh,36rem)]
2268
+ overflow-y-auto overscroll-contain rounded-2xl border border-border bg-card
2269
+ shadow-xl md:max-h-[none] md:overflow-visible md:rounded-md"
2270
+ >
2271
+ <div class="flex items-center gap-lg border-b border-border-subtle px-xl py-lg">
2272
+ <dga-avatar [name]="ucfg.name" [src]="ucfg.avatarSrc" size="md" />
2273
+ <div class="flex min-w-0 flex-1 flex-col gap-xxs">
2274
+ <span class="truncate text-body-md font-semibold text-display">{{ ucfg.name }}</span>
2275
+ @if (ucfg.role) {
2276
+ <span class="truncate text-body-sm text-muted">{{ ucfg.role }}</span>
2277
+ }
2278
+ @if (ucfg.email) {
2279
+ <span class="truncate text-body-sm text-paragraph">{{ ucfg.email }}</span>
2280
+ }
2281
+ </div>
2282
+ <button
2283
+ type="button"
2284
+ class="inline-flex size-5xl shrink-0 cursor-pointer items-center justify-center
2285
+ rounded-sm text-muted transition-colors hover:bg-neutral-light hover:text-fg
2286
+ focus-visible:outline focus-visible:outline-2
2287
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
2288
+ aria-label="Close"
2289
+ (click)="closeUserMenu()"
2290
+ >
2291
+ <dga-icon name="close" size="md" />
2292
+ </button>
2293
+ </div>
2294
+ <div class="flex flex-col gap-xxs p-sm">
2295
+ @for (link of ucfg.links || []; track link.id) {
2296
+ @if (link.id === 'logout') {
2297
+ <div class="my-xs border-t border-border-subtle" role="separator"></div>
2298
+ <button
2299
+ type="button"
2300
+ role="menuitem"
2301
+ class="flex w-full cursor-pointer items-center gap-md rounded-sm px-lg py-sm
2302
+ text-start text-body-md text-fg-error transition-colors hover:bg-error-light
2303
+ focus-visible:outline focus-visible:outline-2
2304
+ focus-visible:outline-offset-[-2px] focus-visible:outline-focus"
2305
+ (click)="onUserLink(link)"
2306
+ >
2307
+ @if (link.icon) {
2308
+ <dga-icon [name]="link.icon" size="md" />
2309
+ }
2310
+ <span>{{ link.label }}</span>
2311
+ </button>
2312
+ } @else if (link.href) {
2313
+ <a
2314
+ role="menuitem"
2315
+ class="flex w-full cursor-pointer items-center gap-md rounded-sm px-lg py-sm
2316
+ text-start text-body-md text-fg transition-colors hover:bg-neutral-light
2317
+ focus-visible:outline focus-visible:outline-2
2318
+ focus-visible:outline-offset-[-2px] focus-visible:outline-focus"
2319
+ [href]="appHref(link.href)"
2320
+ (click)="onUserLink(link)"
2321
+ >
2322
+ @if (link.icon) {
2323
+ <dga-icon [name]="link.icon" size="md" />
2324
+ }
2325
+ <span>{{ link.label }}</span>
2326
+ </a>
2327
+ } @else {
2328
+ <button
2329
+ type="button"
2330
+ role="menuitem"
2331
+ class="flex w-full cursor-pointer items-center gap-md rounded-sm px-lg py-sm
2332
+ text-start text-body-md text-fg transition-colors hover:bg-neutral-light
2333
+ focus-visible:outline focus-visible:outline-2
2334
+ focus-visible:outline-offset-[-2px] focus-visible:outline-focus"
2335
+ (click)="onUserLink(link)"
2336
+ >
2337
+ @if (link.icon) {
2338
+ <dga-icon [name]="link.icon" size="md" />
2339
+ }
2340
+ <span>{{ link.label }}</span>
2341
+ </button>
2342
+ }
2343
+ }
2344
+ </div>
2345
+ </div>
2346
+ </div>
2347
+ }
2348
+ </div>
2349
+ }
2350
+
2351
+ <!-- Mobile navigation: sheet | fullscreen | push -->
2352
+ @if (isPortal() && items().length) {
2353
+ @if (mobileOpen() && mobileNav() !== 'push') {
2354
+ <button
2355
+ type="button"
2356
+ class="fixed inset-0 cursor-default md:hidden"
2357
+ [class]="
2358
+ mobileNav() === 'fullscreen'
2359
+ ? 'z-[35] bg-overlay/60 backdrop-blur-[3px]'
2360
+ : 'z-10 bg-overlay/40 backdrop-blur-[1px]'
2361
+ "
2362
+ tabindex="-1"
2363
+ [attr.aria-label]="menuCloseLabel()"
2364
+ (click)="closeAll()"
2365
+ ></button>
2366
+ }
2367
+
2368
+ <div
2369
+ [id]="collapseId"
2370
+ [class]="mobilePanelClass()"
2371
+ [attr.data-mobile-nav]="mobileNav()"
2372
+ [attr.aria-hidden]="!mobileOpen()"
2373
+ [attr.inert]="mobileOpen() ? null : ''"
2374
+ >
2375
+ @if (mobileNav() === 'fullscreen') {
2376
+ <div
2377
+ class="flex h-(--size-header-portal) shrink-0 items-center justify-between
2378
+ gap-lg border-b border-border px-xl"
2379
+ >
2380
+ <div class="flex min-w-0 items-center gap-lg">
2381
+ @if (brandLogo()) {
2382
+ <img
2383
+ class="h-5xl w-auto max-w-[10rem] shrink-0 object-contain"
2384
+ [src]="brandLogo()"
2385
+ [alt]="brandLogoAlt()"
2386
+ />
2387
+ } @else {
2388
+ <span class="truncate text-body-md font-semibold text-display">{{
2389
+ brand() || brandLogoAlt() || navLabel()
2390
+ }}</span>
2391
+ }
2392
+ </div>
2393
+ <button
2394
+ type="button"
2395
+ class="inline-flex size-6xl shrink-0 cursor-pointer items-center justify-center
2396
+ rounded-md text-fg transition-colors hover:bg-neutral-light
2397
+ focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2
2398
+ focus-visible:outline-focus"
2399
+ [attr.aria-label]="menuCloseLabel()"
2400
+ (click)="closeAll()"
2401
+ >
2402
+ <dga-icon name="close" size="xl" />
2403
+ </button>
2404
+ </div>
2405
+ }
2406
+
2407
+ @if (mobileNav() === 'sheet') {
2408
+ <div
2409
+ class="flex min-h-(--size-header-portal) shrink-0 items-center justify-end
2410
+ border-b border-border-subtle px-md
2411
+ pt-[env(safe-area-inset-top,0px)]"
2412
+ >
2413
+ <button
2414
+ type="button"
2415
+ class="inline-flex size-6xl shrink-0 cursor-pointer items-center justify-center
2416
+ rounded-md text-fg transition-colors hover:bg-neutral-light
2417
+ focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2
2418
+ focus-visible:outline-focus"
2419
+ [attr.aria-label]="menuCloseLabel()"
2420
+ (click)="closeAll()"
2421
+ >
2422
+ <dga-icon name="close" size="xl" />
2423
+ </button>
2424
+ </div>
2425
+ }
2426
+
2427
+ @if (mobileNav() === 'push') {
2428
+ <div class="border-b border-border-subtle px-xl py-md">
2429
+ <p class="m-0 text-body-sm font-medium text-muted">{{ navLabel() }}</p>
2430
+ </div>
2431
+ }
2432
+
2433
+ <div
2434
+ class="min-h-0 flex-1 overflow-y-auto overscroll-contain px-xl py-lg
2435
+ pb-[max(1rem,env(safe-area-inset-bottom))]"
2436
+ >
2437
+ <ul class="m-0 flex list-none flex-col gap-none p-0" role="list">
2438
+ @for (item of items(); track item.label; let i = $index) {
2439
+ <li class="border-b border-border-subtle last:border-b-0">
2440
+ @if (item.dropdown) {
2441
+ <button
2442
+ type="button"
2443
+ [class]="mobileItemClass(!!item.current, openDropdown() === i)"
2444
+ [attr.aria-expanded]="openDropdown() === i"
2445
+ (click)="toggleDropdown(i, $event)"
2446
+ >
2447
+ <span class="min-w-0 flex-1" [class]="item.current ? 'text-fg-primary' : ''">{{
2448
+ item.label
2449
+ }}</span>
2450
+ <dga-icon
2451
+ name="chevronDown"
2452
+ size="md"
2453
+ class="shrink-0"
2454
+ [class]="
2455
+ openDropdown() === i
2456
+ ? 'rotate-180 text-fg-primary transition-transform duration-200'
2457
+ : 'text-muted transition-transform duration-200'
2458
+ "
2459
+ />
2460
+ </button>
2461
+
2462
+ @if (openDropdown() === i) {
2463
+ <div
2464
+ class="dga-panel-enter space-y-md border-s-2 border-primary/30 pb-lg ps-lg
2465
+ ms-sm"
2466
+ >
2467
+ @if (item.dropdown.featured; as feat) {
2468
+ <a
2469
+ class="flex flex-col gap-sm rounded-md border border-border
2470
+ bg-primary-faint p-xl text-start no-underline"
2471
+ [href]="appHref(feat.href || '#')"
2472
+ (click)="closeAll()"
2473
+ >
2474
+ <span class="text-body-md font-semibold text-display">{{
2475
+ feat.title
2476
+ }}</span>
2477
+ @if (feat.description) {
2478
+ <span class="text-body-sm text-paragraph">{{
2479
+ feat.description
2480
+ }}</span>
2481
+ }
2482
+ @if (feat.cta) {
2483
+ <span class="text-body-sm font-medium text-fg-primary">{{
2484
+ feat.cta
2485
+ }}</span>
2486
+ }
2487
+ </a>
2488
+ }
2489
+
2490
+ @if (item.dropdown.columns?.length) {
2491
+ @for (col of item.dropdown.columns!; track col.title ?? $index) {
2492
+ <div class="flex flex-col gap-xs">
2493
+ @if (col.title) {
2494
+ <p class="px-xl text-body-sm font-medium text-fg-primary">
2495
+ {{ col.title }}
2496
+ </p>
2497
+ }
2498
+ <ul class="m-0 flex list-none flex-col gap-none p-0">
2499
+ @for (link of col.links; track link.label) {
2500
+ <li>
2501
+ <a
2502
+ [class]="mobileLink"
2503
+ [href]="appHref(link.href || '#')"
2504
+ (click)="onNavLinkClick(link, $event); closeAll()"
2505
+ >
2506
+ <span>{{ link.label }}</span>
2507
+ @if (link.description) {
2508
+ <span class="text-body-sm text-muted">{{
2509
+ link.description
2510
+ }}</span>
2511
+ }
2512
+ </a>
2513
+ </li>
2514
+ }
2515
+ </ul>
2516
+ </div>
2517
+ }
2518
+ } @else {
2519
+ <ul class="m-0 flex list-none flex-col gap-none p-0">
2520
+ @for (link of flatLinks(item); track link.label) {
2521
+ <li>
2522
+ <a
2523
+ [class]="mobileLink"
2524
+ [href]="appHref(link.href || '#')"
2525
+ (click)="onNavLinkClick(link, $event); closeAll()"
2526
+ >
2527
+ <span>{{ link.label }}</span>
2528
+ @if (link.description) {
2529
+ <span class="text-body-sm text-muted">{{
2530
+ link.description
2531
+ }}</span>
2532
+ }
2533
+ </a>
2534
+ </li>
2535
+ }
2536
+ </ul>
2537
+ }
2538
+ </div>
2539
+ }
2540
+ } @else {
2541
+ <a
2542
+ [class]="mobileItemClass(!!item.current)"
2543
+ [attr.aria-current]="item.current ? 'page' : null"
2544
+ [href]="appHref(item.href || '#')"
2545
+ (click)="onNavLinkClick(item, $event); closeAll()"
2546
+ >
2547
+ @if (item.current) {
2548
+ <span
2549
+ class="absolute inset-y-md start-0 w-1 rounded-e-sm bg-primary"
2550
+ aria-hidden="true"
2551
+ ></span>
2552
+ }
2553
+ <span class="min-w-0 flex-1" [class]="item.current ? 'text-fg-primary' : ''">{{
2554
+ item.label
2555
+ }}</span>
2556
+ </a>
2557
+ }
2558
+ </li>
2559
+ }
2560
+ </ul>
2561
+ </div>
2562
+
2563
+ @if (actions().length) {
2564
+ <div
2565
+ class="flex shrink-0 items-center justify-around gap-sm border-t border-border
2566
+ bg-nav px-xl py-md pb-[max(0.75rem,env(safe-area-inset-bottom))]"
2567
+ >
2568
+ @for (action of actions(); track action.id) {
2569
+ @if (action.href) {
2570
+ <a
2571
+ class="relative inline-flex min-h-6xl min-w-6xl cursor-pointer flex-col
2572
+ items-center justify-center gap-xxs rounded-md px-md text-fg
2573
+ hover:bg-neutral-light
2574
+ focus-visible:outline focus-visible:outline-2
2575
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
2576
+ [href]="appHref(action.href)"
2577
+ [attr.aria-label]="action.label"
2578
+ [attr.data-dga-action]="action.id"
2579
+ (click)="closeAll()"
2580
+ >
2581
+ @if (action.icon) {
2582
+ <dga-icon [name]="action.icon" size="xl" />
2583
+ }
2584
+ <span class="max-w-11xl truncate text-body-2xs">{{ action.label }}</span>
2585
+ @if (action.badge) {
2586
+ <span [class]="badgeClass()">{{ action.badge }}</span>
2587
+ }
2588
+ </a>
2589
+ } @else {
2590
+ <button
2591
+ type="button"
2592
+ class="relative inline-flex min-h-6xl min-w-6xl cursor-pointer flex-col
2593
+ items-center justify-center gap-xxs rounded-md px-md text-fg
2594
+ hover:bg-neutral-light
2595
+ focus-visible:outline focus-visible:outline-2
2596
+ focus-visible:outline-offset-2 focus-visible:outline-focus"
2597
+ [attr.aria-label]="action.label"
2598
+ [attr.data-dga-action]="action.id"
2599
+ (click)="onMobileAction(action, $event)"
2600
+ >
2601
+ @if (action.icon) {
2602
+ <dga-icon [name]="action.icon" size="xl" />
2603
+ }
2604
+ <span class="max-w-11xl truncate text-body-2xs">{{ action.label }}</span>
2605
+ @if (action.badge) {
2606
+ <span [class]="badgeClass()">{{ action.badge }}</span>
2607
+ }
2608
+ </button>
2609
+ }
2610
+ }
2611
+ </div>
2612
+ }
2613
+ </div>
2614
+ }
2615
+ `,host:{role:"banner","[class]":"hostClasses()","[attr.data-appearance]":"appearance()","[attr.data-mobile-nav]":"mobileNav()","[attr.data-mobile-open]":"mobileOpen() || null","(mouseleave)":"onHostLeave($event)"},styles:["@keyframes dga-fade-in{0%{opacity:0}to{opacity:1}}@keyframes dga-panel-in{0%{opacity:0;transform:translateY(-.625rem) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes dga-chip-in{0%{opacity:0;transform:translateY(.25rem)}to{opacity:1;transform:translateY(0)}}@keyframes dga-notif-row-in{0%{opacity:0;transform:translateY(.35rem)}to{opacity:1;transform:translateY(0)}}@keyframes dga-expand-fade{0%{opacity:0;transform:translateY(-.25rem)}to{opacity:1;transform:translateY(0)}}@keyframes dga-spin{to{transform:rotate(360deg)}}@keyframes dga-badge-pulse{0%,to{transform:scale(1)}50%{transform:scale(1.12)}}@keyframes dga-push-in{0%{opacity:0;transform:translateY(-.75rem)}to{opacity:1;transform:translateY(0)}}@keyframes dga-sheet-in-ltr{0%{opacity:.85;transform:translate3d(-100%,0,0)}to{opacity:1;transform:translateZ(0)}}@keyframes dga-sheet-in-rtl{0%{opacity:.85;transform:translate3d(100%,0,0)}to{opacity:1;transform:translateZ(0)}}@keyframes dga-fs-in{0%{opacity:0;transform:translateY(.5rem)}to{opacity:1;transform:translateY(0)}}@keyframes dga-sheet-in{0%{opacity:0;transform:translateY(1.25rem)}to{opacity:1;transform:translateY(0)}}:host .dga-util-backdrop,.dga-header-util-layer .dga-util-backdrop{animation:dga-fade-in .22s ease both}.dga-header-util-layer{position:fixed;inset:0;z-index:var(--z-index-urgent, 400);pointer-events:none}.dga-header-util-layer>.dga-util-backdrop,.dga-header-util-layer>.dga-header-util-panel{pointer-events:auto}:host .dga-panel-enter,.dga-header-util-layer .dga-panel-enter{animation:dga-panel-in .28s cubic-bezier(.16,1,.3,1) both;transform-origin:top center;will-change:transform,opacity}:host .dga-panel-enter--anchor-end,.dga-header-util-layer .dga-panel-enter--anchor-end{transform-origin:top right}:host-context([dir=\"rtl\"]) .dga-panel-enter--anchor-end,:host([dir=\"rtl\"]) .dga-panel-enter--anchor-end,:host-context([dir=\"rtl\"]) .dga-header-util-layer .dga-panel-enter--anchor-end,[dir=rtl] .dga-header-util-layer .dga-panel-enter--anchor-end{transform-origin:top left}@media(max-width:767px){:host .dga-panel-enter--anchor-end,.dga-header-util-layer .dga-panel-enter--anchor-end{animation-name:dga-sheet-in;transform-origin:bottom center}}:host .dga-search-chip{animation:dga-chip-in .28s cubic-bezier(.16,1,.3,1) both}:host .dga-search-input{-webkit-appearance:none;appearance:none}:host .dga-search-input::-webkit-search-decoration,:host .dga-search-input::-webkit-search-cancel-button,:host .dga-search-input::-webkit-search-results-button,:host .dga-search-input::-webkit-search-results-decoration{-webkit-appearance:none;appearance:none;display:none}:host .dga-search-voice.dga-search-voice--active{color:var(--dga-text-primary, #1b8354)!important;background:var(--dga-bg-primary-faint, #f3faf6)!important}:host .dga-notif-item,.dga-header-util-layer .dga-notif-item{animation:dga-notif-row-in .26s cubic-bezier(.16,1,.3,1) both}:host .dga-notif-actions,.dga-header-util-layer .dga-notif-actions{animation:dga-expand-fade .2s ease both}:host .dga-login-spinner,.dga-header-util-layer .dga-login-spinner{animation:dga-spin .7s linear infinite}:host .dga-action-badge{animation:dga-badge-pulse 1.8s ease-in-out infinite;transform-origin:center}@media(prefers-reduced-motion:reduce){:host .dga-util-backdrop,:host .dga-panel-enter,:host .dga-search-chip,:host .dga-notif-item,:host .dga-notif-actions,.dga-header-util-layer .dga-util-backdrop,.dga-header-util-layer .dga-panel-enter{animation-duration:.01ms!important;animation-delay:0ms!important}}:host .dga-burger__box{position:relative;display:block;width:1.375rem;height:1rem}:host .dga-burger__line{position:absolute;inset-inline:0;height:2px;border-radius:1px;background:currentColor;transition:transform .22s cubic-bezier(.4,0,.2,1),opacity .16s ease,top .22s cubic-bezier(.4,0,.2,1)}:host .dga-burger__line:nth-child(1){top:0}:host .dga-burger__line:nth-child(2){top:calc(50% - 1px)}:host .dga-burger__line:nth-child(3){top:calc(100% - 2px)}:host .dga-burger--open{background:var(--color-neutral-light, #f3f4f6)}:host .dga-burger--hidden{visibility:hidden;pointer-events:none}:host .dga-burger--open .dga-burger__line:nth-child(1){top:calc(50% - 1px);transform:rotate(45deg)}:host .dga-burger--open .dga-burger__line:nth-child(2){opacity:0;transform:scaleX(.4)}:host .dga-burger--open .dga-burger__line:nth-child(3){top:calc(50% - 1px);transform:rotate(-45deg)}:host .dga-mobile-nav-closed{display:none}:host .dga-mobile-nav-open{display:flex}:host .dga-mobile-push-open{animation:dga-push-in .24s cubic-bezier(.4,0,.2,1) forwards}:host .dga-mobile-sheet-open{animation:dga-sheet-in-rtl .28s cubic-bezier(.4,0,.2,1) forwards}:host-context([dir=\"rtl\"]) .dga-mobile-sheet-open{animation:dga-sheet-in-ltr .28s cubic-bezier(.4,0,.2,1) forwards}:host .dga-mobile-fs-open{animation:dga-fs-in .24s cubic-bezier(.4,0,.2,1) forwards}@media(prefers-reduced-motion:reduce){:host .dga-action-badge,:host .dga-mobile-push-open,:host .dga-mobile-sheet-open,:host .dga-mobile-fs-open,:host-context([dir=\"rtl\"]) .dga-mobile-sheet-open,:host .dga-burger__line{animation:none;transition:none}}\n"]}]}],ctorParameters:()=>[],propDecorators:{brand:[{type:i.Input,args:[{isSignal:!0,alias:"brand",required:!1}]}],brandSlogan:[{type:i.Input,args:[{isSignal:!0,alias:"brandSlogan",required:!1}]}],brandLogo:[{type:i.Input,args:[{isSignal:!0,alias:"brandLogo",required:!1}]}],brandLogoAlt:[{type:i.Input,args:[{isSignal:!0,alias:"brandLogoAlt",required:!1}]}],brandPlaceholder:[{type:i.Input,args:[{isSignal:!0,alias:"brandPlaceholder",required:!1}]}],brandHref:[{type:i.Input,args:[{isSignal:!0,alias:"brandHref",required:!1}]}],navLabel:[{type:i.Input,args:[{isSignal:!0,alias:"navLabel",required:!1}]}],menuToggleLabel:[{type:i.Input,args:[{isSignal:!0,alias:"menuToggleLabel",required:!1}]}],menuCloseLabel:[{type:i.Input,args:[{isSignal:!0,alias:"menuCloseLabel",required:!1}]}],appearance:[{type:i.Input,args:[{isSignal:!0,alias:"appearance",required:!1}]}],mobileNav:[{type:i.Input,args:[{isSignal:!0,alias:"mobileNav",required:!1}]}],sticky:[{type:i.Input,args:[{isSignal:!0,alias:"sticky",required:!1}]}],items:[{type:i.Input,args:[{isSignal:!0,alias:"items",required:!1}]}],actions:[{type:i.Input,args:[{isSignal:!0,alias:"actions",required:!1}]}],search:[{type:i.Input,args:[{isSignal:!0,alias:"search",required:!1}]}],searchQuery:[{type:i.Input,args:[{isSignal:!0,alias:"searchQuery",required:!1}]},{type:i.Output,args:["searchQueryChange"]}],notifications:[{type:i.Input,args:[{isSignal:!0,alias:"notifications",required:!1}]}],login:[{type:i.Input,args:[{isSignal:!0,alias:"login",required:!1}]}],user:[{type:i.Input,args:[{isSignal:!0,alias:"user",required:!1}]}],authenticated:[{type:i.Input,args:[{isSignal:!0,alias:"authenticated",required:!1}]},{type:i.Output,args:["authenticatedChange"]}],loginSubmitting:[{type:i.Input,args:[{isSignal:!0,alias:"loginSubmitting",required:!1}]},{type:i.Output,args:["loginSubmittingChange"]}],loginError:[{type:i.Input,args:[{isSignal:!0,alias:"loginError",required:!1}]},{type:i.Output,args:["loginErrorChange"]}],actionClick:[{type:i.Output,args:["actionClick"]}],searchSubmit:[{type:i.Output,args:["searchSubmit"]}],searchSuggestionSelect:[{type:i.Output,args:["searchSuggestionSelect"]}],searchVoice:[{type:i.Output,args:["searchVoice"]}],notificationAction:[{type:i.Output,args:["notificationAction"]}],loginSubmit:[{type:i.Output,args:["loginSubmit"]}],logout:[{type:i.Output,args:["logout"]}],userLinkClick:[{type:i.Output,args:["userLinkClick"]}],navLinkClick:[{type:i.Output,args:["navLinkClick"]}],customLoginForm:[{type:i.ContentChild,args:[i.forwardRef(()=>m),{isSignal:!0}]}],customLoginFooter:[{type:i.ContentChild,args:[i.forwardRef(()=>p),{isSignal:!0}]}],searchInputRef:[{type:i.ViewChild,args:["searchInput",{isSignal:!0}]}],utilLayerRef:[{type:i.ViewChild,args:["utilLayer",{isSignal:!0}]}],onCloseHeaderOverlays:[{type:b,args:["document:dga-close-header-overlays"]}],onKey:[{type:b,args:["document:keydown",["$event"]]}],onScroll:[{type:b,args:["window:scroll"]}],onResize:[{type:b,args:["window:resize"]}],onDocClick:[{type:b,args:["document:click",["$event"]]}]}});export{v as DgaHeader,x as DgaHeaderLoginAfterFields,h as DgaHeaderLoginBeforeForm,p as DgaHeaderLoginFooter,m as DgaHeaderLoginForm};