Home Reference Source

scripts/django_cradmin/components/CradminSelectedListItem.jsx

import React from "react";
import DomUtilities from "../utilities/DomUtilities";
import {HotKeys} from "react-hotkeys";


export default class CradminSelectedListItem extends React.Component {

  static get defaultProps() {
    return {
      className: 'selectable-list__item  selectable-list__item--selected',
      contentClassName: 'selectable-list__itemcontent',
      iconWrapperClassName: 'selectable-list__icon',
      iconClassName: 'icon-close--light',
      titleTagName: 'strong',
      titleClassName: 'selectable-list__itemtitle',
      descriptionClassName: '',
      ariaTitlePrefix: 'Deselect',
      renderMode: 'TitleAndDescription',
      focusClosestSiblingOnDeSelect: true,
      previousItemData: null,
      nextItemData: null,
      uniqueListId: '',
      useHotKeys: false
    }
  }

  constructor(props) {
    super(props);
    this._name = `django_cradmin.components.CradminSelectedListItem.${this.props.signalNameSpace}.${this.props.uniqueListId}.${this.props.itemKey}`;
    this.handleDeSelect = this.handleDeSelect.bind(this);
    this.handleFocus = this.handleFocus.bind(this);
    this.handleBlur = this.handleBlur.bind(this);
    this._onFocusOnSelectedItemSignal = this._onFocusOnSelectedItemSignal.bind(this);
    this.initializeSignalHandlers();
  }

  initializeSignalHandlers() {
    new window.ievv_jsbase_core.SignalHandlerSingleton().addReceiver(
      `${this.props.signalNameSpace}.FocusOnSelectedItem.${this.props.itemKey}`,
      this._name,
      this._onFocusOnSelectedItemSignal
    );
  }

  componentWillUnmount() {
    new window.ievv_jsbase_core.SignalHandlerSingleton()
      .removeAllSignalsFromReceiver(this._name);
  }

  _deselectItem() {
    new window.ievv_jsbase_core.SignalHandlerSingleton().send(
      `${this.props.signalNameSpace}.DeSelectItem`,
      this.props.data
    );

    if(this.props.focusClosestSiblingOnDeSelect) {
      let closestSiblingData = this.props.previousItemData;
      if(closestSiblingData == null) {
        closestSiblingData = this.props.nextItemData;
      }
      if(closestSiblingData == null) {
        new window.ievv_jsbase_core.SignalHandlerSingleton().send(
          `${this.props.signalNameSpace}.CouldNotFocusOnClosestSelectedItem`);
      } else {
        new window.ievv_jsbase_core.SignalHandlerSingleton().send(
          `${this.props.signalNameSpace}.FocusOnDeSelectableItem`,
          closestSiblingData
        );
      }
    }
  }

  handleDeSelect(event) {
    event.preventDefault();
    this._deselectItem();
  }

  handleFocus() {
    if(this.props.setDataListFocus) {
      new window.ievv_jsbase_core.SignalHandlerSingleton().send(
        `${this.props.signalNameSpace}.Focus`
      );
    }
  }

  handleBlur() {
    if(this.props.setDataListFocus) {
      new window.ievv_jsbase_core.SignalHandlerSingleton().send(
        `${this.props.signalNameSpace}.Blur`
      );
    }
  }

  _onFocusOnSelectedItemSignal() {
    DomUtilities.forceFocus(this._domElement);
  }

  get ariaTitle() {
    if(this.props.data.ariaTitle) {
      return this.props.data.ariaTitle;
    } else {
      let ariaTitle = this.props.data.title;
      if(this.props.ariaTitlePrefix) {
        ariaTitle = `${this.props.ariaTitlePrefix} ${ariaTitle}`;
      }
      return ariaTitle;
    }
  }

  renderTitle() {
    return React.createElement(this.props.titleTagName, {
      className: this.props.titleClassName
    }, this.props.data.title);
  }

  renderDescription() {
    if(this.props.data.description && this.props.data.description != '') {
      return <p className={this.props.descriptionClassName}>{this.props.data.description}</p>;
    } else {
      return '';
    }
  }

  renderIcon() {
    return <i className={this.props.iconClassName} />;
  }

  renderIconWrapper() {
    return <div className={this.props.iconWrapperClassName}>
      {this.renderIcon()}
    </div>;
  }

  renderContentModeTitleAndDescription() {
    return <div className={this.props.contentClassName}>
      {this.renderTitle()}
      {this.renderDescription()}
    </div>;
  }

  renderContentModeTitleOnly() {
    return <div className={this.props.contentClassName}>
      {this.props.data.title}
    </div>;
  }

  renderContentModeHtml() {
    return <div className={this.props.contentClassName}
                dangerouslySetInnerHTML={{__html: this.props.data.html}}></div>;
  }

  renderContent() {
    if(this.props.renderMode == 'TitleAndDescription') {
      return this.renderContentModeTitleAndDescription();
    } else if(this.props.renderMode == 'TitleOnly') {
      return this.renderContentModeTitleOnly();
    } else if(this.props.renderMode == 'html') {
      return this.renderContentModeHtml();
    } else {
      throw new Error(`Invalid renderMode: ${this.props.renderMode}`);
    }
  }

  _focusPreviousItem() {
    if(this.props.previousItemData == null) {
      new window.ievv_jsbase_core.SignalHandlerSingleton().send(
        `${this.props.signalNameSpace}.FocusBeforeFirstSelectableItem`);
    } else {
      new window.ievv_jsbase_core.SignalHandlerSingleton().send(
        `${this.props.signalNameSpace}.FocusOnSelectableItem`,
        this.props.previousItemData
      );
    }
  }

  _focusNextItem() {
    if(this.props.nextItemData == null) {
      new window.ievv_jsbase_core.SignalHandlerSingleton().send(
        `${this.props.signalNameSpace}.FocusAfterLastSelectableItem`);
    } else {
      new window.ievv_jsbase_core.SignalHandlerSingleton().send(
        `${this.props.signalNameSpace}.FocusOnSelectableItem`,
        this.props.nextItemData
      );
    }
  }

  get hotKeysMap() {
    return {
      'deselect': ['delete', 'backspace']
    };
  }

  get hotKeysHandlers() {
    return {
      'deselect': (event) => {
        event.preventDefault();
        this._deselectItem();
      }
    }
  }

  renderWrapper() {
    return <a href="#" className={this.props.className}
              ref={(domElement) => { this._domElement = domElement; }}
              onClick={this.handleDeSelect}
              onFocus={this.handleFocus}
              onBlur={this.handleBlur}
              aria-label={this.ariaTitle}
              role="button">
      {this.renderContent()}
      {this.renderIconWrapper()}
    </a>
  }

  render() {
    if(this.props.useHotKeys) {
      return <HotKeys keyMap={this.hotKeysMap} handlers={this.hotKeysHandlers}>
        {this.renderWrapper()}
      </HotKeys>
    } else {
      return this.renderWrapper();
    }
  }
}