BlockManager

This commit is contained in:
George Berezhnoy 2017-11-30 10:19:07 +03:00
parent b83bd752f9
commit bf83f41cc2
10 changed files with 2314 additions and 1481 deletions

View file

@ -73,6 +73,7 @@
"XMLHttpRequest": true,
"ActiveXObject": true,
"RegExp": true,
"Node": true
"Node": true,
"Proxy": true
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -17,8 +17,8 @@
</body>
<!--<script src="plugins/paragraph/paragraph.js?v=100"></script>-->
<!--<link rel="stylesheet" href="plugins/paragraph/paragraph.css">-->
<script src="plugins/paragraph/paragraph.js?v=100"></script>
<link rel="stylesheet" href="plugins/paragraph/paragraph.css">
<!--<script src="plugins/header/header.js"></script>-->
<!--<link rel="stylesheet" href="plugins/header/header.css">-->
@ -71,26 +71,25 @@
displayInToolbox : true,
enableLineBreaks : true
}
},
data: {
items: [
{
type : 'paragraph',
data : {
text : 'Привет от CodeX'
}
},
{
type : 'paragraph',
data : {
text : 'Пишите нам на team@ifmo.su'
}
},
]
}
});
console.log(editor);
var items = [
{
type : 'paragraph',
data : {
text : 'Привет от CodeX'
}
},
{
type : 'paragraph',
data : {
text : 'Пишите нам на team@ifmo.su'
}
},
];
// var editor2 = new CodexEditor({
// holderId : 'cdx',
// initialBlock: 'header'

View file

