scripts/django_cradmin/components/CradminSelectableList.jsx | django_cradmin API Document

# scripts/django_cradmin/components/CradminSelectableList.jsx

    import React from "react";
    import CradminSelectableListItem from "./CradminSelectableListItem";

    export default class CradminSelectableList extends React.Component {

      static get defaultProps() {
        return {
          className: 'selectable-list',
          keyAttribute: 'id',
          renderSelected: true,
          signalNameSpace: null,
          itemComponentProps: {},
          loadMoreTreshold: 3
        }
      }

      constructor(props) {
        super(props);
        this._name = `django_cradmin.components.CradminSelectableList.${this.props.signalNameSpace}`;
        this.logger = new window.ievv_jsbase_core.LoggerSingleton().getLogger(
          'django_cradmin.components.CradminSelectableList');
        if(this.props.signalNameSpace == null) {
          throw new Error('The signalNameSpace prop is required.');
        }
        this._onDataChangeSignal = this._onDataChangeSignal.bind(this);
        this._onSelectionChangeSignal = this._onSelectionChangeSignal.bind(this);
        this._onFocusOnSelectableItemSignal = this._onFocusOnSelectableItemSignal.bind(this);
        this._onFocusOnFirstSelectableItemSignal = this._onFocusOnFirstSelectableItemSignal.bind(this);
        this._onFocusOnLastSelectableItemSignal = this._onFocusOnLastSelectableItemSignal.bind(this);

        this._focusOnItemData = null;
        this.state = {
          dataList: [],
          renderableDataList: [],
          hasMorePages: false,
          selectedItemsMap: new Map()
        };

        this.initializeSignalHandlers();
      }

      initializeSignalHandlers() {
        new window.ievv_jsbase_core.SignalHandlerSingleton().addReceiver(
          `${this.props.signalNameSpace}.DataChange`,
          this._name,
          this._onDataChangeSignal
        );
        new window.ievv_jsbase_core.SignalHandlerSingleton().addReceiver(
          `${this.props.signalNameSpace}.SelectionChange`,
          this._name,
          this._onSelectionChangeSignal
        );
        new window.ievv_jsbase_core.SignalHandlerSingleton().addReceiver(
          `${this.props.signalNameSpace}.FocusOnSelectableItem`,
          this._name,
          this._onFocusOnSelectableItemSignal
        );
        new window.ievv_jsbase_core.SignalHandlerSingleton().addReceiver(
          `${this.props.signalNameSpace}.FocusOnFirstSelectableItem`,
          this._name,
          this._onFocusOnFirstSelectableItemSignal
        );
        new window.ievv_jsbase_core.SignalHandlerSingleton().addReceiver(
          `${this.props.signalNameSpace}.FocusOnLastSelectableItem`,
          this._name,
          this._onFocusOnLastSelectableItemSignal
        );
      }

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

      render() {
        return <div className={this.props.className}>
          {this.renderItems()}
        </div>;
      }

      _loadMoreIfNeeded() {
        if(this.state.hasMorePages && this.state.renderableDataList.length < this.props.loadMoreTreshold) {
          this.logger.debug('Automatically sending the LoadMore signal because we are below the loadMoreTreshold');
          new window.ievv_jsbase_core.SignalHandlerSingleton().send(
            `${this.props.signalNameSpace}.LoadMore`);
        }
      }

      _makeRenderableDataList(dataList) {
        let renderableDataList = dataList;
        if(!this.props.renderSelected) {
          renderableDataList = [];
          for(let itemData of dataList) {
            let itemKey = itemData[this.props.keyAttribute];
            let isSelected = this.state.selectedItemsMap.has(itemKey);
            if(!isSelected) {
              renderableDataList.push(itemData);
            }
          }
        }
        return renderableDataList;
      }

      _onDataChangeSignal(receivedSignalInfo) {
        if(this.logger.isDebug) {
          this.logger.debug(receivedSignalInfo.toString(), receivedSignalInfo.data);
        }
        const dataList = receivedSignalInfo.data.results;
        this.setState({
          dataList: dataList,
          renderableDataList: this._makeRenderableDataList(dataList),
          hasMorePages: receivedSignalInfo.data.next != null
        });
        this._loadMoreIfNeeded();
      }

      _onSelectionChangeSignal(receivedSignalInfo) {
        if(this.logger.isDebug) {
          this.logger.debug(receivedSignalInfo.toString(), receivedSignalInfo.data);
        }
        this.setState({
          selectedItemsMap: receivedSignalInfo.data.selectedItemsMap,
          renderableDataList: this._makeRenderableDataList(this.state.dataList),
        });
        this._loadMoreIfNeeded();
      }

      _onFocusOnSelectableItemSignal(receivedSignalInfo) {
        if(this.logger.isDebug) {
          this.logger.debug(receivedSignalInfo.toString(), receivedSignalInfo.data);
        }
        this._focusOnItemData = receivedSignalInfo.data;
        this.forceUpdate();
      }

      _onFocusOnFirstSelectableItemSignal(receivedSignalInfo) {
        if(this.logger.isDebug) {
          this.logger.debug(receivedSignalInfo.toString(), receivedSignalInfo.data);
        }
        if(this.state.renderableDataList.length > 0) {
          this._focusOnItemData = this.state.renderableDataList[0];
          this.forceUpdate();
        }
      }

      _onFocusOnLastSelectableItemSignal(receivedSignalInfo) {
        if(this.logger.isDebug) {
          this.logger.debug(receivedSignalInfo.toString(), receivedSignalInfo.data);
        }
        if(this.state.renderableDataList.length > 0) {
          this._focusOnItemData = this.state.renderableDataList[this.state.renderableDataList.length - 1];
          this.forceUpdate();
        }
      }

      renderItem(itemKey, props) {
        return <CradminSelectableListItem key={itemKey} {...props} />;
      }

      renderItems() {
        const items = [];
        let previousItemData = null;
        for(let index=0; index < this.state.renderableDataList.length; index++) {
          let itemData = this.state.renderableDataList[index];
          let itemKey = itemData[this.props.keyAttribute];
          let isSelected = this.state.selectedItemsMap.has(itemKey);

          let nextItemData = null;
          let isLast = index == (this.state.renderableDataList.length - 1);
          if(!isLast) {
            nextItemData = this.state.renderableDataList[index + 1];
          }

          let props = Object.assign({
            focusClosestSiblingOnSelect: !this.props.renderSelected
          }, this.props.itemComponentProps, {
            data: itemData,
            itemKey: itemKey,
            isSelected: isSelected,
            signalNameSpace: this.props.signalNameSpace,
            previousItemData: previousItemData,
            nextItemData: nextItemData
          });
          items.push(this.renderItem(itemKey, props));
          previousItemData = itemData;
        }
        return items;
      }

      _sendFocusOnItemSignal(itemData) {
        const itemKey = itemData[this.props.keyAttribute];
        new window.ievv_jsbase_core.SignalHandlerSingleton().send(
          `${this.props.signalNameSpace}.FocusOnSelectableItem.${itemKey}`);
      }

      componentDidUpdate() {
        if(this._focusOnItemData != null) {
          this._sendFocusOnItemSignal(this._focusOnItemData);
          this._focusOnItemData = null;
        }
      }
    }