Browse Source

Merge branch 'dev' into up-style-manager

up-style-manager
Artur Arseniev 5 years ago
parent
commit
5be9b7884b
  1. 1
      docs/api/editor.md
  2. 2
      src/asset_manager/view/FileUploader.js
  3. 2
      src/canvas/view/CanvasView.js
  4. 16
      src/commands/view/ComponentDrag.js
  5. 2
      src/commands/view/SelectComponent.js
  6. 101
      src/dom_components/model/Component.js
  7. 3
      src/domain_abstract/ui/InputColor.js
  8. 1
      src/editor/index.js
  9. 7
      src/index.js
  10. 2
      src/rich_text_editor/index.js
  11. 4
      src/trait_manager/model/Trait.js
  12. 20
      src/utils/Sorter.js
  13. 9
      src/utils/mixins.js
  14. 118
      test/specs/utils/Sorter.js

1
docs/api/editor.md

@ -124,7 +124,6 @@ Check the [Pages][8] module.
### General
* `canvasScroll` - Canvas is scrolled
* `update` - The structure of the template is updated (its HTML/CSS)
* `undo` - Undo executed
* `redo` - Redo executed

2
src/asset_manager/view/FileUploader.js

@ -26,7 +26,7 @@ export default Backbone.View.extend(
initialize(opts = {}) {
this.options = opts;
const c = opts.config || {};
this.module = c.module;
this.module = opts.module;
this.config = c;
this.em = this.config.em;
this.pfx = c.stylePrefix || '';

2
src/canvas/view/CanvasView.js

@ -70,7 +70,7 @@ export default Backbone.View.extend({
scroll &&
component.views.forEach(view => {
view._getFrame() !== currFrame && view.scrollIntoView(scroll);
view._getFrame() === currFrame && view.scrollIntoView(scroll);
});
},

16
src/commands/view/ComponentDrag.js

@ -321,17 +321,31 @@ export default {
const { Canvas } = editor;
const style = target.getStyle();
const position = 'absolute';
const relPos = [position, 'relative'];
onStart && onStart(this._getDragData());
if (isTran) return;
if (style.position !== position) {
let { left, top, width, height } = Canvas.offset(target.getEl());
let parent = target.parent();
let parentRel;
// Check if to center the target to the pointer position
// Check for the relative parent
do {
const pStyle = parent.getStyle();
parentRel = relPos.indexOf(pStyle.position) >= 0 ? parent : null;
parent = parent.parent();
} while (parent && !parentRel);
// Center the target to the pointer position (used in Droppable for Blocks)
if (center) {
const { x, y } = Canvas.getMouseRelativeCanvas(event);
left = x;
top = y;
} else if (parentRel) {
const offsetP = Canvas.offset(parentRel.getEl());
left = left - offsetP.left;
top = top - offsetP.top;
}
this.setPosition({

2
src/commands/view/SelectComponent.js

@ -325,7 +325,7 @@ export default {
*/
select(model, event = {}) {
if (!model) return;
this.editor.select(model, { scroll: {}, event });
this.editor.select(model, { event });
this.initResize(model);
},

101
src/dom_components/model/Component.js

@ -61,10 +61,10 @@ export const keyUpdateInside = `${keyUpdate}-inside`;
* @property {Object} [attributes={}] Key-value object of the component's attributes, eg. `{ title: 'Hello' }` Default: `{}`
* @property {String} [name=''] Name of the component. Will be used, for example, in Layers and badges
* @property {Boolean} [removable=true] When `true` the component is removable from the canvas, default: `true`
* @property {Boolean|String} [draggable=true] Indicates if it's possible to drag the component inside others.
* @property {Boolean|String|Function} [draggable=true] Indicates if it's possible to drag the component inside others.
* You can also specify a query string to indentify elements,
* eg. `'.some-class[title=Hello], [data-gjs-type=column]'` means you can drag the component only inside elements
* containing `some-class` class and `Hello` title, and `column` components. Default: `true`
* containing `some-class` class and `Hello` title, and `column` components. In the case of a function, target and destination components are passed as arguments, return a Boolean to indicate if the drag is possible. Default: `true`
* @property {Boolean|String|Function} [droppable=true] Indicates if it's possible to drop other components inside. You can use
* a query string as with `draggable`. In the case of a function, target and destination components are passed as arguments, return a Boolean to indicate if the drop is possible. Default: `true`
* @property {Boolean} [badgable=true] Set to false if you don't want to see the badge (with the name) over the component. Default: `true`
@ -951,9 +951,8 @@ export default class Component extends Model.extend(Styleable) {
initTraits(changed) {
const { em } = this;
const event = 'change:traits';
const toListen = [this, event, this.initTraits];
this.stopListening(...toListen);
this.loadTraits();
this.off(event, this.initTraits);
this.__loadTraits();
const attrs = { ...this.get('attributes') };
const traits = this.get('traits');
traits.each(trait => {
@ -964,7 +963,7 @@ export default class Component extends Model.extend(Styleable) {
}
});
traits.length && this.set('attributes', attrs);
this.listenTo(...toListen);
this.on(event, this.initTraits);
changed && em && em.trigger('component:toggled');
return this;
}
@ -1141,46 +1140,71 @@ export default class Component extends Model.extend(Styleable) {
}
}
/**
* Load traits
* @param {Array} traits
* @private
*/
loadTraits(traits, opts = {}) {
traits = traits || this.get('traits');
traits = isFunction(traits) ? traits(this) : traits;
__loadTraits(tr, opts = {}) {
let traitsI = tr || this.get('traits');
if (!(traits instanceof Traits)) {
const trt = new Traits([], this.opt);
trt.setTarget(this);
if (!(traitsI instanceof Traits)) {
traitsI = isFunction(traitsI) ? traitsI(this) : traitsI;
const traits = new Traits([], this.opt);
traits.setTarget(this);
if (traits.length) {
traits.forEach(tr => tr.attributes && delete tr.attributes.value);
trt.add(traits);
if (traitsI.length) {
traitsI.forEach(tr => tr.attributes && delete tr.attributes.value);
traits.add(traitsI);
}
this.set('traits', trt, opts);
this.set({ traits }, opts);
}
return this;
}
/**
* Get the trait by id/name
* Get traits.
* @returns {Array<Trait>}
* @example
* const traits = component.getTraits();
* console.log(traits);
* // [Trait, Trait, Trait, ...]
*/
getTraits() {
this.__loadTraits();
return [...this.get('traits').models];
}
/**
* Replace current collection of traits with a new one.
* @param {Array<Object>} traits Array of trait definitions
* @returns {Array<Trait>}
* @example
* const traits = component.setTraits([{ type: 'checkbox', name: 'disabled'}, ...]);
* console.log(traits);
* // [Trait, ...]
*/
setTraits(traits) {
const tr = isArray(traits) ? traits : [traits];
this.set({ traits: tr });
return this.getTraits();
}
/**
* Get the trait by id/name.
* @param {String} id The `id` or `name` of the trait
* @return {Trait} Trait model
* @return {Trait|null} Trait getModelToStyle
* @example
* const traitTitle = component.getTrait('title');
* traitTitle && traitTitle.set('label', 'New label');
*/
getTrait(id) {
return this.get('traits').filter(trait => {
return trait.get('id') === id || trait.get('name') === id;
})[0];
return (
this.getTraits().filter(trait => {
return trait.get('id') === id || trait.get('name') === id;
})[0] || null
);
}
/**
* Update a trait
* Update a trait.
* @param {String} id The `id` or `name` of the trait
* @param {Object} props Object with the props to update
* @return {this}
@ -1191,10 +1215,9 @@ export default class Component extends Model.extend(Styleable) {
* });
*/
updateTrait(id, props) {
const { em } = this;
const trait = this.getTrait(id);
trait && trait.set(props);
em && em.trigger('component:toggled');
this.em?.trigger('component:toggled');
return this;
}
@ -1215,25 +1238,25 @@ export default class Component extends Model.extend(Styleable) {
/**
* Remove trait/s by id/s.
* @param {String|Array<String>} id The `id`/`name` of the trait (or an array)
* @return {Array} Array of removed traits
* @return {Array<Trait>} Array of removed traits
* @example
* component.removeTrait('title');
* component.removeTrait(['title', 'id']);
*/
removeTrait(id) {
const { em } = this;
const ids = isArray(id) ? id : [id];
const toRemove = ids.map(id => this.getTrait(id));
const removed = this.get('traits').remove(toRemove);
em && em.trigger('component:toggled');
return removed;
const traits = this.get('traits');
const removed = toRemove.length ? traits.remove(toRemove) : [];
this.em?.trigger('component:toggled');
return isArray(removed) ? removed : [removed];
}
/**
* Add trait/s by id/s.
* Add new trait/s.
* @param {String|Object|Array<String|Object>} trait Trait to add (or an array of traits)
* @param {Options} opts Options for the add
* @return {Array} Array of added traits
* @return {Array<Trait>} Array of added traits
* @example
* component.addTrait('title', { at: 1 }); // Add title trait (`at` option is the position index)
* component.addTrait({
@ -1243,10 +1266,10 @@ export default class Component extends Model.extend(Styleable) {
* component.addTrait(['title', {...}, ...]);
*/
addTrait(trait, opts = {}) {
const { em } = this;
this.__loadTraits();
const added = this.get('traits').add(trait, opts);
em && em.trigger('component:toggled');
return added;
this.em?.trigger('component:toggled');
return isArray(added) ? added : [added];
}
/**

3
src/domain_abstract/ui/InputColor.js

@ -82,10 +82,9 @@ export default Input.extend({
*/
getColorEl() {
if (!this.colorEl) {
const { em } = this;
const { em, model } = this;
const self = this;
const ppfx = this.ppfx;
var model = this.model;
var colorEl = $(`<div class="${this.ppfx}field-color-picker"></div>`);
var cpStyle = colorEl.get(0).style;

1
src/editor/index.js

@ -90,7 +90,6 @@
* ### Pages
* Check the [Pages](/api/pages.html) module.
* ### General
* * `canvasScroll` - Canvas is scrolled
* * `update` - The structure of the template is updated (its HTML/CSS)
* * `undo` - Undo executed
* * `redo` - Redo executed

7
src/index.js

@ -2,6 +2,7 @@ import { isElement, isFunction } from 'underscore';
import $ from 'utils/cash-dom';
import Editor from './editor';
import polyfills from 'utils/polyfills';
import { getGlobal } from 'utils/mixins';
import PluginManager from './plugin_manager';
polyfills();
@ -57,13 +58,13 @@ export default {
// Load plugins
config.plugins.forEach(pluginId => {
let plugin = plugins.get(pluginId);
let plugin = isFunction(pluginId) ? pluginId : plugins.get(pluginId);
const plgOptions = config.pluginsOpts[pluginId] || {};
// Try to search in global context
if (!plugin) {
const wplg = window[pluginId];
plugin = wplg && wplg.default ? wplg.default : wplg;
const wplg = getGlobal()[pluginId];
plugin = wplg?.default || wplg;
}
if (plugin) {

2
src/rich_text_editor/index.js

@ -34,7 +34,7 @@ export default () => {
let config = {};
let toolbar, actions, lastEl, lastElPos, globalRte;
const eventsUp =
'change:canvasOffset canvasScroll frame:scroll component:update';
'change:canvasOffset frame:scroll component:update';
const hideToolbar = () => {
const style = toolbar.style;
const size = '-1000px';

4
src/trait_manager/model/Trait.js

@ -47,6 +47,10 @@ export default Backbone.Model.extend({
this.set({ value }, { fromTarget: 1 });
},
getValue() {
return this.getTargetValue();
},
getTargetValue() {
const name = this.get('name');
const target = this.target;

20
src/utils/Sorter.js

@ -619,16 +619,24 @@ export default Backbone.View.extend({
return result;
}
// check if the source is draggable in target
// Check if the source is draggable in target
let draggable = srcModel.get('draggable');
draggable = draggable instanceof Array ? draggable.join(', ') : draggable;
result.dragInfo = draggable;
draggable = isString(draggable) ? this.matches(trg, draggable) : draggable;
result.draggable = draggable;
if (isFunction(draggable)) {
const res = draggable(srcModel, trgModel);
result.dragInfo = res;
result.draggable = res;
draggable = res;
} else {
draggable = draggable instanceof Array ? draggable.join(', ') : draggable;
result.dragInfo = draggable;
draggable = isString(draggable)
? this.matches(trg, draggable)
: draggable;
result.draggable = draggable;
}
// Check if the target could accept the source
let droppable = trgModel.get('droppable');
if (isFunction(droppable)) {
const res = droppable(srcModel, trgModel);
result.droppable = res;

9
src/utils/mixins.js

@ -1,7 +1,16 @@
import { keys, isUndefined, isElement, isArray } from 'underscore';
export const isDef = value => typeof value !== 'undefined';
export const hasWin = () => typeof window !== 'undefined';
export const getGlobal = () =>
typeof globalThis !== 'undefined'
? globalThis
: typeof window !== 'undefined'
? window
: global;
export const toLowerCase = str => (str || '').toLowerCase();
const elProt = hasWin() ? window.Element.prototype : {};

118
test/specs/utils/Sorter.js

@ -436,56 +436,100 @@ describe('Sorter', () => {
});
describe('Valid Target with components', () => {
var parentModel;
var parentView;
var sorter;
describe('Droppable', () => {
var parentModel;
var parentView;
beforeEach(() => {
parentModel = new Component({
droppable: (srcModel, trgModel) => {
if (srcModel.getEl().className === 'canDrop') {
return true;
beforeEach(() => {
parentModel = new Component({
droppable: (srcModel, trgModel) => {
return srcModel.getEl().className === 'canDrop';
}
return false;
}
});
parentView = new ComponentTextView({
model: parentModel
});
});
parentView = new ComponentTextView({
model: parentModel
afterEach(() => {
parentView.remove();
});
sorter = new Sorter({ container: parentView.el });
});
test('Droppable function', () => {
var srcModel = new Component({
tagName: 'div',
draggable: true,
content: 'Content text',
attributes: { class: 'canDrop' }
});
var srcView = new ComponentTextView({
model: srcModel
});
expect(obj.validTarget(parentView.el, srcView.el).valid).toEqual(true);
});
afterEach(() => {
parentView.remove();
test('Not droppable function', () => {
var srcModel = new Component({
tagName: 'div',
draggable: true,
content: 'Content text',
attributes: { class: 'cannotDrop' }
});
var srcView = new ComponentTextView({
model: srcModel
});
expect(obj.validTarget(parentView.el, srcView.el).valid).toEqual(false);
});
});
test('Droppable function', () => {
var srcModel = new Component({
tagName: 'div',
draggable: true, // Can't move it
content: 'Content text', // Text inside component
attributes: { class: 'canDrop' }
});
var srcView = new ComponentTextView({
model: srcModel
});
describe('Draggable', () => {
var srcModel;
var srcView;
expect(obj.validTarget(parentView.el, srcView.el).valid).toEqual(true);
});
beforeEach(() => {
srcModel = new Component({
draggable: (srcModel, trgModel) => {
return trgModel.getEl().className === 'canDrag';
}
});
srcView = new ComponentTextView({
model: srcModel
});
});
test('Not droppable function', () => {
var srcModel = new Component({
tagName: 'div',
draggable: true,
content: 'Content text',
attributes: { class: 'cannotDrop' }
afterEach(() => {
srcView.remove();
});
var srcView = new ComponentTextView({
model: srcModel
test('Draggable function', () => {
var parentModel = new Component({
tagName: 'div',
droppable: true,
content: 'Content text',
attributes: { class: 'canDrag' }
});
var parentView = new ComponentTextView({
model: parentModel
});
expect(obj.validTarget(parentView.el, srcView.el).valid).toEqual(true);
});
expect(obj.validTarget(parentView.el, srcView.el).valid).toEqual(false);
test('Not draggable function', () => {
var parentModel = new Component({
tagName: 'div',
droppable: true,
content: 'Content text',
attributes: { class: 'cannotDrag' }
});
var parentView = new ComponentTextView({
model: parentModel
});
expect(obj.validTarget(parentView.el, srcView.el).valid).toEqual(false);
});
});
});
});

Loading…
Cancel
Save