Untitled

 avatar
unknown
plain_text
21 days ago
14 kB
2
Indexable
import {
  a, span, div, input, ul, li,
} from '../../scripts/dom-builder.js';
import Tag from '../../libs/tag/tag.js';
import Avatar from '../../libs/avatar/avatar.js';
import Button from '../../libs/button/button.js';
import Hero from '../../libs/hero/hero.js';
import {
  fetchTagList,
  getContentType,
  getMetadataOverride,
  getPageTags,
  getTagLink,
} from '../../scripts/utils/tags-utils.js';
import { formatDate, toTitleCase } from '../../scripts/utils/text-utils.js';
import { fetchAuthors, getAuthorMetadata, lookupProfiles } from '../../scripts/utils/author-utils.js';
import { isArticle } from '../../scripts/utils/article-utils.js';
import highlight from '../../scripts/utils/fuse-search-utils.js';
import isNewsPage from '../../scripts/utils/news-utils.js';
import { getPublishedDate, isMachineTranslated } from '../../scripts/utils/metadata-utils.js';
import { moveInstrumentation } from '../../scripts/utils/editor-support-utils.js';
import { getMetadata, toCamelCase } from '../../scripts/utils/aem-utils.js';
import { fetchPlaceholders } from '../../scripts/utils/i18n-utils.js';
import { decorateIcons } from '../../scripts/aem.js';

function buildAuthorEl(author) {
  const renderLink = author.path && author.path.indexOf('/people/') === -1;
  if (renderLink) {
    return a({ class: 'hero__content__info__author', href: author.path }, author.name);
  }
  return span({ class: 'hero__content__info__author' }, author.name);
}

function buildMetaInfo(placeholders, authors = []) {
  const info = [];
  if (authors.length > 0) {
    const authorEl = span({ class: 'hero__content__info__authors' });
    if (authors.length === 1) {
      const avatar = Avatar.fromAuthorEntry(authors[0]);
      if (avatar.getImage()) {
        info.push(avatar.render('small', true, false));
      }
      authorEl.append(buildAuthorEl(authors[0]));
    } else {
      authors.forEach((author) => {
        if (author) {
          authorEl.append(buildAuthorEl(author));
        }
      });
    }
    info.push(authorEl);
  }

  if (isNewsPage() || !getMetadata('modified-date')) {
    const publishedDate = getPublishedDate();
    if (publishedDate) {
      info.push(
        span({ class: 'hero__content__info__date' }, `${placeholders.publishedOn || 'Published on'} ${formatDate(publishedDate)}`),
      );
    }
  } else if (getMetadata('modified-date')) {
    info.push(
      span({ class: 'hero__content__info__date' }, `${placeholders.updatedOn || 'Updated on'} ${formatDate(getMetadata('modified-date'))}`),
    );
  }

  const readingTime = getMetadataOverride('twitter:data2');
  if (readingTime) {
    info.push(span({ class: 'hero__content__info__read-time' }, readingTime));
  }

  return info;
}

function replacePlaceholderText(elem, tags) {
  if (elem && (elem.innerText.includes('[page]') || elem.innerText.includes('[author]'))) {
    // find the first tag in tags which matches the path in topics-path or news-path
    let h1TitleTag;
    Object.keys(tags).forEach((tag) => {
      const tagData = tags[tag];
      if (
        tagData['topic-path'] === window.location.pathname
        || tagData['news-path'] === window.location.pathname
      ) {
        h1TitleTag = tagData;
      }
    });
    elem.innerHTML = elem.innerHTML.replace('[page]', h1TitleTag?.label || '');
    let author = getMetadata('author');
    if (!author) {
      const path = window.location.pathname;
      if (path.includes('/author/')) {
        author = toTitleCase(path.split('/author/').pop());
      }
    }
    elem.innerHTML = elem.innerHTML.replace('[author]', author || '');
  }
  return elem;
}

function findFirstTag(tags) {
  if (getMetadata('primarytag')) {
    const tag = tags[toCamelCase(getMetadata('primarytag').split(',')[0].trim())];
    return new Tag(tag.label, getTagLink(tag, document.location.pathname));
  }
  const articleTags = getPageTags();
  const tagsLiEL = articleTags.filter((articleTag) => {
    const tag = tags[toCamelCase(articleTag)];
    return tag && !tag.key.startsWith('contenthub:content-type/') && (tag['topic-path'] || tag['news-path']);
  })
    .map((articleTag) => {
      const tag = tags[toCamelCase(articleTag)];
      return new Tag(tag.label, getTagLink(tag, document.location.pathname));
    });
  return tagsLiEL[0];
}

