libs/core/frame/src/frame-parts/navigation/navigation-step.component.ts

Extends

CdkStepHeader

Implements

OnDestroy

Metadata

Relationships

Depends on

Index

Properties
Inputs
HostBindings
Accessors

Constructor

constructor()

Inputs

hideDot
Type : boolean
Default value : false
hideLabel
Type : boolean
Default value : false
horizontal
Type : boolean
Default value : false
isForbidden
Type : boolean
Default value : false
label
Type : string
labelId
Type : string
Required :  true
state
Default value : StepState.PENDING

HostBindings

attr.aria-label
Type : any
attr.aria-labelledby
Type : any
attr.aria-selected
Type : "true" | "false"
attr.role
Type : string
Default value : 'tab'
attr.tabindex
Type : "0" | "-1"
class.is-complete
Type : boolean
class.is-current
Type : boolean
class.is-horizontal
Type : any
class.is-interactive
Type : boolean
class.is-pending
Type : boolean
class.is-unclickable
Type : any
class.is-unknown
Type : boolean
class.no-dot
Type : any

Properties

_elementRef
Type : ElementRef<HTMLElement>
role
Type : string
Default value : 'tab'
Decorators :
@HostBinding('attr.role')

Accessors

tabindex
gettabindex()
isHorizontal
getisHorizontal()
isNoDot
getisNoDot()
isUnclickable
getisUnclickable()
ariaLabelledBy
getariaLabelledBy()
ariaLabel
getariaLabel()
ariaSelected
getariaSelected()
isCurrent
getisCurrent()
isComplete
getisComplete()
isInteractive
getisInteractive()
isUnknown
getisUnknown()
isPending
getisPending()
import { NxIconModule } from '@allianz/ng-aquila/icon';
import { FocusMonitor } from '@angular/cdk/a11y';
import { CdkStepHeader } from '@angular/cdk/stepper';
import {
  ChangeDetectionStrategy,
  Component,
  ElementRef,
  HostBinding,
  OnDestroy,
  inject,
  input
} from '@angular/core';
import { StepState } from './navigation-step-state';

