codevise/pageflow

View on GitHub
package/src/ui/views/inputs/ColorInputView.js

Summary

Maintainability
A
40 mins
Test Coverage
import Marionette from 'backbone.marionette';
import _ from 'underscore';
import 'jquery.minicolors';

import {inputView} from '../mixins/inputView';

import template from '../../templates/inputs/textInput.jst';

/**
 * Input view for a color value in hex representation.
 * See {@link inputView} for further options
 *
 * @param {Object} [options]
 *
 * @param {string|function} [options.defaultValue]
 *   Color value to display by default. The corresponding value is not
 *   stored in the model. Selecting the default value when a different
 *   value was set before, unsets the attribute in the model.
 *
 * @param {string} [options.defaultValueBinding]
 *   Name of an attribute the default value depends on. If a function
 *   is used as defaultValue option, it will be passed the value of the
 *   defaultValueBinding attribute each time it changes. If no
 *   defaultValue option is set, the value of the defaultValueBinding
 *   attribute will be used as default value.
 *
 * @param {string[]} [options.swatches]
 *   Preset color values to be displayed inside the picker drop
 *   down. The default value, if present, is always used as the
 *   first swatch automatically.
 *
 * @class
 */
export const ColorInputView = Marionette.ItemView.extend({
  mixins: [inputView],

  template,
  className: 'color_input',

  ui: {
    input: 'input'
  },

  events: {
    'mousedown': 'refreshPicker'
  },

  onRender: function() {
    this.ui.input.minicolors({
      changeDelay: 200,
      change: _.bind(function(color) {
        this._saving = true;

        if (color === this.defaultValue()) {
          this.model.unset(this.options.propertyName);
        }
        else {
          this.model.set(this.options.propertyName, color);
        }

        this._saving = false;
      }, this)
    });

    this.listenTo(this.model, 'change:' + this.options.propertyName, this.load);

    if (this.options.defaultValueBinding) {
      this.listenTo(this.model, 'change:' + this.options.defaultValueBinding, this.updateSettings);
    }

    this.updateSettings();
  },

  updateSettings: function() {
    this.resetSwatchesInStoredSettings();

    this.ui.input.minicolors('settings', {
      defaultValue: this.defaultValue(),
      swatches: this.getSwatches()
    });

    this.load();
  },

  // see https://github.com/claviska/jquery-minicolors/issues/287
  resetSwatchesInStoredSettings: function() {
    const settings = this.ui.input.data('minicolors-settings');

    if (settings) {
      delete settings.swatches;
      this.ui.input.data('minicolors-settings', settings);
    }
  },

  load: function() {
    if (!this._saving) {
      this.ui.input.minicolors('value',
                               this.model.get(this.options.propertyName) || this.defaultValue());
    }

    this.$el.toggleClass('is_default', !this.model.has(this.options.propertyName));
  },

  refreshPicker: function() {
    this.ui.input.minicolors('value', {});
  },

  getSwatches: function() {
    return _.chain([this.defaultValue(), this.options.swatches])
      .flatten()
      .uniq()
      .compact()
      .value();
  },

  defaultValue: function () {
    var bindingValue;

    if (this.options.defaultValueBinding) {
      bindingValue = this.model.get(this.options.defaultValueBinding);
    }

    if (typeof this.options.defaultValue === 'function') {
      return this.options.defaultValue(bindingValue);
    }
    else if ('defaultValue' in this.options) {
      return this.options.defaultValue;
    }
    else {
      return bindingValue;
    }
  }
});