All files / lib/dynamic-heading dynamic-heading-level.service.ts

99.04% Statements 104/105
95.45% Branches 21/22
100% Functions 5/5
99.04% Lines 104/105

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 1061x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 999x 999x 999x 999x 999x 999x 999x 999x 999x 999x 999x   999x 999x 999x 999x 999x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 253x 253x 1x 1x 1x 1x 177x 177x 177x 177x 7x 177x 1x  
/*******************************************************************************
 * Copyright bei
 * Entwicklungs- und Pflegeverbund für das gemeinsame Fachverfahren gefa
 *
 *******************************************************************************/
import {
  Directive,
  Inject,
  Injectable,
  InjectionToken,
  Optional,
  Self,
  SkipSelf,
} from '@angular/core';
 
/** Injection token which can be used to configure the `DynamicHeadingLevelService`. */
export const DYNAMIC_HEADING_START_LEVEL: InjectionToken<DynamicHeadingStartLevel> =
  new InjectionToken('dynamicHeadingStartLevel');
 
export type DynamicHeadingStartLevel =
  | DynamicHeadingStartLevelAbsolute
  | DynamicHeadingStartLevelRelative;
 
/**
 * Configuration which lets the dynamic headings start at an absolute / fixed level.
 */
export interface DynamicHeadingStartLevelAbsolute {
  /** @ignore */
  kind: 'absolute';
  /** Level at which headings will start. */
  start: number;
}
 
/**
 * Configuration which lets the dynamic headings start with a custom offset from the
 * next parent. Can be used to use level offsets different from the default value.
 */
export interface DynamicHeadingStartLevelRelative {
  /** @ignore */
  kind: 'relative';
  /** Offset which will be added to the dynamically determined parent level. */
  offset: number;
}
 
/**
 * Controls the level of dynamic heading components which use the service. Can be also used as directive to
 * increase the level of headings nested within the directive's target.
 *
 * By default, the current level is increased by one for all children.
 */
@Directive({
  selector: '[data-gc-dynamic-heading-level]',
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix -- also usable as directive, but primarly named as service to keep consistency with related injectables
export class DynamicHeadingLevelService {
  /**
   * The heading level at which contained headings should start.
   */
  public readonly headingLevel: number;
 
  constructor(
    @Optional() @SkipSelf() parentLevel: DynamicHeadingLevelService | null,
    @Inject(DYNAMIC_HEADING_START_LEVEL)
    @Optional()
    @Self()
    startConfig: DynamicHeadingStartLevel | null,
  ) {
    const start =
      startConfig?.kind === 'absolute' ? startConfig.start : undefined;
    const offset = startConfig?.kind === 'relative' ? startConfig.offset : 1;
    if (start !== undefined && start < 1) {
      throw new Error(
        `start heading level must be at least 1, got ${start.toString()}`,
      );
    }
 
    this.headingLevel = start ?? (parentLevel?.headingLevel ?? 1) + offset;
  }
}
 
/**
 * Alternative dynamic heading service implementation, which does not increase the level and
 * reuses the level of the next parent.
 *
 * This may be used to avoid increasing the level of headings within a component's view, whilst
 * increasing the level of (projected) children via the default service implementation.
 */
@Injectable()
export class DynamicHeadingLevelPassthroughService
  implements DynamicHeadingLevelService
{
  public get headingLevel(): number {
    return this.parentLevel.headingLevel;
  }
 
  private readonly parentLevel: DynamicHeadingLevelService;
 
  constructor(
    @Optional() @SkipSelf() parentLevel: DynamicHeadingLevelService | null,
  ) {
    this.parentLevel =
      parentLevel ??
      new DynamicHeadingLevelService(null, { kind: 'absolute', start: 1 });
  }
}