polkadot-js/apps

View on GitHub
packages/react-components/src/LinkExternal.tsx

Summary

Maintainability
A
45 mins
Test Coverage
// Copyright 2017-2024 @polkadot/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { LinkTypes } from '@polkadot/apps-config/links/types';
import type { BN } from '@polkadot/util';

import React, { useMemo } from 'react';

import { externalLinks } from '@polkadot/apps-config';
import { useApi } from '@polkadot/react-hooks';

import { styled } from './styled.js';
import { useTranslation } from './translate.js';

interface Props {
  className?: string;
  data: BN | number | string;
  hash?: string;
  isText?: boolean;
  isSidebar?: boolean;
  isSmall?: boolean;
  type: LinkTypes;
  withTitle?: boolean;
}

function genLinks (systemChain: string, { data, hash, isText, type }: Props): React.ReactNode[] {
  return Object
    .entries(externalLinks)
    .map(([name, { chains, create, homepage, isActive, paths, ui }]): React.ReactNode | null => {
      const extChain = chains[systemChain];
      const extPath = paths[type];

      if (!isActive || !extChain || !extPath) {
        return null;
      }

      return (
        <a
          href={create(extChain, extPath, data, hash)}
          key={name}
          rel='noopener noreferrer'
          target='_blank'
          title={`${name}, ${homepage}`}
        >
          {isText
            ? name
            : <img src={ui.logo} />
          }
        </a>
      );
    })
    .filter((node): node is React.ReactNode => !!node);
}

function LinkExternal ({ className = '', data, hash, isSidebar, isSmall, isText, type, withTitle }: Props): React.ReactElement<Props> | null {
  const { t } = useTranslation();
  const { systemChain } = useApi();

  const links = useMemo(
    () => genLinks(systemChain, { data, hash, isSidebar, isText, type }),
    [systemChain, data, hash, isSidebar, isText, type]
  );

  if (!links.length && !withTitle) {
    return null;
  }

  return (
    <StyledDiv className={`${className} ui--LinkExternal ${isText ? 'isText' : 'isLogo'} ${withTitle ? 'isMain' : ''} ${isSmall ? 'isSmall' : ''} ${isSidebar ? 'isSidebar' : ''}`}>
      {(isText && !isSmall) && <div>{t('View this externally')}</div>}
      {withTitle && (
        <h5>{t('external links')}</h5>
      )}
      <div className='links'>
        {links.length
          ? links.map((link, index) => <span key={index}>{link}</span>)
          : <div>{t('none')}</div>
        }
      </div>
    </StyledDiv>
  );
}

const StyledDiv = styled.div`
  text-align: right;

  &.isMain {
    text-align: left;
  }

  &.isSmall {
    font-size: var(--font-size-small);
    line-height: 1.35;
    text-align: center;
  }

  &.isSidebar {
    text-align: center;

    .links {
      img {
        height: 2rem;
        width: 2rem;
      }
    }
  }

  &:not(.fullColor) {
    .links {
      img {
        filter: grayscale(1) opacity(0.66);

        &:hover {
          filter: grayscale(0) opacity(1);
        }
      }
    }
  }

  .links {
    img {
      border-radius: 50%;
      cursor: pointer;
      height: 1.5rem;
      width: 1.5rem;
    }

    span {
      word-wrap: normal;
      display: inline-block;
    }

    span+span {
      margin-left: 0.3rem;
    }
  }

  &.isLogo {
    line-height: 1;

    .links {
      white-space: nowrap;
    }
  }
`;

export default React.memo(LinkExternal);