grapesjs-clot
Version:
Free and Open Source Web Builder Framework
328 lines (287 loc) • 8.27 kB
JavaScript
import Backbone from 'backbone';
import { isUndefined, isString, isFunction } from 'underscore';
import { capitalize } from 'utils/mixins';
import { parse, stringify } from 'flatted';
import CircularJSON from 'circular-json';
import {
ClientState,
ClientStateEnum,
setState,
ApplyingLocalOp,
ApplyingBufferedLocalOp,
} from '../../utils/WebSocket';
import { myEditor } from '../..';
const $ = Backbone.$;
export default Backbone.View.extend({
events: {},
eventCapture: ['change'],
appendInput: 1,
attributes() {
return this.model.get('attributes');
},
templateLabel() {
const { ppfx } = this;
const label = this.getLabel();
return `<div class="${ppfx}label" title="${label}">${label}</div>`;
},
templateInput() {
const { clsField } = this;
return `<div class="${clsField}" data-input></div>`;
},
initialize(o = {}) {
const { config = {} } = o;
const { model, eventCapture } = this;
const { target } = model;
const { type } = model.attributes;
this.config = config;
this.em = config.em;
this.pfx = config.stylePrefix || '';
this.ppfx = config.pStylePrefix || '';
this.target = target;
const { ppfx } = this;
this.clsField = `${ppfx}field ${ppfx}field-${type}`;
[
['change:value', this.onValueChange],
['remove', this.removeView],
].forEach(([event, clb]) => {
model.off(event, clb);
this.listenTo(model, event, clb);
});
model.view = this;
this.listenTo(model, 'change:label', this.render);
this.listenTo(model, 'change:placeholder', this.rerender);
this.events = {};
eventCapture.forEach(event => (this.events[event] = 'onChange'));
this.delegateEvents();
this.init();
},
getClbOpts() {
return {
component: this.target,
trait: this.model,
elInput: this.getInputElem(),
};
},
removeView() {
this.remove();
this.removed();
},
init() {},
removed() {},
onRender() {},
onUpdate() {},
onEvent() {},
/**
* Fires when the input is changed
* @private
*/
onChange(event) {
//console.log('TraitView.js => onChange start');
const el = this.getInputElem();
let id = this.em.getSelected().getId();
let target = this.em.get('DomComponents').getById(id);
let traits = target.getTraits();
let cid = this.model.cid;
let traitIndex = 0;
for (let i = 0; i < traits.length; i++) {
if (traits[i].cid == cid) {
traitIndex = i;
break;
}
}
if (el && !isUndefined(el.value)) {
this.model.set('value', el.value);
}
this.onEvent({
...this.getClbOpts(),
event,
});
//console.log('TraitView.js => onChange end');
let opOpts = {
id: id,
traitIndex: traitIndex,
value: el.value,
};
let op = {
action: 'update-trait',
opts: opOpts,
};
if (ClientState == ClientStateEnum.Synced) {
// set state to ApplyingLocalOp
setState(ClientStateEnum.ApplyingLocalOp);
// increase localTS and set localOp
ApplyingLocalOp(op);
} else if (ClientState == ClientStateEnum.AwaitingACK || ClientState == ClientStateEnum.AwaitingWithBuffer) {
// set state to ApplyingBufferedLocalOp
setState(ClientStateEnum.ApplyingBufferedLocalOp);
// push the op to buffer
ApplyingBufferedLocalOp(op);
}
},
getValueForTarget() {
return this.model.get('value');
},
setInputValue(value) {
const el = this.getInputElem();
el && (el.value = value);
},
/**
* On change callback
* @private
*/
onValueChange(model, value, opts = {}) {
//console.log('trait_manager/view/TraitView.js => onValueChange start');
if (opts.fromTarget) {
this.setInputValue(model.get('value'));
this.postUpdate();
} else {
const val = this.getValueForTarget();
model.setTargetValue(val, opts);
}
//console.log('trait_manager/view/TraitView.js => onValueChange end');
},
/**
* Render label
* @private
*/
renderLabel() {
const { $el, target } = this;
const label = this.getLabel();
let tpl = this.templateLabel(target);
if (this.createLabel) {
tpl =
this.createLabel({
label,
component: target,
trait: this,
}) || '';
}
$el.find('[data-label]').append(tpl);
},
/**
* Returns label for the input
* @return {string}
* @private
*/
getLabel() {
const { em } = this;
const { label, name } = this.model.attributes;
return em.t(`traitManager.traits.labels.${name}`) || capitalize(label || name).replace(/-/g, ' ');
},
/**
* Returns current target component
*/
getComponent() {
return this.target;
},
/**
* Returns input element
* @return {HTMLElement}
* @private
*/
getInputEl() {
if (!this.$input) {
const { em, model } = this;
const md = model;
const { name } = model.attributes;
const plh = md.get('placeholder') || md.get('default') || '';
const type = md.get('type') || 'text';
const min = md.get('min');
const max = md.get('max');
const value = this.getModelValue();
const input = $(`<input type="${type}" placeholder="${plh}">`);
const i18nAttr = em.t(`traitManager.traits.attributes.${name}`) || {};
input.attr(i18nAttr);
if (!isUndefined(value)) {
md.set({ value }, { silent: true });
input.prop('value', value);
}
if (min) {
input.prop('min', min);
}
if (max) {
input.prop('max', max);
}
this.$input = input;
}
return this.$input.get(0);
},
getInputElem() {
const { input, $input } = this;
return input || ($input && $input.get && $input.get(0)) || this.getElInput();
},
getModelValue() {
let value;
const model = this.model;
const target = this.target;
const name = model.get('name');
if (model.get('changeProp')) {
value = target.get(name);
} else {
const attrs = target.get('attributes');
value = model.get('value') || attrs[name];
}
return !isUndefined(value) ? value : '';
},
getElInput() {
return this.elInput;
},
/**
* Renders input
* @private
* */
renderField() {
const { $el, appendInput, model } = this;
const inputs = $el.find('[data-input]');
const el = inputs[inputs.length - 1];
let tpl = model.el;
if (!tpl) {
tpl = this.createInput ? this.createInput(this.getClbOpts()) : this.getInputEl();
}
if (isString(tpl)) {
el.innerHTML = tpl;
this.elInput = el.firstChild;
} else {
appendInput ? el.appendChild(tpl) : el.insertBefore(tpl, el.firstChild);
this.elInput = tpl;
}
model.el = this.elInput;
},
hasLabel() {
const { label } = this.model.attributes;
return !this.noLabel && label !== false;
},
rerender() {
this.model.el = null;
this.render();
},
postUpdate() {
this.onUpdate(this.getClbOpts());
},
render() {
const { $el, pfx, ppfx, model } = this;
const { type, id } = model.attributes;
const hasLabel = this.hasLabel && this.hasLabel();
const cls = `${pfx}trait`;
this.$input = null;
let tmpl = `<div class="${cls} ${cls}--${type}">
${hasLabel ? `<div class="${ppfx}label-wrp" data-label></div>` : ''}
<div class="${ppfx}field-wrp ${ppfx}field-wrp--${type}" data-input>
${
this.templateInput
? isFunction(this.templateInput)
? this.templateInput(this.getClbOpts())
: this.templateInput
: ''
}
</div>
</div>`;
$el.empty().append(tmpl);
hasLabel && this.renderLabel();
this.renderField();
this.el.className = `${cls}__wrp ${cls}__wrp-${id}`;
this.postUpdate();
this.onRender(this.getClbOpts());
return this;
},
});