@Component({
  selector: 'frame-navigation-step',
  templateUrl: 'navigation-step.component.html',
  styleUrls: [`./navigation-step.component.scss`],
  imports: [NxIconModule],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class NavigationStepComponent extends CdkStepHeader implements OnDestroy {
  override _elementRef: ElementRef<HTMLElement>;
  private _focusMonitor = inject(FocusMonitor);

  @HostBinding('attr.tabindex')
  get tabindex() {
    return this.state() === StepState.CURRENT ? '0' : '-1';
  }
  @HostBinding('attr.role') role = 'tab';

  readonly horizontal = input(false);
  readonly label = input<string>();
  readonly state = input(StepState.PENDING);
  readonly hideDot = input(false);
  readonly isForbidden = input(false);
  readonly hideLabel = input(false);
  readonly labelId = input.required<string>();

  @HostBinding('class.is-horizontal')
  get isHorizontal() {
    return this.horizontal();
  }

  @HostBinding('class.no-dot')
  get isNoDot() {
    return this.hideDot();
  }

  @HostBinding('class.is-unclickable')
  get isUnclickable() {
    return this.isForbidden();
  }

  @HostBinding('attr.aria-labelledby')
  get ariaLabelledBy() {
    return this.label() && !this.hideLabel() ? this.labelId() : null;
  }

  // Fallback in case the label is hidden
  @HostBinding('attr.aria-label')
  get ariaLabel() {
    if (this.hideLabel()) {
      return this.isComplete ? 'Completed ' + this.label() : this.label() || null;
    }
    return null;
  }

  @HostBinding('attr.aria-selected')
  get ariaSelected() {
    return this.isCurrent ? 'true' : 'false';
  }

  constructor() {
    super();

    this._elementRef = inject<ElementRef<HTMLElement>>(ElementRef);

    this._focusMonitor.monitor(this._elementRef);
  }

  ngOnDestroy() {
    this._focusMonitor.stopMonitoring(this._elementRef);
  }

  @HostBinding('class.is-current')
  get isCurrent() {
    return this.state() === StepState.CURRENT;
  }

  @HostBinding('class.is-complete')
  get isComplete() {
    return this.state() === StepState.COMPLETE;
  }

  @HostBinding('class.is-interactive')
  get isInteractive() {
    return this.state() === StepState.COMPLETE;
  }

  @HostBinding('class.is-unknown')
  get isUnknown() {
    return this.state() === StepState.UNKNOWN;
  }

  @HostBinding('class.is-pending')
  get isPending() {
    return this.state() === StepState.PENDING;
  }
}
<div class="dot">
  <div class="inner-dot"></div>
  <nx-icon class="checkmark" name="check"></nx-icon>
</div>
<div class="label-wrapper" [id]="labelId()">
  @if (isComplete) {
  <span class="sr-only" i18n="@@frame.navigation-step.completed">Completed: </span>
  } @if (label() && !hideLabel()) {
  <span class="label">{{ label() }}</span>
  }
</div>

./navigation-step.component.scss

$dot-size: 8px;
$dot-border-size: 2px;

:host {
  display: flex;
  position: relative;
  align-items: center;
  font-size: var(--multi-stepper-step-font-size);
  min-height: 20px;
}

:host(.is-horizontal) {
  flex: 1;
  flex-direction: column;
}

:host:focus {
  outline: none;
}

:host(.cdk-keyboard-focused) {
  .dot {
    box-shadow: 0 0 0 2px var(--multi-stepper-dot-checkmark-color),
      0 0 0 6px var(--multi-stepper-dot-touched-color);
  }
}

.label {
  margin-left: 16px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

:host(.is-horizontal) .label {
  margin-left: 0;
  white-space: initial;
  text-align: center;
}

:host(.is-interactive) .dot,
:host(.is-interactive) .label {
  cursor: pointer;
}

.dot {
  flex-shrink: 0;
  border-radius: 50%;
  width: 2 * $dot-size;
  height: 2 * $dot-size;
  border: $dot-border-size solid var(--multi-stepper-dot-touched-color);
  position: relative;
  font-size: 16px;
}

:host(.no-dot) .dot {
  display: none;
}

:host(.is-horizontal) .dot {
  margin-bottom: $dot-size;
}

.icon {
  font-size: 16px;
}

.inner-dot {
  width: $dot-size;
  height: $dot-size;
  border-radius: 50%;
  background-color: var(--multi-stepper-dot-touched-color);
  transform: translate($dot-border-size, $dot-border-size);
  position: absolute;
  display: none;
}

.checkmark {
  position: absolute;
  transform: translate(-$dot-border-size, -$dot-border-size);
  display: none;
}

:host(.is-complete) {
  .dot {
    background-color: var(--multi-stepper-dot-touched-color);
    color: var(--multi-stepper-dot-checkmark-color);
  }

  .checkmark {
    display: block;
  }
}

:host(.is-current) {
  .dot {
    color: var(--multi-stepper-dot-checkmark-color);
  }
  .inner-dot {
    display: block;
  }
  cursor: pointer;
}

:host(.is-current:not(.is-horizontal)) {
  .label {
    font-weight: 700;
  }
}

:host(.is-unknown) {
  .dot {
    border-color: var(--multi-stepper-dot-disabled-color);
  }
  cursor: not-allowed;
}

:host(.is-pending) {
  color: var(--multi-stepper-step-disabled-color);

  .dot {
    border: $dot-border-size solid var(--multi-stepper-dot-disabled-color);
  }
  cursor: not-allowed;
}

:host(.is-unclickable) {
  .dot,
  .label {
    cursor: not-allowed;
  }
  cursor: not-allowed;
}

:host(:not(.no-dot):not(.is-horizontal)) {
  margin-bottom: 2 * $dot-size + 2 * $dot-border-size;

  &:not(:last-child)::before {
    content: '';
    position: absolute;
    top: 3 * $dot-size - $dot-border-size;
    left: $dot-size - $dot-border-size;
    border-left: calc($dot-size / 2) dotted var(--multi-stepper-line-color);
    height: 2 * $dot-size;
  }

  &.is-complete::before {
    border-left-style: solid;
    border-left-color: var(--multi-stepper-dot-touched-color);
    height: 3 * $dot-size;
    margin: (-2 * $dot-border-size) 0;
  }
}

:host(:not(.no-dot).is-horizontal) {
  &:not(:last-child)::before {
    content: '';
    position: absolute;
    top: $dot-size - $dot-border-size;
    left: (3 * $dot-size);
    border-top: calc($dot-size / 2) dotted var(--multi-stepper-line-color);
    height: 0;
    width: calc(100% - #{3 * $dot-size});
    transform: translateX(50%);
  }

  &.is-complete::before {
    border-top-style: solid;
    border-top-color: var(--multi-stepper-dot-touched-color);
    left: (2 * $dot-size);
    width: calc(100% - #{2 * $dot-size});
  }
}

:host-context(.is-a1).is-horizontal {
  --a1-dot-size: var(--semantic-size-content-m, 24px);
  --a1-dot-radius: calc(var(--a1-dot-size) / 2);
  --a1-item-gap: var(--semantic-gap-all-linear-300, 12px);

  align-items: flex-start;

  .dot {
    width: var(--a1-dot-size);
    height: var(--a1-dot-size);
    font-size: var(--a1-dot-size);
  }

  &.is-current .inner-dot {
    display: none;
  }

  &.is-complete .dot {
    background-color: var(--semantic-color-signal-attention-positive-resting);
    border-color: var(--semantic-color-signal-attention-positive-resting);
  }

  &:last-child {
    min-width: 0;
    flex-grow: 0;
    align-items: flex-end;

    .label {
      white-space: nowrap;
      overflow: visible;
    }
  }
}

:host-context(.is-a1).is-horizontal:not(:last-child):not(:first-child) .label-wrapper {
  position: relative;
  left: var(--a1-dot-radius);
  transform: translateX(-50%);
}

:host-context(.is-a1).is-horizontal:not(.no-dot) {
  &:not(:last-child)::before {
    content: '';
    position: absolute;
    top: calc(var(--a1-dot-radius) - #{$dot-border-size});
    height: 2px;
    border-radius: 4px;
    background: var(--semantic-color-border-secondary-disabled);
    border-top: none;
    transform: none;
  }

  &.is-complete::before {
    background: var(--semantic-color-signal-attention-positive-resting);
  }

  &:not(:last-child):not(:nth-last-child(2))::before {
    left: calc(2 * var(--a1-dot-radius) + var(--a1-item-gap));
    width: calc(100% - 2 * var(--a1-dot-radius) - 2 * var(--a1-item-gap));
  }

  &:nth-last-child(2)::before {
    left: calc(2 * var(--a1-dot-radius) + var(--a1-item-gap));
    width: calc(100% - 4 * var(--a1-dot-radius) - 2 * var(--a1-item-gap));
  }
}

.sr-only {
  display: block;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""