File

src/converter/teaser/fs-teaser-converter.ts

Description

This interface defines a converter for teasers.

Extends

Converter

import { FsTeaserOverlayFactory } from './fs-teaser-overlay-factory';
import { Injectable, InjectionToken } from '@angular/core';
import { Converter } from '@spartacus/core';

import { nullSafe, toPercentageValue } from 'fs-spartacus-common';
import {
  CmsImageMap,
  TextLinkTemplate,
  CmsInputTextFormData,
  ImageLinkTemplate,
  ExpectedTeaserStructureFromCaas,
  OverlayDimension,
  OverlayPosition,
  PictureMetaData,
  TeaserComponentData,
  SearchLinkLinkTemplate,
  OverlayText,
  OverlayLink,
  OverlayImage,
  CategoryLinkTemplate,
  ProductLinkTemplate,
  ContentLinkTemplate,
  CmsImageMapMedia,
  CmsImageMapResolution,
} from './fs-teaser.model';

/**
 * This interface defines a converter for teasers.
 */
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface FsTeaserConverterInterface extends Converter<ExpectedTeaserStructureFromCaas, TeaserComponentData> {}

/**
 * The injection token for the {@link FsTeaserConverter}.
 */
export const FsTeaserConverterInjectionToken = new InjectionToken<FsTeaserConverterInterface>('FsTeaserConverter');

const FS_TEASER_IMAGE_MAP_SECTION_TEMPLATE_NAME = 'st_image';
const FS_TEASER_HYPERLINK_SECTION_TEMPLATE_NAME = 'st_link';
const FS_TEASER_IMAGE_ALT_TEXT_SECTION_TEMPLATE_NAME = 'st_alt_text';

const DEFAULT_RESOLUTION = 'ORIGINAL';

/**
 * This class is a factory to create content links from a {@link ExpectedTeaserStructureFromCaas}.
 *
 * @export
 * @class FsTeaserConverter
 */
@Injectable({ providedIn: 'root' })
export class FsTeaserConverter implements FsTeaserConverterInterface {
  private source: ExpectedTeaserStructureFromCaas;
  private target: TeaserComponentData;

  constructor(private overlayFactory: FsTeaserOverlayFactory) {}

  public convert(source: ExpectedTeaserStructureFromCaas): TeaserComponentData {
    // reset the result object with every call
    this.target = {};
    this.source = source;
    if (this.source.otherProperties != null) {
      this.setSectionPreviewId();
      this.processImageMap(
        this.getTeaserSection<CmsImageMap>(FS_TEASER_IMAGE_MAP_SECTION_TEMPLATE_NAME),
        this.getTeaserSection<CmsInputTextFormData>(FS_TEASER_IMAGE_ALT_TEXT_SECTION_TEMPLATE_NAME)
      );
      this.setTeaserHyperlink(this.getTeaserSection<any>(FS_TEASER_HYPERLINK_SECTION_TEMPLATE_NAME));
    }
    return this.target;
  }

  private setSectionPreviewId(): void {
    this.target.previewId = this.source.otherProperties.previewId;
  }

  private getTeaserSection<T extends CmsInputTextFormData | CmsImageMap>(identifier: string): T | undefined {
    if (this.source.otherProperties.formData != null) {
      const section = this.source.otherProperties.formData[identifier];
      if (section != null) {
        return section as T;
      }
    }
  }

  private processImageMap(imageMap: CmsImageMap, imageAltText?: CmsInputTextFormData): void {
    if (imageMap != null) {
      const { value } = imageMap;
      const { areas, media, resolution } = value;
      this.target.overlays = { images: [], texts: [], links: [] };
      if (media != null) {
        this.setBackgroundImage(media, resolution, imageAltText);
        this.addOverlays(areas, media);
      }
    }
  }

  private setTeaserHyperlink(data: any): void {
    // TODO SAPCC-309 teaser content module: link to a different page (category or pdp)
  }

