File

components/content/content.component.ts

Description

Main content for application.

Angular material expansion panels with links to swagger pages of all services.

Implements

AfterViewInit

Metadata

Index

Properties
Methods

Constructor

constructor(templateService: NshmpTemplateService, el: ElementRef, route: ActivatedRoute, router: Router, hazardService: HazardService, nshmpService: NshmpService)
Parameters :
Name Type Optional
templateService NshmpTemplateService No
el ElementRef<HTMLDivElement> No
route ActivatedRoute No
router Router No
hazardService HazardService No
nshmpService NshmpService No

Methods

Private addActive
addActive(cardEl: Element)

Add active class name to element.

Parameters :
Name Type Optional Description
cardEl Element No

The mat card element

Returns : void
Private checkUrl
checkUrl()

Check URL parameters.

Returns : void
ngAfterViewInit
ngAfterViewInit()
Returns : void
onExpansionPanelClosed
onExpansionPanelClosed()

Reset navigation parameters.

Returns : void
onExpansionPanelOpen
onExpansionPanelOpen(serviceGroup: ServiceGroup)

Set URL query parameters.

Parameters :
Name Type Optional Description
serviceGroup ServiceGroup No

The service group opened

Returns : void
onLinkIconClick
onLinkIconClick(serviceGroup: string)

Update query parameters on URL on link click.

Parameters :
Name Type Optional Description
serviceGroup string No

The service group

Returns : void
onNavClick
onNavClick(serviceGroup: string)

Scroll to expansion panel and set URL parameters.

Parameters :
Name Type Optional Description
serviceGroup string No

The service group clicked

Returns : void
onScroll
onScroll()

Handle on scroll on scroll event.

Highlight navigation if scrolling over expansion panel.

Returns : void
Private resetActive
resetActive()

Remove active class name from menu.

Returns : void
toServiceId
toServiceId(service: ServiceConfig)

Convert label to id.

Parameters :
Name Type Optional Description
service ServiceConfig No

The service

Returns : string

Properties

cardTriggerHeight
Type : number
Default value : 100

Trigger height

dynamicHazardServices$
Type : Observable<ServiceConfig[]>
Default value : this.hazardService .dynamicNshmService$( `${this.nshmpHazWs.url}${this.nshmpHazWs.services.nshms}`, ) .pipe( map(usage => { return usage.response.map(nshm => ({ contextPath: nshm.url.split('.gov').pop(), label: nshm.label, swaggerEndPoint: this.nshmpHazWs.services.curveServices.swagger, url: nshm.url, })); }), )

Dynamic hazard services