function getEyebrowContent(eyebrow, tags, isAnArticle) {
  let eyebrowText = eyebrow?.textContent;
  const contentTypeTag = tags[toCamelCase(getContentType())];

  if (!eyebrowText && isAnArticle) {
    // if no eyebrow text is set, use the content type for articles
    eyebrowText = contentTypeTag?.label || getContentType()?.split('/')[1].replace('-', ' ');
  }

  let newEyebrow = '';
  if (eyebrow?.firstElementChild?.tagName === 'A') {
    // If author has added a custom link, add appropriate classes for styling
    newEyebrow = eyebrow.firstElementChild;
  } else if (eyebrowText && isAnArticle) {
    // If article, add link to parent topics page, and appropriate classes for styling
    const eyeBrowHref = (() => {
      if (contentTypeTag && contentTypeTag['topic-path'] && contentTypeTag['topic-path'] !== '0') return contentTypeTag['topic-path'];
      if (contentTypeTag && contentTypeTag['news-path'] && contentTypeTag['news-path'] !== '0') return contentTypeTag['news-path'];
      return null;
    })();
    newEyebrow = eyeBrowHref ? a({ href: eyeBrowHref }, eyebrowText) : eyebrowText;
  } else if (eyebrowText) {
    // Else display simple span or nothing
    newEyebrow = eyebrowText;
  }
  return newEyebrow;
}

function dispatchSearchEvent(searchValue) {
  const searchEvent = new CustomEvent('searchEvent', { detail: { searchValue } });
  document.dispatchEvent(searchEvent);
}

function getMatchIndicesLength(match) {
  return match.indices.reduce((sum, [start, end]) => sum + (end - start + 1), 0);
}

function getBestMatch(matches) {
  if (!matches || matches.length === 0) return '';

  let bestMatch = matches[0];
  let bestMatchLength = getMatchIndicesLength(bestMatch);

  matches.slice(1).forEach((currentMatch) => {
    const currentMatchLength = getMatchIndicesLength(currentMatch);

    if (currentMatchLength > bestMatchLength) {
      bestMatch = currentMatch;
      bestMatchLength = currentMatchLength;
    }
  });

  return bestMatch;
}

function hideAutocompleteList(autocompleteList) {
  autocompleteList.classList.remove('hero__content__autocomplete-list-visible');
  autocompleteList.innerHTML = '';
}

function updateAutocomplete( // eslint-disable-line no-unused-vars
  autocompleteList,
  searchInput,
  results,
) {
  if (!results.length) {
    autocompleteList.classList.remove('hero__content__autocomplete-list-visible');
    return;
  }

  autocompleteList.classList.add('hero__content__autocomplete-list-visible');
  autocompleteList.innerHTML = '';

  const listTitle = span({ class: 'hero__content__autocomplete-title' });
  listTitle.textContent = 'suggested searches';
  autocompleteList.append(listTitle);

  results.forEach((matchResult) => {
    const listItem = li({ class: 'hero__content__autocomplete-item' });
    const searchIcon = span({ class: 'icon icon-search' });

    const bestMatch = getBestMatch(matchResult.matches);
    const itemText = matchResult.item[bestMatch.key];

    const highlightedTextWrapper = span();
    highlightedTextWrapper.innerHTML = highlight(itemText, bestMatch);
    listItem.append(searchIcon, highlightedTextWrapper);

    listItem.addEventListener('click', () => {
      searchInput.value = itemText;
      dispatchSearchEvent(itemText);
      hideAutocompleteList(autocompleteList);
    });
    autocompleteList.appendChild(listItem);
  });
}

function removeHeroElementsForSearchMode(heroEl) {
  heroEl.querySelector('.hero__content__eyebrow')?.remove();
  heroEl.querySelector('.hero__additional-content')?.remove();
  heroEl.querySelector('.hero__content__info')?.remove();
}