  private setBackgroundImage(media: CmsImageMapMedia, resolution: CmsImageMapResolution, imageAltText?: CmsInputTextFormData): void {
    const usedResolution = resolution?.uid || DEFAULT_RESOLUTION;
    this.target.media = {
      url: nullSafe(media?.resolutions?.resolutionsMetaData?.[usedResolution]?.url, null),
      resolution: usedResolution,
      resolutions: nullSafe(media?.resolutions?.resolutionsMetaData, {}),
      altText: nullSafe(imageAltText?.value, ''),
      mime: nullSafe(media?.pictureMetaData?.mimeType, ''),
    };
  }

  private createOverlayPositionData(
    area: ImageLinkTemplate | TextLinkTemplate | SearchLinkLinkTemplate | CategoryLinkTemplate | ProductLinkTemplate | ContentLinkTemplate,
    pictureMetaData: PictureMetaData
  ): OverlayPosition & OverlayDimension {
    const positionAndDimension: OverlayPosition & OverlayDimension = {
      width: 0,
      height: 0,
      widthRatio: `0%`,
      heightRatio: `0%`,
      left: `0%`,
      top: `0%`,
    };
    if (area.rightBottom && area.leftTop && pictureMetaData) {
      const rightBottomX = nullSafe(area.rightBottom.x, 0);
      const rightBottomY = nullSafe(area.rightBottom.y, 0);
      const leftTopX = nullSafe(area.leftTop.x, 0);
      const leftTopY = nullSafe(area.leftTop.y, 0);
      const widthInPx = rightBottomX - leftTopX;
      const heightInPx = rightBottomY - leftTopY;
      const pictureMetaDataWidth = nullSafe(pictureMetaData.width, 0);
      const pictureMetaDataHeight = nullSafe(pictureMetaData.height, 0);
      positionAndDimension.width = widthInPx;
      positionAndDimension.height = heightInPx;
      positionAndDimension.widthRatio = `${toPercentageValue(widthInPx, pictureMetaDataWidth)}%`;
      positionAndDimension.heightRatio = `${toPercentageValue(heightInPx, pictureMetaDataHeight)}%`;
      positionAndDimension.left = `${toPercentageValue(leftTopX, pictureMetaDataWidth)}%`;
      positionAndDimension.top = `${toPercentageValue(leftTopY, pictureMetaDataHeight)}%`;
    }
    return positionAndDimension;
  }

  private addOverlays(
    areas: Array<
      ImageLinkTemplate | TextLinkTemplate | SearchLinkLinkTemplate | CategoryLinkTemplate | ProductLinkTemplate | ContentLinkTemplate
    > | null,
    media: { pictureMetaData: PictureMetaData }
  ): void {
    if (media != null && media.pictureMetaData != null && Array.isArray(areas)) {
      areas.filter((item) => item != null).forEach((area) => this.addOverlay(area, media));
    }
  }

  private addOverlay(
    area: ImageLinkTemplate | TextLinkTemplate | SearchLinkLinkTemplate | CategoryLinkTemplate | ProductLinkTemplate | ContentLinkTemplate,
    media: { pictureMetaData: PictureMetaData }
  ): void {
    const { template } = nullSafe(area.link, {});
    const positionAndDimension = this.createOverlayPositionData(area, media.pictureMetaData);
    if (template && template.uid) {
      const result = this.overlayFactory.createOverlay(template.uid, area, positionAndDimension);

      switch (template.uid) {
        case 'text':
          this.target.overlays.texts.push(result as OverlayText);
          break;

        case 'image':
          this.target.overlays.images.push(result as OverlayImage);
          break;

        case 'search_link':
        case 'category_link':
        case 'product_link':
        case 'content_link':
          this.target.overlays.links.push(result as OverlayLink);
          break;

        default:
          console.warn(
            `No overlay implementation was found for the template '${template.uid}'! Please extend the TeaserConverter and add an implementation for this template uid`
          );
      }
    }
  }
}

results matching ""

    No results matching ""