Source: @@components/entityList/EntityEntry.js

/**
 * @file Represents an entity entry in the list view
 * @module @@components/entityList/EntityEntry
 */
import React from 'react';
import { path, pipe } from 'ramda';
import { List, Card, Space, Checkbox, Tooltip, Input } from 'antd';
import {connect} from 'react-redux';
import {PropertyEntry} from './PropertyEntry';
import {PrefixedText} from './PrefixedText';
import {
  RightOutlined,
  DownOutlined,
  EyeInvisibleOutlined,
  EyeOutlined,
  InfoCircleOutlined,
  DeleteOutlined,
  CopyOutlined
} from '@ant-design/icons';
import styled from '@emotion/styled';
import { getClassById, getLabelLanguage, getShowHumanReadable } from '@@selectors';
import * as Controls from './Controls';
import * as ModelState from '@@app-state/model/state';
import { dispatch } from '@@app-state';
import { VarNameContainer } from '@@components/controls/var-name-container';
import { Graph } from '@@graph';
import { translated } from '@@localization';

const ExpandIconContainer = styled.div`
  display: inline-block;
  width: 100%;
`;

const Container = styled(Card)`
  width: 100%;
  cursor: pointer;
`;

const ControlsContainer = styled.div`
  display: flex;
`;

const renderPropertyEntry = id => (
  <PropertyEntry
    id={id}
  />
);

class EntityEntryComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      expanded: false,
      varName: path(['entity', 'varName'], props)
    };
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    const varName = path(['entity', 'varName'], this.props);

    if (varName !== prevState.varName && (prevState.varName === this.state.varName)) {
      this.setState({varName});
    }
  }

  onNameChange = e => this.setState({varName: e.target.value});

  toggleExpanded = () => this.setState(s => ({expanded: !s.expanded}));

  getToggleIcon = () => this.state.expanded ? <DownOutlined /> : <RightOutlined />

  getControls = () => {
    const {id, entity} = this.props;
    const {toggleSelected, toggleHidden, updateName, deleteClass, onCopyEntity} = dispatchProps;
    const {selected, hidden} = entity;
    const {varName} = this.state;
    return (
      <ControlsContainer>
        <Space>
          <VarNameContainer>
            <Input
              type="text"
              value={varName}
              onChange={this.onNameChange}
              onBlur={() => updateName(id, varName)}
              onPressEnter={() => updateName(id, varName)}
            />
          </VarNameContainer>
          <Tooltip title={`${selected ? translated('Hide entity in the result set') : translated('Show entity in the result set')}`}>
            <Checkbox
              onChange={e => toggleSelected(id, e.target.checked)}
              name="Select"
              checked={selected}
            />
          </Tooltip>
          <Controls.Toggle
            flag={true}
            tooltipTextOn={translated('Copy entity')}
            onClick={() => onCopyEntity(id)}
            OnIcon={CopyOutlined}
          />
          <Controls.Toggle
            flag={true}
            tooltipTextOn={translated('Delete entity')}
            onClick={() => deleteClass(id)}
            OnIcon={DeleteOutlined}
          />
          <Controls.Toggle
            flag={!hidden}
            tooltipTextOn={translated('Hide entity in the graph')}
            tooltipTextOff={translated('Show entity in the graph')}
            onClick={() => toggleHidden(id, !hidden)}
            OnIcon={EyeOutlined}
            OffIcon={EyeInvisibleOutlined}
          />
        </Space>
      </ControlsContainer>
    );
  }

  getInfoIcon = description => description && <Tooltip title={description}>
    <InfoCircleOutlined />
  </Tooltip>;

  getTitle = label => label ?
    <Tooltip
      title={<PrefixedText.Unwrapped title={this.props.entity.type}/>}
    >{label}</Tooltip> : <PrefixedText title={this.props.entity.type}/>;

  render() {
    const {entity, showHumanReadable, labelLanguage} = this.props;
    const {propertyIds, info} = entity;

    let toShow = path(['byLanguage', labelLanguage], info) || path(['byLanguage', 'default'], info) || {}
    if (!showHumanReadable) {
      toShow = {};
    }
    const {label, description} = toShow;

    return (
      <Container
        type="inner"
        title={<ExpandIconContainer onClick={this.toggleExpanded}>
          <Space>
            {this.getToggleIcon()}
            {this.getTitle(label)}
            {this.getInfoIcon(description)}
          </Space>
        </ExpandIconContainer>}
        size="small"
        bodyStyle={{paddingTop: 0, paddingBottom: 0}}
        extra={this.getControls()}
      >
        {this.state.expanded && <List
          dataSource={propertyIds}
          renderItem={renderPropertyEntry}
        />}
      </Container>
    );
  }
}


const mapStateToProps = (appState, {id}) => ({
  entity: getClassById(id, appState),
  showHumanReadable: getShowHumanReadable(appState),
  labelLanguage: getLabelLanguage(appState)
});

const dispatchProps = {
  toggleHidden: pipe(ModelState.toggleClassHidden, dispatch),
  toggleSelected: pipe(ModelState.toggleClassSelected, dispatch),
  updateName: pipe(ModelState.updateClassName, dispatch),
  onCopyEntity: id => {
    Graph.onCreateNewEntity(id);
  },
  deleteClass: id => {
    Graph.onDeleteEntity(id);
  }
};

export const EntityEntry = connect(mapStateToProps, null)(EntityEntryComponent);