@ -243,6 +243,7 @@ module.exports = class CodexEditor {
return Promise.resolve()
.then(prepareDecorator(this.moduleInstances.ui))
.then(prepareDecorator(this.moduleInstances.Tools))
.then(prepareDecorator(this.moduleInstances.BlockManager))
.catch(function (error) {

63
src/components/block.js Normal file
View file

@ -0,0 +1,63 @@
/**
*
* @class Block
* @classdesc This class describes editor`s block, including block`s HTMLElement, data and tool
*
* @property {Tool} tool current block tool (Paragraph, for example)
* @property {Object} CSS block`s css classes
*
*/
import $ from './dom';
export default class Block {
/**
* @constructor
*
* @param {Object} tool current block plugin`s instance
*/
constructor(tool) {
this.tool = tool;
this.CSS = {
wrapper: 'ce-block',
content: 'ce-block__content'
};
this._html = this._compose();
}
/**
* Make default block wrappers and put tool`s content there
*
* @returns {HTMLDivElement}
* @private
*/
_compose() {
let wrapper = $.make('div', this.CSS.wrapper),
content = $.make('div', this.CSS.content);
content.appendChild(this.tool.html);
wrapper.appendChild(content);
return wrapper;
}
/**
* Get block`s HTML
*
* @returns {HTMLDivElement}
*/
get html() {
return this._html;
}
}

View file

@ -0,0 +1,344 @@
/**
* @class BlockManager
* @classdesc Manage editor`s blocks storage and appearance
*
*
*/
import Block from '../block';
module.exports = class BlockManager {
/**
* Module key name
* @returns {string}
*/
static get name() {
return 'BlockManager';
}
/**
* @constructor
*
* @param {EditorConfig} config
*/
constructor({ config }) {
this.config = config;
this.Editor = null;
this._blocks = null;
this._currentBlcokIndex = -1;
}
/**
* Editor modules setting
*
* @param Editor
*/
set state(Editor) {
this.Editor = Editor;
}
/**
* Should be called after Editor.ui preparation
* Define this._blocks property
*
* @returns {Promise}
*/
prepare() {
return new Promise(resolve => {
let blocks = new Blocks(this.Editor.ui.nodes.redactor);
/**
* We need to use Proxy to overload set/get [] operator.
* So we can use array-like syntax to access blocks
*
* @example
* this._blocks[0] = new Block(...);
*
* block = this._blocks[0];
*
* @todo proxy the enumerate method
*
* @type {Proxy}
* @private
*/
this._blocks = new Proxy(blocks, {
set: Blocks.set,
get: Blocks.get
});
resolve();
});
}
/**
* Insert new block into _blocks
*
* @param {String} toolName plugin name
* @param {Object} data plugin data
*/
insert(toolName, data) {
let toolInstance = this.Editor.Tools.construct(toolName, data),
block = new Block(toolInstance);
this._blocks[++this._currentBlcokIndex] = block;
}
/**
* Get Block instance by html element
*
* @todo get first level block before searching
*
* @param {HTMLElement} element
* @returns {Block}
*/
getBlock(element) {
let nodes = Array.prototype.slice.call(this._blocks.workingArea.children),
index = nodes.indexOf(element);
if (index >= 0) {
return this._blocks[index];
}
}
/**
* Get current Block instance
*/
get currentBlock() {
return this._blocks.workingArea.children[this._currentBlcokIndex];
}
/**
* Get working html element
*/
get currentNode() {
return this._blocks.workingArea.children[this._currentBlcokIndex];
}
/**
* Set _currentBlockIndex to passed block
*
* @param {HTMLElement} element
*/
set currentNode(element) {
let nodes = Array.prototype.slice.call(this._blocks.workingArea.children);
this._currentBlcokIndex = nodes.indexOf(element);
}
/**
* Get array of Block instances
*
* @returns {Array}
*/
get blocks() {
return this._blocks.array;
}
};
/**
* @class Blocks
* @classdesc Class to work with Block instances array
*
* @private
*
* @property {HTMLElement} workingArea editor`s working node
*
*/
class Blocks {
/**
* @constructor
*
* @param {HTMLElement} workingArea editor`s working node
*/
constructor(workingArea) {
this._blocks = [];
this.workingArea = workingArea;
}
/**
* Push back new Block
*
* @param {Block} block
*/
push(block) {
this._blocks.push(block);
this.workingArea.appendChild(block.html);
}
/**
* Insert new Block at passed index
*
* @param {Number} index index to insert Block
* @param {Block} block Block to insert
*/
insert(index, block) {
if (!this.length) {
this.push(block);
return;
}
if (index > this.length) {
return;
}
this._blocks[index] = block;
if (index > 0) {
let previousBlock = this._blocks[index - 1];
previousBlock.html.insertAdjacentElement('afterend', block.html);
} else {
let nextBlock = this._blocks[index + 1];
nextBlock.html.insertAdjacentElement('beforebegin', block.html);
}
}
/**
* Insert Block after passed target
*
* @todo decide if this method is necessary
*
* @param {Block} targetBlock target after wich Block should be inserted
* @param {Block} newBlock Block to insert
*/
insertAfter(targetBlock, newBlock) {
let index = this._blocks.indexOf(targetBlock);
this.insert(index + 1, newBlock);
}
/**
* Get Block by index
*
* @param {Number} index Block index
* @returns {Block}
*/
get(index) {
return this._blocks[index];
}
/**
* Return index of passed Block
*
* @param {Block} block
* @returns {Number}
*/
indexOf(block) {
return this._blocks.indexOf(block);
}
/**
* Get length of Block instances array
*
* @returns {Number}
*/
get length() {
return this._blocks.length;
}
/**
* Get Block instances array
*
* @returns {Array}
*/
get array() {
return this._blocks;
}
/**
* Proxy trap to implement array-like setter
*
* @example
* blocks[0] = new Block(...)
*
* @param {Blocks} instance Blocks instance
* @param {Number|String} index block index
* @param {Block} block Block to set
* @returns {Boolean}
*/
static set(instance, index, block) {
if (isNaN(Number(index))) {
return false;
}
instance.insert(index, block);
return true;
}
/**
* Proxy trap to implement array-like getter
*
* @param {Blocks} instance Blocks instance
* @param {Number|String} index Block index
* @returns {Block|*}
*/
static get(instance, index) {
if (isNaN(Number(index))) {
return instance[index];
}
return instance.get(index);
}
}

View file

@ -66,11 +66,7 @@ module.exports = class Renderer {
let tool = item.type,
data = item.data;
let instance = this.Editor.Tools.construct(tool, data),
index = this.Editor.Content.insertBlock(instance.html);
this.Editor.Tools.add(instance, index);
this.Editor.BlockManager.insert(tool, data);
return Promise.resolve();

View file

@ -15,6 +15,8 @@
*/
/**
* @todo update according to current API
*
* @typedef {Object} Tool
* @property render
* @property save
@ -100,8 +102,6 @@ module.exports = class Tools {
this.toolsAvailable = {};
this.toolsUnavailable = {};
this._list = [];
}
/**
@ -215,6 +215,9 @@ module.exports = class Tools {
*
* @param {String} tool tool name
* @param {Object} data initial data
*
* @todo throw exceptions if tool doesnt exist
*
*/
construct(tool, data) {
@ -227,32 +230,4 @@ module.exports = class Tools {
}
/**
* Insert tool instance for private list
*
* @param {Object} instance tool instance
* @param {Number} index tool index
*/
add(instance, index) {
this._list[index] = instance;
}
/**
* Get tool instance by html element
*
* @param el
* @returns {*}
*/
getByElement(el) {
let index = el.dataset.toolId;
if (!index) return null;
return this._list[index];
}
};