nshmpHazWs
Default value : environment.webServices.nshmpHazWs
nshmpWsStatic
Default value : environment.webServices.nshmpWsStatic
serviceGroups
Type : ServiceGroup[]
Default value : [ // Dynamic hazard caluclations { applicationsUsedIn: [ APPS.hazard.disagg, APPS.hazard.dynamic, APPS.source.mfd, APPS.source.rateAndProbability, ], id: ServiceGroupId.NSHMP_HAZ, images: [ { darkModeImage: 'dynamic-hazard-dark-mode.webp', image: 'dynamic-hazard.webp', }, { darkModeImage: 'disagg-dark-mode.jpeg', image: 'disagg.jpeg', }, ], services$: this.dynamicHazardServices$, subtitle: 'Hazard, Disaggregation, Rate, and Probabilities of a NSHM', title: 'NSHM Dynamic Hazard Calculations', }, // Data services { applicationsUsedIn: [...gmmApps(), APPS.source.data], id: ServiceGroupId.NSHMP_WS, images: [ { darkModeImage: 'gmm-spectra-dark-mode.webp', image: 'gmm-spectra.webp', }, { image: 'fault-sections.jpeg', }, ], services$: of([ { ...environment.webServices.data, swaggerEndPoint: environment.webServices.data.services.swagger, }, ]), subtitle: 'Ground Motion Models, Faults Sections, and GPS Data', title: 'Data Services', }, // Static hazard services { applicationsUsedIn: [APPS.hazard.static], id: ServiceGroupId.NSHMP_WS_STATIC_HAZARD, images: [ { darkModeImage: 'static-hazard-dark-mode.webp', image: 'static-hazard.webp', }, { darkModeImage: 'static-hazard-spectra-dark-mode.webp', image: 'static-hazard-spectra.webp', }, ], services$: this.staticHazardServices$, subtitle: 'Pre-Computed and Published Hazard Curves', title: 'NSHM Static Hazard Curve Services', }, // RTGM services { applicationsUsedIn: [apps().designMaps.rtgm], id: ServiceGroupId.RTGM, images: [ { darkModeImage: 'rtgm-derivative-dark-mode.webp', image: 'rtgm-derivative.webp', }, { darkModeImage: 'rtgm-integral-dark-mode.webp', image: 'rtgm-integral.webp', }, ], services$: of([ { ...environment.webServices.rtgm, swaggerEndPoint: environment.webServices.rtgm.services.swagger, }, ]), subtitle: 'RTGM from hazard curves', title: 'Risk-Targeted Ground Motion Service', }, // NCM services { applicationsUsedIn: [], id: ServiceGroupId.NCM, images: [ { image: 'ncm-geophys.png', }, { image: 'ncm-temp.png', }, ], services$: of([ { ...environment.webServices.ncm, swaggerEndPoint: environment.webServices.ncm.services.swagger, }, ]), subtitle: 'Geologic, Geophysical, Mineral, and Temperature Profiles', title: 'National Crustal Model Services', }, // AASHTO 2023 services { applicationsUsedIn: [], id: ServiceGroupId.AASHTO_2023, images: [{image: 'aashto-logo.jpeg'}], services$: of([ { ...environment.webServices.aashto2023, swaggerEndPoint: environment.webServices.aashto2023.services.swagger, }, ]), subtitle: 'Risk-Targeted Desgin Response Spectra for 2023 Edition', title: 'AASHTO 2023 Services', }, ]

List of web services.

sidenavCollapsed
Default value : false

Whether sidenav is collapsed

staticHazardServices$
Type : Observable<ServiceConfig[]>
Default value : this.hazardService .staticNshmService$( `${this.nshmpWsStatic.url}${this.nshmpWsStatic.services.nshms}`, ) .pipe( map(usage => { const nshmpWsStatic = environment.webServices.nshmpWsStatic; return usage.response.map(nshm => ({ contextPath: nshm.url.split('.gov').pop(), label: nshm.label, swaggerEndPoint: nshmpWsStatic.services.curveServices.swagger, url: nshm.url, })); }), )

Static hazard services

Public templateService
Type : NshmpTemplateService
import {AsyncPipe, NgClass} from '@angular/common';
import {AfterViewInit, Component, ElementRef} from '@angular/core';
import {MatButton, MatIconButton} from '@angular/material/button';
import {
  MatCard,
  MatCardContent,
  MatCardHeader,
  MatCardSubtitle,
  MatCardTitle,
} from '@angular/material/card';
import {MatDivider} from '@angular/material/divider';
import {MatIcon} from '@angular/material/icon';
import {MatListItem, MatListItemLine, MatNavList} from '@angular/material/list';
import {
  MatSidenav,
  MatSidenavContainer,
  MatSidenavContent,
} from '@angular/material/sidenav';
import {ActivatedRoute, Router, RouterLink} from '@angular/router';
import {HazardService} from '@ghsc/nshmp-lib-ng/hazard';
import {NshmpService} from '@ghsc/nshmp-lib-ng/nshmp';
import {NshmpTemplateService} from '@ghsc/nshmp-template';
import {environment} from 'projects/nshmp-apps/src/environments/environment';
import {apps} from 'projects/nshmp-apps/src/shared/utils/applications.utils';
import {gmmApps} from 'projects/nshmp-apps/src/shared/utils/navigation.utils';
import {map, Observable, of} from 'rxjs';

import {QueryParameters} from '../../models/query-parameters.model';
import {
  ServiceConfig,
  ServiceGroup,
  ServiceGroupId,
} from '../../models/service.model';

const APPS = apps();

/**
 * Main content for application.
 *
 * Angular material expansion panels with links to swagger pages of all services.
 */
@Component({
  imports: [
    MatSidenavContainer,
    MatSidenav,
    NgClass,
    MatNavList,
    MatListItem,
    MatListItemLine,
    MatButton,
    MatIcon,
    MatSidenavContent,
    MatCard,
    MatCardHeader,
    MatCardTitle,
    MatIconButton,
    MatCardSubtitle,
    MatCardContent,
    MatDivider,
    RouterLink,
    AsyncPipe,
  ],
  selector: 'app-content',
  styleUrl: './content.component.scss',
  templateUrl: './content.component.html',
})
export class ContentComponent implements AfterViewInit {
  nshmpHazWs = environment.webServices.nshmpHazWs;
  nshmpWsStatic = environment.webServices.nshmpWsStatic;

  /** Trigger height */
  cardTriggerHeight = 100;

  /** Dynamic hazard services */
  dynamicHazardServices$: Observable<ServiceConfig[]> = this.hazardService
    .dynamicNshmService$(
      `${this.nshmpHazWs.url}${this.nshmpHazWs.services.nshms}`,
    )
    .pipe(
      map(usage => {
        return usage.response.map(nshm => ({
          contextPath: nshm.url.split('.gov').pop(),
          label: nshm.label,
          swaggerEndPoint: this.nshmpHazWs.services.curveServices.swagger,
          url: nshm.url,
        }));
      }),
    );

  /** Static hazard services */
  staticHazardServices$: Observable<ServiceConfig[]> = this.hazardService
    .staticNshmService$(
      `${this.nshmpWsStatic.url}${this.nshmpWsStatic.services.nshms}`,
    )
    .pipe(
      map(usage => {
        const nshmpWsStatic = environment.webServices.nshmpWsStatic;

        return usage.response.map(nshm => ({
          contextPath: nshm.url.split('.gov').pop(),
          label: nshm.label,
          swaggerEndPoint: nshmpWsStatic.services.curveServices.swagger,
          url: nshm.url,
        }));
      }),
    );

  /** Whether sidenav is collapsed */
  sidenavCollapsed = false;

  /**
   * List of web services.
   */
  serviceGroups: ServiceGroup[] = [
    // Dynamic hazard caluclations
    {
      applicationsUsedIn: [
        APPS.hazard.disagg,
        APPS.hazard.dynamic,
        APPS.source.mfd,
        APPS.source.rateAndProbability,
      ],
      id: ServiceGroupId.NSHMP_HAZ,
      images: [
        {
          darkModeImage: 'dynamic-hazard-dark-mode.webp',
          image: 'dynamic-hazard.webp',
        },
        {
          darkModeImage: 'disagg-dark-mode.jpeg',
          image: 'disagg.jpeg',
        },
      ],
      services$: this.dynamicHazardServices$,
      subtitle: 'Hazard, Disaggregation, Rate, and Probabilities of a NSHM',
      title: 'NSHM Dynamic Hazard Calculations',
    },
    // Data services
    {
      applicationsUsedIn: [...gmmApps(), APPS.source.data],
      id: ServiceGroupId.NSHMP_WS,
      images: [
        {
          darkModeImage: 'gmm-spectra-dark-mode.webp',
          image: 'gmm-spectra.webp',
        },
        {
          image: 'fault-sections.jpeg',
        },
      ],
      services$: of([
        {
          ...environment.webServices.data,
          swaggerEndPoint: environment.webServices.data.services.swagger,
        },
      ]),
      subtitle: 'Ground Motion Models, Faults Sections, and GPS Data',
      title: 'Data Services',
    },
    // Static hazard services
    {
      applicationsUsedIn: [APPS.hazard.static],
      id: ServiceGroupId.NSHMP_WS_STATIC_HAZARD,
      images: [
        {
          darkModeImage: 'static-hazard-dark-mode.webp',
          image: 'static-hazard.webp',
        },
        {
          darkModeImage: 'static-hazard-spectra-dark-mode.webp',
          image: 'static-hazard-spectra.webp',
        },
      ],
      services$: this.staticHazardServices$,
      subtitle: 'Pre-Computed and Published Hazard Curves',
      title: 'NSHM Static Hazard Curve Services',
    },
    // RTGM services
    {
      applicationsUsedIn: [apps().designMaps.rtgm],
      id: ServiceGroupId.RTGM,
      images: [
        {
          darkModeImage: 'rtgm-derivative-dark-mode.webp',
          image: 'rtgm-derivative.webp',
        },
        {
          darkModeImage: 'rtgm-integral-dark-mode.webp',
          image: 'rtgm-integral.webp',
        },
      ],
      services$: of([
        {
          ...environment.webServices.rtgm,
          swaggerEndPoint: environment.webServices.rtgm.services.swagger,
        },
      ]),
      subtitle: 'RTGM from hazard curves',
      title: 'Risk-Targeted Ground Motion Service',
    },
    // NCM services
    {
      applicationsUsedIn: [],
      id: ServiceGroupId.NCM,
      images: [
        {
          image: 'ncm-geophys.png',
        },
        {
          image: 'ncm-temp.png',
        },
      ],
      services$: of([
        {
          ...environment.webServices.ncm,
          swaggerEndPoint: environment.webServices.ncm.services.swagger,
        },
      ]),
      subtitle: 'Geologic, Geophysical, Mineral, and Temperature Profiles',
      title: 'National Crustal Model Services',
    },
    // AASHTO 2023 services
    {
      applicationsUsedIn: [],
      id: ServiceGroupId.AASHTO_2023,
      images: [{image: 'aashto-logo.jpeg'}],
      services$: of([
        {
          ...environment.webServices.aashto2023,
          swaggerEndPoint: environment.webServices.aashto2023.services.swagger,
        },
      ]),
      subtitle: 'Risk-Targeted Desgin Response Spectra for 2023 Edition',
      title: 'AASHTO 2023 Services',
    },
  ];

  constructor(
    public templateService: NshmpTemplateService,
    private el: ElementRef<HTMLDivElement>,
    private route: ActivatedRoute,
    private router: Router,
    private hazardService: HazardService,
    private nshmpService: NshmpService,
  ) {}

  ngAfterViewInit() {
    this.checkUrl();
    this.onScroll();
  }

  /**
   * Reset navigation parameters.
   */
  onExpansionPanelClosed(): void {
    this.router
      .navigate([])
      .catch((error: Error) => this.nshmpService.throwError$(error));
  }

  /**
   * Set URL query parameters.
   *
   * @param serviceGroup The service group opened
   * @param service The service
   */
  onExpansionPanelOpen(serviceGroup: ServiceGroup): void {
    const queryParams: QueryParameters = {
      serviceGroup: serviceGroup.id,
    };

    this.router
      .navigate([], {
        queryParams: {
          ...this.route.snapshot.queryParams,
          ...queryParams,
        },
      })
      .catch((error: Error) => this.nshmpService.throwError$(error));
  }

  /**
   * Update query parameters on URL on link click.
   *
   * @param serviceGroup The service group
   */
  onLinkIconClick(serviceGroup: string): void {
    const queryParams: QueryParameters = {
      serviceGroup,
    };

    this.router
      .navigate([], {
        queryParams,
      })
      .catch((error: Error) => this.nshmpService.throwError$(error));
  }

  /**
   * Scroll to expansion panel and set URL parameters.
   *
   * @param serviceGroup The service group clicked
   */
  onNavClick(serviceGroup: string): void {
    const serviceEl = this.el.nativeElement.querySelector(`#${serviceGroup}`);
    serviceEl?.scrollIntoView();
    this.onLinkIconClick(serviceGroup);

    setTimeout(() => {
      this.resetActive();
      this.addActive(serviceEl);
    }, 0);
  }

  /**
   * Handle on scroll on scroll event.
   *
   * Highlight navigation if scrolling over expansion panel.
   */
  onScroll() {
    this.resetActive();

    document.querySelectorAll('mat-card').forEach(cardEl => {
      const rect = cardEl.getBoundingClientRect();

      if (
        rect.bottom > this.cardTriggerHeight &&
        rect.top < this.cardTriggerHeight
      ) {
        this.addActive(cardEl);
      }
    });
  }

  /**
   * Convert label to id.
   *
   * @param service The service
   */
  toServiceId(service: ServiceConfig): string {
    return service.label.replace(/ /g, '-').replace(/\./g, '-');
  }

  /**
   * Add `active` class name to element.
   *
   * @param cardEl The mat card element
   */
  private addActive(cardEl: Element): void {
    const navEl = this.el.nativeElement.querySelector(`.${cardEl.id}`);
    navEl?.classList.add('active');
  }

  /**
   * Check URL parameters.
   */
  private checkUrl() {
    const query: QueryParameters = this.route.snapshot.queryParams;

    if (query.serviceGroup) {
      this.el.nativeElement
        .querySelector(`#${query.serviceGroup}`)
        ?.scrollIntoView();
    }
  }

  /**
   * Remove `active` class name from menu.
   */
  private resetActive() {
    document
      .querySelector('mat-nav-list')
      .querySelectorAll('a')
      .forEach(navEl => navEl.classList.remove('active'));
  }
}
<mat-sidenav-container class="height-full" autosize>
  @if ((templateService.isSmallScreen$ | async) === false) {
    <mat-sidenav
      mode="side"
      opened
      class="mat-elevation-z2"
      [ngClass]="{collapsed: sidenavCollapsed}"
    >
      @if (sidenavCollapsed === false) {
        <mat-nav-list>
          @for (serviceGroup of serviceGroups; track serviceGroup) {
            <a
              mat-list-item
              (click)="onNavClick(serviceGroup.id)"
              [class]="serviceGroup.id"
            >
              <span matListItemLine>
                {{ serviceGroup.title }}
              </span>
            </a>
          }
        </mat-nav-list>
      }

      <div class="collapse-sidenav">
        <button
          mat-button
          class="grid-col-12"
          (click)="sidenavCollapsed = !sidenavCollapsed"
        >
          @if (sidenavCollapsed === false) {
            <div class="icon-text">
              <mat-icon aria-label="Left arrow icon">
                keyboard_double_arrow_left
              </mat-icon>
              <span>Collapse sidebar</span>
            </div>
          } @else {
            <mat-icon aria-label="Right arrow icon">
              keyboard_double_arrow_right
            </mat-icon>
          }
        </button>
      </div>
    </mat-sidenav>
  }

  <mat-sidenav-content (scroll)="onScroll()">
    <div class="service-content grid-container-widescreen">
      @for (serviceGroup of serviceGroups; track serviceGroup) {
        <div class="service-group">
          <!-- Service info -->
          <mat-card [id]="serviceGroup.id">
            <mat-card-header>
              <mat-card-title>
                <div class="card-title">
                  <button
                    mat-icon-button
                    (click)="onLinkIconClick(serviceGroup.id)"
                  >
                    <mat-icon aria-label="Link Icon">link</mat-icon>
                  </button>
                  {{ serviceGroup.title }}
                </div>
              </mat-card-title>
              <mat-card-subtitle>{{ serviceGroup.subtitle }}</mat-card-subtitle>
            </mat-card-header>

            <!-- Images -->
            <mat-card-content>
              @if (
                (templateService.isSmallScreen$ | async) === false &&
                serviceGroup.images.length > 0
              ) {
                <mat-divider />
                <div class="card-section">
                  <div class="grid-row">
                    @for (
                      serviceImage of serviceGroup.images;
                      track serviceImage
                    ) {
                      <div class="grid-col-12 tablet:grid-col-6 content-image">
                        @if (
                          templateService.isDarkMode &&
                          serviceImage.darkModeImage
                        ) {
                          <img
                            src="assets/services/{{
                              serviceImage.darkModeImage
                            }}"
                            [alt]="serviceImage.darkModeImage"
                          />
                        } @else {
                          <img
                            src="assets/services/{{ serviceImage.image }}"
                            [alt]="serviceImage.image"
                          />
                        }
                      </div>
                    }
                  </div>
                </div>
              }

              @if (serviceGroup.applicationsUsedIn.length > 0) {
                <mat-divider />

                <!-- Application used list -->
                <div class="card-section">
                  <h4>Applications Used In</h4>
                  <mat-nav-list>
                    @for (app of serviceGroup.applicationsUsedIn; track app) {
                      <a
                        mat-list-item
                        [routerLink]="app.routerLink"
                        target="_blank"
                      >
                        {{ app.display }}
                      </a>
                    }
                  </mat-nav-list>
                </div>
              }

              <mat-divider />

              <!-- Expansion panels with Swagger -->
              @if (serviceGroup.services$ | async; as services) {
                @if (services.length > 0) {
                  <div class="card-section">
                    <h4>Service Information</h4>
                    <!-- List of links to swagger pages -->
                    <mat-nav-list>
                      @for (service of services; track service) {
                        <a mat-list-item [href]="service.url" target="_blank">
                          {{ service.label }}
                        </a>
                      }
                    </mat-nav-list>
                  </div>
                }
              }
            </mat-card-content>
          </mat-card>
        </div>
      }
    </div>
  </mat-sidenav-content>
</mat-sidenav-container>
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""