⌈⌋ ⎇ branch:  Bitrhythm


Artifact Content

Artifact 7c38d9f4901f71e3fd4bc77c5a6f0af427bfa280790f6f7227ac194e70873e12:


'use strict';

let Interface = require('../core/interface');
let Step = require('../models/step');
let math = require('../util/math');
let util = require('../util/util');

/**
* Number
*
* @description Number interface which is controllable by dragging or typing.
*
* @demo <span nexus-ui="number"></span>
*
* @example
* var number = new Nexus.Number('#target')
*
* @example
* var number = new Nexus.Number('#target',{
*   'size': [60,30],
*   'value': 0,
*   'min': 0,
*   'max': 20000,
*   'step': 1
* })
*
* @output
* change
* Fires any time the interface's value changes. <br>
* The event data is the number value of the interface.
*
* @outputexample
* number.on('change',function(v) {
*   console.log(v);
* })
*
*
*/


export default class Number extends Interface {

  constructor() {

    let options = ['value'];

    let defaults = {
      'size': [60,30],
      'value': 0,
      'min': 0,
      'max': 20000,
      'step': 1
    };

    super(arguments,options,defaults);

    this._value = new Step(this.settings.min,this.settings.max,this.settings.step,this.settings.value);

    /*
    Default: 2. How many decimal places to clip the number's visual rendering to. This does not affect number's actual value output -- for that, set the step property to .01, .1, or 1.
    @type {number}
    @example number.decimalPlaces = 2;
    */
    this.decimalPlaces = 2;
    this.actual = 0;

    this.max = this._value.max;

    this.min = this._value.min;

    this.step = this._value.step;

    this.init();
    this.render();

  }

  buildFrame() {
    this.element = document.createElement('input');
    this.element.type = 'text';

    this.element.addEventListener('blur', function () {
      this.element.style.backgroundColor = this.colors.fill;
      this.element.style.color = this.colors.dark;
      if (this.element.value !== this.value) {
        this.value = parseFloat(this.element.value);
        this.render();
      }
    }.bind(this));

    util.setInputFilter(this.element, function(value) {
      return /^-?\d*\.?\d*$/.test(value); });

    this.element.addEventListener('keydown', function (e) {
      if (e.which===13) {
        this.element.blur();
        this.value = this.element.value;
        this.emit('change',this.value);
        this.render();
      }
    }.bind(this), true);

    this.parent.appendChild(this.element);

  }

  sizeInterface() {

    this._minDimension = Math.min(this.width,this.height);

    let styles = 'width: ' + this.width + 'px;';
    styles += 'height: ' + this.height + 'px;';
    styles += 'background-color: #e7e7e7;';
    styles += 'color: #333;';
    styles += 'font-family: arial;';
    styles += 'font-weight: 500;';
    styles += 'font-size:' + this._minDimension/2 + 'px;';
  //  styles += 'highlight: #d18;';
    styles += 'border: none;';
    styles += 'outline: none;';
    styles += 'padding: '+this._minDimension/4+'px '+this._minDimension/4+'px;';
    styles += 'box-sizing: border-box;';
    styles += 'userSelect: text;';
    styles += 'mozUserSelect: text;';
    styles += 'webkitUserSelect: text;';
    this.element.style.cssText += styles;

    // to add eventually
    // var css = '#'+this.elementID+'::selection{ background-color: transparent }';

    this.element.value = this.value;

  }

  colorInterface() {
      this.element.style.backgroundColor = this.colors.fill;
      this.element.style.color = this.colors.dark;
  }

  render() {

    this.element.value = math.prune(this.value,this.decimalPlaces);

  }

  click() {
    this.hasMoved = false;
    this.element.readOnly = true;
	  this.actual = this.value;
    this.initial = { y: this.mouse.y };
    this.changeFactor = math.invert( this.mouse.x / this.width );
  }

  move() {
    this.hasMoved = true;
    if (this.clicked) {

      let newvalue = this.actual - (this.mouse.y - this.initial.y) * ( math.clip( this.max-this.min, 0, 1000 ) / 200 ) * Math.pow(this.changeFactor,2);
      this.value = newvalue;

  		this.render();
      if (this._value.changed) {
        this.emit('change',this.value);
      }

  	}
  }

  release() {
    if (!this.hasMoved) {
      this.element.readOnly = false;
  		this.element.focus();
  		this.element.setSelectionRange(0, this.element.value.length);
  		this.element.style.backgroundColor = this.colors.accent;
  		this.element.style.color = this.colors.light;
    } else {
      document.body.focus();
    }
  }

  /**
  Connect this number interface to a dial or slider
  @param {Interface} element Element to connect to.
  @example number.link(slider)
  */
  link(destination) {
    this.min = destination.min;
    this.max = destination.max;
    this.step = destination.step;
    destination.on('change',(v) => {
      this.passiveUpdate(v);
    });
    this.on('change',(v) => {
      destination.value = v;
    });
    this.value = destination.value;
  /*  return {
      listener1: listener1,
      listener2: listener2,
      destroy: () => {
        listener1.remove() (or similar)
        listener2.remove() (or similar)
      }
    } */
  }

  passiveUpdate(v) {
    this._value.update(v);
    this.render();
  }

  /**
  The interface's current value. If set manually, will update the interface and trigger the output event.
  @type {number}
  @example number.value = 10;
  */
  get value() {
    return this._value.value;
  }
  set value(v) {
    this._value.update(v);
    this.emit('change',this.value);
    this.render();
  }

  /**
  Lower limit of the number's output range
  @type {number}
  @example number.min = 1000;
  */
  get min() {
    return this._value.min;
  }
  set min(v) {
    this._value.min = v;
  }

  /**
  Upper limit of the number's output range
  @type {number}
  @example number.max = 1000;
  */
  get max() {
    return this._value.max;
  }
  set max(v) {
    this._value.max = v;
  }

  /**
  The increment that the number's value changes by.
  @type {number}
  @example number.step = 5;
  */
  get step() {
    return this._value.step;
  }
  set step(v) {
    this._value.step = v;
  }

}