function addSearchInput({ hero, contentContainer }) {
  removeHeroElementsForSearchMode(hero);

  const searchInputPlaceholder = contentContainer.querySelector('h6')?.previousElementSibling;
  const searchInputContainer = div({ class: 'hero__content__input-container', role: 'search' });
  const searchAutocompleteList = ul({ class: 'hero__content__autocomplete-list' });
  const searchInput = input(
    {
      class: 'hero__content__input',
      placeholder: searchInputPlaceholder?.textContent || 'Search for your SAP product here',
    },
  );

  searchInput.addEventListener('input', () => {
    const searchValue = searchInput.value;

    if (searchValue.length >= 1) {
      searchInput.classList.add('has-text');
    } else {
      searchInput.classList.remove('has-text');
    }

    dispatchSearchEvent(searchValue);
  });

  searchInput.addEventListener('keydown', (e) => {
    if (e.key === 'Enter') {
      dispatchSearchEvent(searchInput.value);
    }
  });

  // TODO: uncomment, please, for autocomplete functionality
  // document.addEventListener('searchResults', (e) => {
  //   const { results } = e.detail;
  //   updateAutocomplete(searchAutocompleteList, searchInput, results);
  //   decorateIcons(searchAutocompleteList);
  // });

  document.addEventListener('click', (event) => {
    if (!searchInput.contains(event.target) && !searchAutocompleteList.contains(event.target)) {
      hideAutocompleteList(searchAutocompleteList);
    }
  });

  const heroContentEl = hero.querySelector('.hero__content');
  const searchIcon = span({ class: 'icon icon-search', 'aria-hidden': true });
  const clearIcon = span({ class: 'icon icon-close' });

  clearIcon.addEventListener('click', () => {
    searchInput.value = '';
    searchInput.classList.remove('has-text');
    dispatchSearchEvent('');
    hideAutocompleteList(searchAutocompleteList);
  });

  searchIcon.addEventListener('click', () => {
    dispatchSearchEvent(searchInput.value);
  });

  searchInputContainer.append(searchInput, clearIcon, searchIcon, searchAutocompleteList);
  heroContentEl.append(searchInputContainer);
  decorateIcons(heroContentEl);
}

/**
 * loads and decorates the hero
 * @param {Element} block The hero block element
 */
export default async function decorate(block) {
  const isAnArticle = isArticle();
  const isMediaBlend = isAnArticle || block.classList.contains('media-blend');
  const tags = await fetchTagList();
  let videoHref = null;

  const backgroundImageContainer = block.children[0];
  const contentContainer = block.children[1];

  // extract block content
  const heading = contentContainer.querySelector('h1');
  const eyebrow = contentContainer.querySelector('h6');

  const hasFullBackgroundImage = block.classList.contains('full-background-image');
  const hasSearchMode = block.classList.contains('search');
  const backgroundImage = backgroundImageContainer.querySelector('picture');
  const additionalContentImage = contentContainer.querySelector('picture');

  if (isMediaBlend) {
    const link = contentContainer.querySelector('p:has(~ h1) a');
    videoHref = link?.href;
    link?.remove();
  }

  if (backgroundImage) {
    if (hasFullBackgroundImage) {
      backgroundImage.querySelectorAll('source[type="image/webp"]').forEach((source) => {
        source.srcset = source.srcset.replaceAll('format=webply', 'format=webpll');
      });
      backgroundImage.classList.add('full-background-image', 'hero__background-image');
      block.parentElement.append(backgroundImage);
    }
  }
  const placeholders = await fetchPlaceholders();

  // Add primary tag or news placeholder
  const tagEls = [];
  if (isNewsPage() && isAnArticle) {
    tagEls.push(new Tag(placeholders[toCamelCase('SAP News Center')], '/news').render());
  } else {
    const firstTag = findFirstTag(tags);
    if (firstTag) {
      tagEls.push(firstTag.render());
    }
  }

  const buttons = [...contentContainer.querySelectorAll('h1 ~ p.button-container a')].map((anchor) => {
    const type = anchor.parentElement.nodeName === 'EM' ? 'secondary' : 'primary';
    return new Button(anchor.textContent, null, type, {
      xs: 'medium',
      l: 'large',
    }, anchor.href).render();
  });
  const description = contentContainer.querySelectorAll('p:not(.button-container, :has(~ h1))');

  const searchHeroProps = {
    heading: replacePlaceholderText(heading, tags),
    eyebrow: getEyebrowContent(eyebrow, tags, isAnArticle),
    description,
    additionalContent: {
      additionalContentImage,
      additionalContentVideoHref: videoHref,
    },
    metadata: isMediaBlend ? buildMetaInfo(placeholders) : [],
    buttons,
    tags: tagEls,
    isMachineTranslated: isMachineTranslated(),
    placeholders,
  };

  const heroProps = hasSearchMode ? searchHeroProps : {
    ...searchHeroProps,
    backgroundImage: hasFullBackgroundImage ? null : backgroundImage,
  };

  const hero = new Hero(heroProps);

  if (isMediaBlend) {
    fetchAuthors().then((authorIndex) => {
      hero.setMetadata(buildMetaInfo(
        placeholders,
        lookupProfiles(getAuthorMetadata(), authorIndex),
      ));
    });
  }

  const heroEl = hero.render();

  if (hasSearchMode) {
    addSearchInput({ hero: heroEl, contentContainer });
  }

  heroEl.classList.add(...block.classList);
  moveInstrumentation(block, heroEl);
  block.replaceWith(heroEl);
}
Editor is loading...
Leave a Comment