grapesjs
Version:
Free and Open Source Web Builder Framework
297 lines (243 loc) • 7.28 kB
JavaScript
import { bindAll } from 'underscore';
import {on, off} from 'utils/mixins';
const Input = require('./Input');
const Backbone = require('backbone');
const $ = Backbone.$;
module.exports = Input.extend({
events: {
'change input': 'handleChange',
'change select': 'handleUnitChange',
'click [data-arrow-up]': 'upArrowClick',
'click [data-arrow-down]': 'downArrowClick',
'mousedown [data-arrows]': 'downIncrement',
},
template() {
const ppfx = this.ppfx;
return `
<span class="${ppfx}input-holder"></span>
<span class="${ppfx}field-units"></span>
<div class="${ppfx}field-arrows" data-arrows>
<div class="${ppfx}field-arrow-u" data-arrow-up></div>
<div class="${ppfx}field-arrow-d" data-arrow-down></div>
</div>
`;
},
inputClass() {
const ppfx = this.ppfx;
return this.opts.contClass || `${ppfx}field ${ppfx}field-integer`;
},
initialize(opts = {}) {
Input.prototype.initialize.apply(this, arguments);
bindAll(this, 'moveIncrement', 'upIncrement');
this.doc = document;
this.listenTo(this.model, 'change:unit', this.handleModelChange);
},
/**
* Set value to the model
* @param {string} value
* @param {Object} opts
*/
setValue(value, opts) {
var opt = opts || {};
var valid = this.validateInputValue(value, {deepCheck: 1});
var validObj = {value: valid.value};
// If found some unit value
if(valid.unit || valid.force) {
validObj.unit = valid.unit;
}
this.model.set(validObj, opt);
// Generally I get silent when I need to reflect data to view without
// reupdating the target
if(opt.silent) {
this.handleModelChange();
}
},
/**
* Handled when the view is changed
*/
handleChange(e) {
e.stopPropagation();
this.setValue(this.getInputEl().value);
this.elementUpdated();
},
/**
* Handled when the view is changed
*/
handleUnitChange(e) {
e.stopPropagation();
var value = this.getUnitEl().value;
this.model.set('unit', value);
this.elementUpdated();
},
/**
* Fired when the element of the property is updated
*/
elementUpdated() {
this.model.trigger('el:change');
},
/**
* Updates the view when the model is changed
* */
handleModelChange() {
const model = this.model;
this.getInputEl().value = model.get('value');
const unitEl = this.getUnitEl();
unitEl && (unitEl.value = model.get('unit'));
},
/**
* Get the unit element
* @return {HTMLElement}
*/
getUnitEl() {
if (!this.unitEl) {
const model = this.model;
const units = model.get('units') || [];
if (units.length) {
const options = [];
units.forEach(unit => {
const selected = unit == model.get('unit') ? 'selected' : '';
options.push(`<option ${selected}>${unit}</option>`);
});
const temp = document.createElement('div');
temp.innerHTML = `<select class="${this.ppfx}input-unit">${options.join('')}</select>`;
this.unitEl = temp.firstChild;
}
}
return this.unitEl;
},
/**
* Invoked when the up arrow is clicked
* */
upArrowClick() {
const model = this.model;
const step = model.get('step');
let value = model.get('value');
value = this.normalizeValue(value + step);
var valid = this.validateInputValue(value);
model.set('value', valid.value);
this.elementUpdated();
},
/**
* Invoked when the down arrow is clicked
* */
downArrowClick() {
const model = this.model;
const step = model.get('step');
const value = model.get('value');
const val = this.normalizeValue(value - step);
var valid = this.validateInputValue(val);
model.set('value', valid.value);
this.elementUpdated();
},
/**
* Change easily integer input value with click&drag method
* @param Event
*
* @return void
* */
downIncrement(e) {
e.preventDefault();
this.moved = 0;
var value = this.model.get('value');
value = this.normalizeValue(value);
this.current = {y: e.pageY, val: value};
on(this.doc, 'mousemove', this.moveIncrement);
on(this.doc, 'mouseup', this.upIncrement);
},
/** While the increment is clicked, moving the mouse will update input value
* @param Object
*
* @return bool
* */
moveIncrement(ev) {
this.moved = 1;
const model = this.model;
const step = model.get('step');
const data = this.current;
var pos = this.normalizeValue(data.val + (data.y - ev.pageY) * step);
this.prValue = this.validateInputValue(pos).value;
model.set('value', this.prValue, {avoidStore: 1});
return false;
},
/**
* Stop moveIncrement method
* */
upIncrement() {
const model = this.model;
const step = model.get('step');
off(this.doc, 'mouseup', this.upIncrement);
off(this.doc, 'mousemove', this.moveIncrement);
if(this.prValue && this.moved) {
var value = this.prValue - step;
model.set('value', value, {avoidStore: 1})
.set('value', value + step);
this.elementUpdated();
}
},
normalizeValue(value, defValue = 0) {
const model = this.model;
const step = model.get('step');
let stepDecimals = 0;
if (isNaN(value)) {
return defValue;
}
value = parseFloat(value);
if (Math.floor(value) !== value) {
const side = step.toString().split('.')[1];
stepDecimals = side ? side.length : 0;
}
return stepDecimals ? parseFloat(value.toFixed(stepDecimals)) : value;
},
/**
* Validate input value
* @param {String} value Raw value
* @param {Object} opts Options
* @return {Object} Validated string
*/
validateInputValue(value, opts) {
var force = 0;
var opt = opts || {};
var model = this.model;
var val = value !== '' ? value : model.get('defaults');
var units = model.get('units') || [];
var unit = model.get('unit') || (units.length && units[0]) || '';
var max = model.get('max');
var min = model.get('min');
if(opt.deepCheck) {
var fixed = model.get('fixedValues') || [];
if (val) {
// If the value is one of the fixed values I leave it as it is
var regFixed = new RegExp('^' + fixed.join('|'), 'g');
if (fixed.length && regFixed.test(val)) {
val = val.match(regFixed)[0];
unit = '';
force = 1;
} else {
var valCopy = val + '';
val += ''; // Make it suitable for replace
val = parseFloat(val.replace(',', '.'));
val = !isNaN(val) ? val : model.get('defaults');
var uN = valCopy.replace(val, '');
// Check if exists as unit
if(_.indexOf(units, uN) >= 0)
unit = uN;
}
}
}
if(typeof max !== 'undefined' && max !== '')
val = val > max ? max : val;
if(typeof min !== 'undefined' && min !== '')
val = val < min ? min : val;
return {
force,
value: val,
unit
};
},
render() {
Input.prototype.render.call(this);
const unit = this.getUnitEl();
unit && this.$el.find(`.${this.ppfx}field-units`).get(0).appendChild(unit);
return this;
}
});