{ "version": 3, "sources": ["src/app/sidenav/_components/side-nav-item/side-nav-item.component.ts", "src/app/sidenav/_components/side-nav-item/side-nav-item.component.html", "src/app/_pipes/setting-fragment.pipe.ts", "src/app/sidenav/preference-nav/preference-nav.component.ts", "src/app/sidenav/preference-nav/preference-nav.component.html"], "sourcesContent": ["import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, Input, OnInit} from '@angular/core';\r\nimport {NavigationEnd, Router, RouterLink} from '@angular/router';\r\nimport {filter, map, tap} from 'rxjs';\r\nimport {NavService} from 'src/app/_services/nav.service';\r\nimport {takeUntilDestroyed} from \"@angular/core/rxjs-interop\";\r\nimport {AsyncPipe, NgClass, NgOptimizedImage, NgTemplateOutlet} from \"@angular/common\";\r\nimport {ImageComponent} from \"../../../shared/image/image.component\";\r\nimport {Breakpoint, UtilityService} from \"../../../shared/_services/utility.service\";\r\n\r\n\r\n@Component({\r\n selector: 'app-side-nav-item',\r\n standalone: true,\r\n imports: [RouterLink, NgOptimizedImage, ImageComponent, NgTemplateOutlet, NgClass, AsyncPipe],\r\n templateUrl: './side-nav-item.component.html',\r\n styleUrls: ['./side-nav-item.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush\r\n})\r\nexport class SideNavItemComponent implements OnInit {\r\n private readonly destroyRef = inject(DestroyRef);\r\n private readonly router = inject(Router);\r\n private readonly cdRef = inject(ChangeDetectorRef);\r\n protected readonly navService = inject(NavService);\r\n protected readonly utilityService = inject(UtilityService);\r\n\r\n /**\r\n * Id for automatic scrolling to.\r\n */\r\n @Input() id: string | null = null;\r\n\r\n /**\r\n * Icon to display next to item. ie) 'fa-home'\r\n */\r\n @Input() icon: string = '';\r\n @Input() imageUrl: string | null = '';\r\n /**\r\n * Removes all the space around the icon area\r\n */\r\n @Input() noIcon: boolean = false;\r\n /**\r\n * Text for the item\r\n */\r\n @Input() title: string = '';\r\n\r\n /**\r\n * If a link should be generated when clicked. By default (undefined), no link will be generated\r\n */\r\n @Input() link: string | undefined;\r\n /**\r\n * If external, link will be used as full href and rel will be applied\r\n */\r\n @Input() external: boolean = false;\r\n /**\r\n * If using a link, then you can pass optional queryParameters\r\n */\r\n @Input() queryParams: any | undefined = undefined;\r\n /**\r\n * If using a lin, then you can pass optional fragment to append to the end\r\n */\r\n @Input() fragment: string | undefined = undefined;\r\n /**\r\n * Optional count to pass in that will show as a red badge on the side, indicating some action needs to be taken\r\n */\r\n @Input() badgeCount: number | null = -1;\r\n\r\n\r\n @Input() comparisonMethod: 'startsWith' | 'equals' = 'equals';\r\n\r\n\r\n\r\n highlighted = false;\r\n\r\n constructor() {\r\n this.router.events\r\n .pipe(\r\n filter(event => event instanceof NavigationEnd),\r\n takeUntilDestroyed(this.destroyRef),\r\n map(evt => evt as NavigationEnd),\r\n tap((evt: NavigationEnd) => this.triggerHighlightCheck(evt.url)),\r\n tap(_ => this.collapseNavIfApplicable())\r\n ).subscribe();\r\n }\r\n\r\n ngOnInit(): void {\r\n setTimeout(() => {\r\n this.triggerHighlightCheck(this.router.url);\r\n }, 100);\r\n }\r\n\r\n triggerHighlightCheck(routeUrl: string) {\r\n const [url, queryParams] = routeUrl.split('?');\r\n const [page, fragment = ''] = url.split('#');\r\n\r\n this.updateHighlight(page, queryParams, url.includes('#') ? fragment : undefined);\r\n }\r\n\r\n\r\n updateHighlight(page: string, queryParams?: string, fragment?: string) {\r\n if (this.link === undefined) {\r\n this.highlighted = false;\r\n this.cdRef.markForCheck();\r\n return;\r\n }\r\n\r\n if (!page.endsWith('/') && !queryParams && this.fragment === undefined && queryParams === undefined) {\r\n page = page + '/';\r\n }\r\n\r\n let fragmentEqual = false;\r\n if (fragment === this.fragment) {\r\n fragmentEqual = true;\r\n }\r\n if (this.fragment === '' && fragment === undefined) { // This is the case where we load a fragment of nothing and browser removes the #\r\n fragmentEqual = true;\r\n }\r\n\r\n const queryParamsEqual = this.queryParams === queryParams;\r\n\r\n if (this.comparisonMethod === 'equals' && page === this.link && fragmentEqual && queryParamsEqual) {\r\n this.highlighted = true;\r\n this.cdRef.markForCheck();\r\n return;\r\n }\r\n\r\n if (this.comparisonMethod === 'startsWith' && page.startsWith(this.link)) {\r\n if (queryParams && queryParams === this.queryParams && fragmentEqual) {\r\n this.highlighted = true;\r\n this.cdRef.markForCheck();\r\n return;\r\n }\r\n\r\n this.highlighted = true;\r\n this.cdRef.markForCheck();\r\n return;\r\n }\r\n\r\n this.highlighted = false;\r\n this.cdRef.markForCheck();\r\n }\r\n\r\n openLink() {\r\n this.collapseNavIfApplicable();\r\n\r\n if (Object.keys(this.queryParams).length !== 0) {\r\n this.router.navigateByUrl(this.link + '?' + this.queryParams);\r\n return;\r\n } else if (this.fragment) {\r\n this.router.navigateByUrl(this.link + '#' + this.fragment);\r\n return;\r\n }\r\n\r\n this.router.navigateByUrl(this.link!);\r\n }\r\n\r\n // If on mobile, automatically collapse the side nav after making a selection\r\n collapseNavIfApplicable() {\r\n if (this.utilityService.getActiveBreakpoint() < Breakpoint.Tablet) {\r\n this.navService.collapseSideNav(true);\r\n }\r\n }\r\n\r\n}\r\n", "@if (link === undefined || link.length === 0) {\r\n