react-tags/react-tags

View on GitHub
src/components/SingleTag.tsx

Summary

Maintainability
A
2 hrs
Test Coverage
import React, { useRef } from 'react';
import { useDrag, useDrop } from 'react-dnd';
import ClassNames from 'classnames';
import { canDrag, canDrop } from './utils';

import RemoveComponent from './RemoveComponent';

const ItemTypes = { TAG: 'tag' };

export interface Tag {
  id: string;
  className: string;
  [key: string]: string;
}

export interface TagProps {
  labelField: string;
  onDelete: (
    event:
      | React.MouseEvent<HTMLSpanElement>
      | React.KeyboardEvent<HTMLSpanElement>
  ) => void;
  tag: Tag;
  moveTag?: (dragIndex: number, hoverIndex: number) => void;
  removeComponent?: React.ComponentType<any>;
  onTagClicked: (
    event: React.MouseEvent<HTMLSpanElement> | React.TouchEvent<HTMLSpanElement>
  ) => void;
  classNames: {
    tag: string;
    remove: string;
  };
  readOnly: boolean;
  index: number;
  allowDragDrop: boolean;
}

const SingleTag = (props: TagProps) => {
  const tagRef = useRef(null);
  const {
    readOnly = false,
    tag,
    classNames,
    index,
    moveTag,
    allowDragDrop = true,
    labelField = 'text',
  } = props;

  const [{ isDragging }, drag] = useDrag(() => ({
    type: ItemTypes.TAG,
    collect: (monitor) => ({
      isDragging: !!monitor.isDragging(),
    }),
    item: props,
    canDrag: () => canDrag({ moveTag, readOnly, allowDragDrop }),
  }));

  const [, drop] = useDrop(() => ({
    accept: ItemTypes.TAG,
    drop: (item: TagProps) => {
      const dragIndex = item.index;
      const hoverIndex = index;
      if (dragIndex === hoverIndex) {
        return;
      }

      props?.moveTag?.(dragIndex, hoverIndex);
    },
    canDrop: (item) => canDrop(item),
  }));

  drag(drop(tagRef));

  const label = props.tag[props.labelField];
  const { className = '' } = tag;
  /* istanbul ignore next */
  const opacity = isDragging ? 0 : 1;
  return (
    <span
      ref={tagRef}
      className={ClassNames('tag-wrapper', classNames.tag, className)}
      style={{
        opacity,
        cursor: canDrag({ moveTag, readOnly, allowDragDrop }) ? 'move' : 'auto',
      }}
      data-testid="tag"
      onClick={props.onTagClicked}
      onTouchStart={props.onTagClicked}>
      {label}
      <RemoveComponent
        tag={props.tag}
        className={classNames.remove}
        removeComponent={props.removeComponent}
        onRemove={props.onDelete}
        readOnly={readOnly}
        index={index}
      />
    </span>
  );
};

export { SingleTag };