polkadot-js/apps

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

Summary

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

import type { AccountId, AccountIndex, Address } from '@polkadot/types/interfaces';

import React, { useCallback } from 'react';

import { useToggle } from '@polkadot/react-hooks';

import EditButton from './EditButton.js';
import Input from './Input.js';
import { styled } from './styled.js';
import Tags from './Tags.js';

export interface RowProps {
  address?: AccountId | AccountIndex | Address | string | null;
  buttons?: React.ReactNode;
  children?: React.ReactNode;
  className?: string;
  defaultName?: string | null;
  details?: React.ReactNode;
  icon?: React.ReactNode;
  iconInfo?: React.ReactNode;
  isDisabled?: boolean;
  isInline?: boolean;
  isEditableName?: boolean;
  isEditableTags?: boolean;
  isShortAddr?: boolean;
  name?: string;
  onChangeName?: (_: string) => void;
  onChangeTags?: (_: string[]) => void;
  onSaveName?: () => void;
  onSaveTags?: () => void;
  tags?: string[];
}

function Row ({ address, buttons, children, className = '', defaultName, details, icon, iconInfo, isDisabled, isEditableName, isEditableTags, isInline, isShortAddr = true, name, onChangeName, onChangeTags, onSaveName, onSaveTags, tags }: RowProps): React.ReactElement<RowProps> {
  const [isEditingName, toggleIsEditingName] = useToggle();
  const [isEditingTags, toggleIsEditingTags] = useToggle();

  const _onSaveName = useCallback((): void => {
    onSaveName && onSaveName();
    toggleIsEditingName();
  }, [onSaveName, toggleIsEditingName]);

  return (
    <StyledDiv className={`${className} ui--Row ${isDisabled ? 'isDisabled' : ''} ${isInline ? 'isInline' : ''}`}>
      <div className='ui--Row-base'>
        {icon && (
          <div className='ui--Row-icon'>
            {icon}
            {iconInfo && (
              <div className='ui--Row-icon-info'>
                {iconInfo}
              </div>
            )}
          </div>
        )}
        <div className='ui--Row-details'>
          {(name || defaultName) && (
            isEditableName && isEditingName
              ? (
                <Input
                  autoFocus
                  defaultValue={name || defaultName}
                  isInPlaceEditor
                  onBlur={_onSaveName}
                  onChange={onChangeName}
                  onEnter
                  withLabel={false}
                />
              )
              : (
                <div className='ui--Row-name'>
                  {
                    isEditableName
                      ? (
                        <EditButton onClick={toggleIsEditingName}>
                          {name || defaultName}
                        </EditButton>
                      )
                      : name || defaultName
                  }
                </div>
              )
          )}
          {address && (
            <div className={`ui--Row-address ${isShortAddr ? 'shortAddr' : ''}`}>
              {address.toString()}
            </div>
          )}
          {details}
          {tags && (
            <Tags
              className='ui--Row-tags'
              isEditable={isEditableTags}
              isEditing={isEditingTags}
              onChange={onChangeTags}
              onSave={onSaveTags}
              onToggleIsEditing={toggleIsEditingTags}
              value={tags}
            />
          )}
        </div>
        {buttons && (
          <div className='ui--Row-buttons'>
            {buttons}
          </div>
        )}
      </div>
      {children && (
        <div className='ui--Row-children'>
          {children}
        </div>
      )}
    </StyledDiv>
  );
}

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

  &.isDisabled {
    opacity: var(--opacity-light);

    .ui--IdentityIcon  {
      filter: grayscale(100%);
    }
  }

  &.isInline {
    display: flex;

    .ui--Row-accountId {
      white-space: nowrap;
    }
  }

  &.isInvalid {
    .ui--Row-accountId,
    .ui--Row-icon {
      filter: grayscale(100);
      opacity: var(--opacity-light);
    }
  }

  .ui--Row-base {
    display: flex;
    min-width: 16rem;
  }

  .ui--Row-buttons {
    position: relative;
    margin-right: -0.5rem;
    margin-top: -0.5rem;
    white-space: nowrap;
    height: 0rem;
    overflow: visible;

    button.ui.button:last-child {
      margin-right: 0;
    }
  }

  .ui--Row-children {
    display: block;
    padding-left: 1rem;
    padding-top: 1rem;
  }

  .ui--Row-details {
    flex: 1;
    margin-right: 1rem;
    padding: 0.25rem 0 0;
    width: 100%;
    min-width: 0;

    .account-label{
      margin: -0.75rem 0 0 0
    }

    * {
      vertical-align: middle;
    }
  }

  .ui--Row-address,
  .ui--Row-accountIndex {
    padding: 0;
    margin-bottom: 0.25rem;

    &.shortAddr {
      min-width: var(--width-shortaddr);
      max-width: var(--width-shortaddr);
      opacity: var(--opacity-light);
      overflow: hidden;
      text-overflow: ellipsis;
    }
  }

  .ui--Row-name {
    display: flex;
    box-sizing: border-box;
    height: 1.5rem;
    margin: 0;
    padding: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-transform: uppercase;
    overflow: hidden;
    text-overflow: inherit;
  }

  .ui--Row-icon {
    flex: 0;
    margin-right: 1em;
    position: relative;

    .ui--Row-icon-info {
      left: -0.5rem;
      position: absolute;
      top: -0.5rem;
    }
  }

  .ui--Row-name-input {
    input {
      height: 1em;
      text-transform: uppercase;
      margin-top: -0.3em;
    }
  }

  .ui--Row-tags {
    &.editable {
      display: flex;
      flex-wrap: wrap;
      justify-content: left;

      .addTags {
        border: 1px #00000052 solid;
        border-radius: .5em;
        border-style: dashed;
        color: grey;
        font-size: var(--font-size-tiny);
        padding: .1em 0.3em 0.1em 0.3em;
        margin-top: .2em;
      }

      > div.label {
        margin-top:.3em
      }
    }
  }

  .ui--Row-tags-input {
    margin-bottom: -1.4em;
  }
`;

export default React.memo(Row);