Home Reference Source

src/components/_classes/multivalue/Multivalue.js

import Field from '../field/Field';
import NativePromise from 'native-promise-only';
import _ from 'lodash';

export default class Multivalue extends Field {
  get dataValue() {
    const parent = super.dataValue;

    if (!parent && this.component.multiple) {
      return [];
    }
    return parent;
  }

  set dataValue(value) {
    super.dataValue = value;
  }

  get defaultValue() {
    let value = super.defaultValue;

    if (this.component.multiple) {
      if (_.isArray(value)) {
        value = !value.length ? [super.emptyValue] : value;
      }
      else {
        value = [value];
      }
    }

    return value;
  }

  get addAnother() {
    return this.t(this.component.addAnother || 'Add Another');
  }

  useWrapper() {
    return this.component.hasOwnProperty('multiple') && this.component.multiple;
  }

  render() {
    // If single value field.
    if (!this.useWrapper()) {
      return super.render(
        `<div ref="element">
          ${this.renderElement(
            this.component.type !== 'hidden' ? this.dataValue : ''
          )}
        </div>`
      );
    }

    // Make sure dataValue is in the correct array format.
    let dataValue = this.dataValue;
    if (!Array.isArray(dataValue)) {
      dataValue = dataValue ? [dataValue] : [];
    }

    // If multiple value field.
    return super.render(this.renderTemplate('multiValueTable', {
      rows: dataValue.map(this.renderRow.bind(this)).join(''),
      disabled: this.disabled,
      addAnother: this.addAnother,
    }));
  }

  renderElement() {
    return '';
  }

  renderRow(value, index) {
    return this.renderTemplate('multiValueRow', {
      index,
      disabled: this.disabled,
      element: `${this.renderElement(value, index)}`,
    });
  }

  attach(dom) {
    const superAttach = super.attach(dom);
    this.loadRefs(dom, {
      addButton: 'multiple',
      input: 'multiple',
      removeRow: 'multiple',
      mask: 'multiple',
      select: 'multiple',
    });

    const promises = [];

    this.refs.input.forEach((element, index) => {
      promises.push(this.attachElement.call(this, element, index));
    });

    if (!this.component.multiple) {
      return NativePromise.all(promises);
    }

    this.refs.removeRow.forEach((removeButton, index) => {
      this.addEventListener(removeButton, 'click', (event) => {
        event.preventDefault();
        this.removeValue(index);
      });
    });

    // If single value field.
    this.refs.addButton.forEach((addButton) => {
      this.addEventListener(addButton, 'click', (event) => {
        event.preventDefault();
        this.addValue();
      });
    });
    return superAttach.then(() => {
      return NativePromise.all(promises);
    });
  }

  detach() {
    if (this.refs.input && this.refs.input.length) {
      this.refs.input.forEach((input) => {
        if (input.mask) {
          input.mask.destroy();
        }
        if (input.widget) {
          input.widget.destroy();
        }
      });
    }
    if (this.refs.mask && this.refs.mask.length) {
      this.refs.mask.forEach((input) => {
        if (input.mask) {
          input.mask.destroy();
        }
      });
    }
    super.detach();
  }

  /**
   * Attach inputs to the element.
   *
   * @param element
   * @param index
   */
  attachElement(element, index) {
    this.addEventListener(element, this.inputInfo.changeEvent, () => {
      // Delay update slightly to give input mask a chance to run.
      const textCase = _.get(this.component, 'case', 'mixed');

      if (textCase !== 'mixed') {
        const {
          selectionStart,
          selectionEnd,
        } = element;

        if (textCase === 'uppercase' && element.value) {
          element.value = element.value.toUpperCase();
        }
        if (textCase === 'lowercase' && element.value) {
          element.value = element.value.toLowerCase();
        }

        if (element.selectionStart && element.selectionEnd) {
          element.selectionStart = selectionStart;
          element.selectionEnd = selectionEnd;
        }
      }
      // If a mask is present, delay the update to allow mask to update first.
      if (element.mask) {
        setTimeout(() => {
          return this.updateValue(null, {
            modified: (this.component.type !== 'hidden')
          }, index);
        }, 1);
      }
      else {
        return this.updateValue(null, {
          modified: (this.component.type !== 'hidden')
        }, index);
      }
    });

    if (!this.attachMultiMask(index)) {
      this.setInputMask(element);
    }
  }

  onSelectMaskHandler(event) {
    this.updateMask(event.target.maskInput, this.getMaskPattern(event.target.value));
  }

  getMaskPattern(maskName) {
    if (!this.multiMasks) {
      this.multiMasks = {};
    }
    if (this.multiMasks[maskName]) {
      return this.multiMasks[maskName];
    }
    const mask = this.component.inputMasks.find(inputMask => inputMask.label === maskName);
    this.multiMasks[maskName] = mask ? mask.mask : this.component.inputMasks[0].mask;
    return this.multiMasks[maskName];
  }

  attachMultiMask(index) {
    if (!(this.isMultipleMasksField && this.component.inputMasks.length && this.refs.input.length)) {
      return false;
    }

    const maskSelect = this.refs.select[index];
    maskSelect.onchange = this.onSelectMaskHandler.bind(this);
    maskSelect.maskInput = this.refs.mask[index];
    this.setInputMask(maskSelect.maskInput, this.component.inputMasks[0].mask);
    return true;
  }

  updateMask(input, mask) {
    if (!mask) {
      return;
    }
    this.setInputMask(input, mask, !this.component.placeholder);
    this.updateValue();
  }

  /**
   * Adds a new empty value to the data array.
   */
  addNewValue(value) {
    if (value === undefined) {
      value = this.component.defaultValue ?
      this.component.defaultValue : this.emptyValue;
      // If default is an empty aray, default back to empty value.
      if (Array.isArray(value) && value.length === 0) {
        value = this.emptyValue;
      }
    }
    let dataValue = this.dataValue || [];
    if (!Array.isArray(dataValue)) {
      dataValue = [dataValue];
    }

    if (Array.isArray(value)) {
      dataValue = dataValue.concat(value);
    }
    else {
      dataValue.push(value);
    }
    this.dataValue = dataValue;
  }

  /**
   * Adds a new empty value to the data array, and add a new row to contain it.
   */
  addValue() {
    this.addNewValue();
    this.redraw();
    this.checkConditions();
    if (!this.isEmpty(this.dataValue)) {
      this.restoreValue();
    }
    if (this.root) {
      this.root.onChange();
    }
  }
}