Browse Source

Merge branch 'improve-commands' into dev

pull/2031/head
Artur Arseniev 8 years ago
parent
commit
2e9d04248a
  1. 20
      dist/grapes.js
  2. 8
      dist/grapes.min.js
  3. 2
      dist/grapes.min.js.map
  4. 130
      docs/modules/Commands.md
  5. 2
      package-lock.json
  6. 2
      package.json
  7. 27
      src/commands/config/config.js
  8. 66
      src/commands/index.js
  9. 8
      src/commands/view/ComponentExit.js
  10. 232
      src/commands/view/CreateComponent.js
  11. 2
      src/commands/view/MoveComponent.js
  12. 13
      src/commands/view/SelectParent.js
  13. 2
      src/dom_components/model/Component.js
  14. 2
      src/panels/config/config.js

20
dist/grapes.js

@ -25079,6 +25079,7 @@ module.exports = function () {
var elem = (0, _mixins.getElement)(el);
var cv = this.getCanvasView();
if (!elem) return;
if (!cv.isElInViewport(elem) || opts.force) {
elem.scrollIntoView(opts);
@ -27828,7 +27829,13 @@ module.exports = {
width = _editor$Canvas$offset.width,
height = _editor$Canvas$offset.height;
this.setPosition({ x: left, y: top, position: position, width: width, height: height });
this.setPosition({
x: left,
y: top,
width: width + 'px',
height: height + 'px',
position: position
});
}
},
onDrag: function onDrag() {
@ -29962,16 +29969,15 @@ module.exports = {
* On frame scroll callback
* @private
*/
onFrameScroll: function onFrameScroll(e) {
onFrameScroll: function onFrameScroll() {
var el = this.cacheEl;
if (el) {
var elPos = this.getElementPos(el);
this.updateBadge(el, elPos);
var model = this.em.getSelected();
if (model) {
this.updateToolbarPos(model.view.el);
}
var viewEl = model && model.getEl();
viewEl && this.updateToolbarPos(viewEl);
}
},
@ -39262,7 +39268,7 @@ module.exports = function () {
plugins: plugins,
// Will be replaced on build
version: '0.14.58',
version: '0.14.59',
/**
* Initialize the editor with passed options

8
dist/grapes.min.js

File diff suppressed because one or more lines are too long

2
dist/grapes.min.js.map

File diff suppressed because one or more lines are too long

130
docs/modules/Commands.md

@ -0,0 +1,130 @@
---
title: Commands
---
# Commands
A basic command in GrapesJS it's a simple function, but you will see in this guide how powerfull they can be. The main goal of the Command module is to centralize functions and be easily reused across the editor. Another big advantage of using commands is the ability to track them, extend or even interrupt beside some conditions.
::: warning
This guide is referring to GrapesJS v0.14.59 or higher
:::
[[toc]]
## Basic configuration
You can create your commands already from the initialization by passing them in the `commands.defaults` options:
```js
const editor = grapesjs.init({
...
commands: {
defaults: [
{
// id and run are mandatory in this case
id: 'my-command-id',
run() {
alert('This is my command');
},
}, {
id: '...',
// ...
}
],
}
});
```
For all other available options check directly the [configuration source file](https://github.com/artf/grapesjs/blob/dev/src/commands/config/config.js).
Most commonly commands are created dynamicly post initialization, in that case you'll need to use the [Commands API](api/commands.html) (eg. this is what you need if you create a plugin)
```js
const commands = editor.Commands;
commands.add('my-command-id', editor => {
alert('This is my command');
});
// or it would be the same...
commands.add('my-command-id', {
run(editor) {
alert('This is my command');
},
});
```
As you see the definiton is quite easy, you just add an ID and the callback function. The [Editor](api/editor.html) instance is passed as the first argument to the callback so you can access any other module or API method.
Now if you want to call that command you should just run this
```js
editor.runCommand('my-command-id');
```
You could also pass options if you need
```js
editor.runCommand('my-command-id', { some: 'option' });
```
Then you can get the same object as a third argument of the callback.
```js
commands.add('my-command-id', (editor, sender, options = {}) => {
alert(`This is my command ${options.some}`);
});
```
The second argument, `sender`, just indicates who requested the command, in our case will be always the `editor`
Until now there is nothing exiting except a common entry point for functions, but we'll see later its real advantages.
## Default commands
GrapesJS comes along with some default set of commands and you can get a list of all currently availlable commands via `editor.Commands.getAll()`. This will give you an object of all available commands, so, for example, also those added via plugins. You can recognize default commands by their namespace `core:*`, we also recommend to use namepsaces in your own custom commands, but let's get a look more in detail here:
* [`core:canvas-clear`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/CanvasClear.js) - Clear all the content from the canvas (HTML and CSS)
<!-- * `core:canvas-move` -->
<!-- * `core:component-drag` -->
<!-- * `core:component-style-clear` -->
* [`core:component-delete`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/ComponentDelete.js) - Delete a component
* [`core:component-enter`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/ComponentEnter.js) - Select the first children component of the selected one
* [`core:component-exit`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/ComponentExit.js) - Select the parent component of the current selected one
* [`core:component-next`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/ComponentNext.js) - Select the next sibling component
* [`core:component-prev`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/ComponentPrev.js) - Select the previous sibling component
* [`core:component-outline`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/SwitchVisibility.js) - Enable outline border on components
* [`core:component-offset`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/ShowOffset.js) - Enable components offset (margins, paddings)
* [`core:component-select`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/SelectComponent.js) - Enable the process of selecting components in the canvas
* [`core:copy`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/CopyComponent.js) - Copy the current selected component
* [`core:paste`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/PasteComponent.js) - Paste copied component
* [`core:preview`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/Preview.js) - Show the preview of the template in canvas
* [`core:fullscreen`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/Fullscreen.js) - Set the editor fullscreen
* [`core:open-code`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/ExportTemplate.js) - Open a default panel with the template code
* [`core:open-layers`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/OpenLayers.js) - Open a default panel with layers
* [`core:open-styles`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/OpenStyleManager.js) - Open a default panel with the style manager
* [`core:open-traits`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/OpenTraitManager.js) - Open a default panel with the trait manager
* [`core:open-blocks`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/OpenBlocks.js) - Open a default panel with the blocks
* [`core:open-assets`](https://github.com/artf/grapesjs/blob/dev/src/commands/view/OpenAssets.js) - Open a default panel with the assets
* `core:undo` - Call undo operation
* `core:redo` - Call redo operation
tlb-clone
tlb-delete
tlb-move
## Statefull commands
## Events

2
package-lock.json

@ -1,6 +1,6 @@
{
"name": "grapesjs",
"version": "0.14.58",
"version": "0.14.59",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

2
package.json

@ -1,7 +1,7 @@
{
"name": "grapesjs",
"description": "Free and Open Source Web Builder Framework",
"version": "0.14.58",
"version": "0.14.59",
"author": "Artur Arseniev",
"license": "BSD-3-Clause",
"homepage": "http://grapesjs.com",

27
src/commands/config/config.js

@ -1,31 +1,10 @@
module.exports = {
ESCAPE_KEY: 27,
export default {
stylePrefix: 'com-',
// Default array of commands
defaults: [],
// If true, stateful commands (with `run` and `stop` methods) can't be runned multiple times.
// So, if the command is already active, running it again will not execute the `run` method
strict: 1,
// Editor model
// @deprecated
em: null,
// If true center new first-level components
// @deprecated
firstCentered: true,
// If true the new component will created with 'height', else 'min-height'
// @deprecated
newFixedH: false,
// Minimum height (in px) of new component
// @deprecated
minComponentH: 50,
// Minimum width (in px) of component on creation
// @deprecated
minComponentW: 50
strict: 1
};

66
src/commands/index.js

@ -28,13 +28,13 @@
import { isFunction, includes } from 'underscore';
import CommandAbstract from './view/CommandAbstract';
import defaults from './config/config';
module.exports = () => {
let em;
var c = {},
commands = {},
defaultCommands = {},
defaults = require('./config/config');
let c = {};
const commands = {};
const defaultCommands = {};
const active = {};
// Need it here as it would be used below
@ -62,39 +62,21 @@ module.exports = () => {
* @param {Object} config Configurations
* @private
*/
init(config) {
c = config || {};
for (var name in defaults) {
if (!(name in c)) c[name] = defaults[name];
}
init(config = {}) {
c = {
...defaults,
...config
};
em = c.em;
var ppfx = c.pStylePrefix;
const ppfx = c.pStylePrefix;
if (ppfx) c.stylePrefix = ppfx + c.stylePrefix;
// Load commands passed via configuration
for (var k in c.defaults) {
var obj = c.defaults[k];
for (let k in c.defaults) {
const obj = c.defaults[k];
if (obj.id) this.add(obj.id, obj);
}
const ViewCode = require('./view/ExportTemplate');
defaultCommands['select-comp'] = require('./view/SelectComponent');
defaultCommands['create-comp'] = require('./view/CreateComponent');
defaultCommands['delete-comp'] = require('./view/DeleteComponent');
defaultCommands['move-comp'] = require('./view/MoveComponent');
defaultCommands['export-template'] = ViewCode;
defaultCommands['sw-visibility'] = require('./view/SwitchVisibility');
defaultCommands['open-layers'] = require('./view/OpenLayers');
defaultCommands['open-sm'] = require('./view/OpenStyleManager');
defaultCommands['open-tm'] = require('./view/OpenTraitManager');
defaultCommands['open-blocks'] = require('./view/OpenBlocks');
defaultCommands['open-assets'] = require('./view/OpenAssets');
defaultCommands['show-offset'] = require('./view/ShowOffset');
defaultCommands['select-parent'] = require('./view/SelectParent');
defaultCommands.fullscreen = require('./view/Fullscreen');
defaultCommands.preview = require('./view/Preview');
defaultCommands.resize = require('./view/Resize');
defaultCommands['tlb-delete'] = {
run(ed) {
return ed.runCommand('core:component-delete');
@ -170,10 +152,23 @@ module.exports = () => {
defaultCommands['core:undo'] = e => e.UndoManager.undo();
defaultCommands['core:redo'] = e => e.UndoManager.redo();
[
['preview', 'Preview', 'preview'],
['resize', 'Resize', 'resize'],
['fullscreen', 'Fullscreen', 'fullscreen'],
['copy', 'CopyComponent'],
['paste', 'PasteComponent'],
['canvas-move', 'CanvasMove'],
['canvas-clear', 'CanvasClear'],
['open-code', 'ExportTemplate', 'export-template'],
['open-layers', 'OpenLayers', 'open-layers'],
['open-styles', 'OpenStyleManager', 'open-sm'],
['open-traits', 'OpenTraitManager', 'open-tm'],
['open-blocks', 'OpenBlocks', 'open-blocks'],
['open-assets', 'OpenAssets', 'open-assets'],
['component-select', 'SelectComponent', 'select-comp'],
['component-outline', 'SwitchVisibility', 'sw-visibility'],
['component-offset', 'ShowOffset', 'show-offset'],
['component-move', 'MoveComponent', 'move-comp'],
['component-next', 'ComponentNext'],
['component-prev', 'ComponentPrev'],
['component-enter', 'ComponentEnter'],
@ -181,10 +176,13 @@ module.exports = () => {
['component-delete', 'ComponentDelete'],
['component-style-clear', 'ComponentStyleClear'],
['component-drag', 'ComponentDrag']
].forEach(
item =>
(defaultCommands[`core:${item[0]}`] = require(`./view/${item[1]}`))
);
].forEach(item => {
const oldCmd = item[2];
const cmd = require(`./view/${item[1]}`);
const cmdName = `core:${item[0]}`;
defaultCommands[cmdName] = cmd;
if (oldCmd) defaultCommands[oldCmd] = cmd;
});
if (c.em) c.model = c.em.get('Canvas');

8
src/commands/view/ComponentExit.js

@ -4,7 +4,13 @@ module.exports = {
const toSelect = [];
ed.getSelectedAll().forEach(component => {
const next = component.parent();
let next = component.parent();
// Recurse through the parent() chain until a selectable parent is found
while (next && !next.get('selectable')) {
next = next.parent();
}
next && toSelect.push(next);
});

232
src/commands/view/CreateComponent.js

@ -1,232 +0,0 @@
import _ from 'underscore';
import Backbone from 'backbone';
const SelectPosition = require('./SelectPosition');
const $ = Backbone.$;
module.exports = _.extend({}, SelectPosition, {
init(opt) {
_.bindAll(this, 'startDraw', 'draw', 'endDraw', 'rollback');
this.config = opt || {};
this.hType = this.config.newFixedH ? 'height' : 'min-height';
this.allowDraw = 1;
},
/**
* Start with enabling to select position and listening to start drawing
* @private
* */
enable(...args) {
SelectPosition.enable.apply(this, args);
this.$wr.css('cursor', 'crosshair');
if (this.allowDraw) this.$wr.on('mousedown', this.startDraw);
this.ghost = this.canvas.getGhostEl();
},
/**
* Start drawing component
* @param {Object} e Event
* @private
* */
startDraw(e) {
e.preventDefault();
this.stopSelectPosition();
this.ghost.style.display = 'block';
this.frameOff = this.getOffsetDim();
this.startPos = {
top: e.pageY + this.frameOff.top,
left: e.pageX + this.frameOff.left
};
this.isDragged = false;
this.tempComponent = { style: {} };
this.beforeDraw(this.tempComponent);
this.updateSize(this.startPos.top, this.startPos.left, 0, 0);
this.toggleEvents(1);
},
/**
* Enable/Disable events
* @param {Boolean} enable
*/
toggleEvents(enable) {
var method = enable ? 'on' : 'off';
this.$wr[method]('mousemove', this.draw);
this.$wr[method]('mouseup', this.endDraw);
this.$canvas[method]('mousemove', this.draw);
$(document)[method]('mouseup', this.endDraw);
$(document)[method]('keypress', this.rollback);
},
/**
* While drawing the component
* @param {Object} e Event
* @private
* */
draw(e) {
this.isDragged = true;
this.updateComponentSize(e);
},
/**
* End drawing component
* @param {Object} e Event
* @private
* */
endDraw(e) {
this.toggleEvents();
var model = {};
// Only if the mouse was moved
if (this.isDragged) {
this.updateComponentSize(e);
this.setRequirements(this.tempComponent);
var lp = this.sorter.lastPos;
model = this.create(
this.sorter.target,
this.tempComponent,
lp.index,
lp.method
);
this.sorter.prevTarget = null;
}
this.ghost.style.display = 'none';
this.startSelectPosition();
this.afterDraw(model);
},
/**
* Create new component inside the target
* @param {Object} target Tha target collection
* @param {Object} component New component to create
* @param {number} index Index inside the collection, 0 if no children inside
* @param {string} method Before or after of the children
* @param {Object} opts Options
*/
create(target, component, index, method, opts) {
index = method === 'after' ? index + 1 : index;
var opt = opts || {};
var $trg = $(target);
var trgModel = $trg.data('model');
var trgCollection = $trg.data('collection');
var droppable = trgModel ? trgModel.get('droppable') : 1;
opt.at = index;
if (trgCollection && droppable) return trgCollection.add(component, opt);
else console.warn('Invalid target position');
},
/**
* Check and set basic requirements for the component
* @param {Object} component New component to be created
* @return {Object} Component updated
* @private
* */
setRequirements(component) {
var c = this.config;
var compStl = component.style;
// Check min width
if (compStl.width.replace(/\D/g, '') < c.minComponentW)
compStl.width = c.minComponentW + 'px';
// Check min height
if (compStl[this.hType].replace(/\D/g, '') < c.minComponentH)
compStl[this.hType] = c.minComponentH + 'px';
// Set overflow in case of fixed height
if (c.newFixedH) compStl.overflow = 'auto';
if (!this.absoluteMode) {
delete compStl.left;
delete compStl.top;
} else compStl.position = 'absolute';
var lp = this.sorter.lastPos;
if (this.nearFloat(lp.index, lp.method, this.sorter.lastDims))
compStl.float = 'left';
if (
this.config.firstCentered &&
this.getCanvasWrapper() == this.sorter.target
) {
compStl.margin = '0 auto';
}
return component;
},
/**
* Update new component size while drawing
* @param {Object} e Event
* @private
* */
updateComponentSize(e) {
var y = e.pageY + this.frameOff.top;
var x = e.pageX + this.frameOff.left;
var start = this.startPos;
var top = start.top;
var left = start.left;
var height = y - top;
var width = x - left;
if (x < left) {
left = x;
width = start.left - x;
}
if (y < top) {
top = y;
height = start.top - y;
}
this.updateSize(top, left, width, height);
},
/**
* Update size
* @private
*/
updateSize(top, left, width, height) {
var u = 'px';
var ghStl = this.ghost.style;
var compStl = this.tempComponent.style;
ghStl.top = compStl.top = top + u;
ghStl.left = compStl.left = left + u;
ghStl.width = compStl.width = width + u;
ghStl[this.hType] = compStl[this.hType] = height + u;
},
/**
* Used to bring the previous situation before event started
* @param {Object} e Event
* @param {Boolean} forse Indicates if rollback in anycase
* @private
* */
rollback(e, force) {
var key = e.which || e.keyCode;
if (key == this.config.ESCAPE_KEY || force) {
this.isDragged = false;
this.endDraw();
}
return;
},
/**
* This event is triggered at the beginning of a draw operation
* @param {Object} component Object component before creation
* @private
* */
beforeDraw(component) {
component.editable = false; //set this component editable
},
/**
* This event is triggered at the end of a draw operation
* @param {Object} model Component model created
* @private
* */
afterDraw(model) {},
run(editor, sender, opts) {
this.editor = editor;
this.sender = sender;
this.$wr = this.$wrapper;
this.enable();
},
stop() {
this.stopSelectPosition();
this.$wrapper.css('cursor', '');
this.$wrapper.unbind();
}
});

2
src/commands/view/MoveComponent.js

@ -129,7 +129,7 @@ module.exports = _.extend({}, SelectPosition, SelectComponent, {
* */
rollback(e, force) {
var key = e.which || e.keyCode;
if (key == this.opt.ESCAPE_KEY || force) {
if (key == 27 || force) {
this.sorter.moved = false;
this.sorter.endMove();
}

13
src/commands/view/SelectParent.js

@ -1,13 +0,0 @@
module.exports = {
run(editor) {
const sel = editor.getSelected();
let comp = sel && sel.parent();
// Recurse through the parent() chain until a selectable parent is found
while (comp && !comp.get('selectable')) {
comp = comp.parent();
}
comp && editor.select(comp);
}
};

2
src/dom_components/model/Component.js

@ -611,7 +611,7 @@ const Component = Backbone.Model.extend(Styleable).extend(
if (model.collection) {
tb.push({
attributes: { class: 'fa fa-arrow-up' },
command: 'select-parent'
command: 'core:component-exit'
});
}
if (model.get('draggable')) {

2
src/panels/config/config.js

@ -1,5 +1,3 @@
var crc = 'create-comp';
var mvc = 'move-comp';
var swv = 'sw-visibility';
var expt = 'export-template';
var osm = 'open-sm';

Loading…
Cancel
Save