From e468a172b9c0b8eadc3c0f817965efdc6c5d8ffc Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 7 Apr 2019 14:20:33 +0200 Subject: [PATCH] Add textable in Sorter --- src/utils/Sorter.js | 156 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 127 insertions(+), 29 deletions(-) diff --git a/src/utils/Sorter.js b/src/utils/Sorter.js index 12362a946..db445f2e5 100644 --- a/src/utils/Sorter.js +++ b/src/utils/Sorter.js @@ -1,6 +1,6 @@ import Backbone from 'backbone'; import { isString, isFunction, isArray, result } from 'underscore'; -import { on, off, matches, getElement } from 'utils/mixins'; +import { on, off, matches, getElement, getPointerEvent } from 'utils/mixins'; const $ = Backbone.$; module.exports = Backbone.View.extend({ @@ -51,6 +51,7 @@ module.exports = Backbone.View.extend({ this.canvasRelative = o.canvasRelative || 0; this.selectOnEnd = !o.avoidSelectOnEnd; this.scale = o.scale; + this.activeTextModel = null; if (this.em && this.em.on) { this.em.on('change:canvasOffset', this.udpateOffset); @@ -96,6 +97,39 @@ module.exports = Backbone.View.extend({ this.dropContent = content; }, + updateTextViewCursorPosition(e) { + const { em } = this; + const Canvas = em.get('Canvas'); + const targetDoc = Canvas.getDocument(); + const poiner = getPointerEvent(e); + let range = null; + + if (targetDoc.caretRangeFromPoint) { + // Chrome + range = targetDoc.caretRangeFromPoint(poiner.clientX, poiner.clientY); + } else if (e.rangeParent) { + // Firefox + range = targetDoc.createRange(); + range.setStart(e.rangeParent, e.rangeOffset); + } + + const sel = Canvas.getWindow().getSelection(); + Canvas.getFrameEl().focus(); + sel.removeAllRanges(); + sel.addRange(range); + }, + + disableEditingOn(model) { + model && model.getView().disableEditing(); + }, + + setContentEditable(model, mode) { + if (model) { + const el = model.getEl(); + if (el.contentEditable != mode) el.contentEditable = mode; + } + }, + /** * Toggle cursor while sorting * @param {Boolean} active @@ -359,7 +393,8 @@ module.exports = Backbone.View.extend({ * @param {Event} e * */ onMove(e) { - const em = this.em; + const ev = e; + const { em, onMoveClb, activeTextModel } = this; this.moved = 1; // Turn placeholder visibile @@ -385,6 +420,7 @@ module.exports = Backbone.View.extend({ this.eventMove = e; //var targetNew = this.getTargetFromEl(e.target); + const sourceModel = this.getSourceModel(); const dims = this.dimsFromTarget(e.target, rX, rY); const target = this.target; const targetModel = this.getTargetModel(target); @@ -392,26 +428,39 @@ module.exports = Backbone.View.extend({ if (!targetModel) plh.style.display = 'none'; this.lastDims = dims; - var pos = this.findPosition(dims, rX, rY); - // If there is a significant changes with the pointer - if ( - !this.lastPos || - (this.lastPos.index != pos.index || this.lastPos.method != pos.method) - ) { - this.movePlaceholder(this.plh, dims, pos, this.prevTargetDim); - if (!this.$plh) this.$plh = $(this.plh); - - // With canvasRelative the offset is calculated automatically for - // each element - if (!this.canvasRelative) { - if (this.offTop) this.$plh.css('top', '+=' + this.offTop + 'px'); - if (this.offLeft) this.$plh.css('left', '+=' + this.offLeft + 'px'); - } + const pos = this.findPosition(dims, rX, rY); + + if (this.isTextableActive(sourceModel, targetModel)) { + this.activeTextModel = targetModel; + this.setContentEditable(targetModel, true); + plh.style.display = 'none'; this.lastPos = pos; + this.updateTextViewCursorPosition(ev); + } else { + this.disableTextable(); + this.activeTextModel = null; + + // If there is a significant changes with the pointer + if ( + !this.lastPos || + (this.lastPos.index != pos.index || this.lastPos.method != pos.method) + ) { + this.movePlaceholder(this.plh, dims, pos, this.prevTargetDim); + if (!this.$plh) this.$plh = $(this.plh); + + // With canvasRelative the offset is calculated automatically for + // each element + if (!this.canvasRelative) { + if (this.offTop) this.$plh.css('top', '+=' + this.offTop + 'px'); + if (this.offLeft) this.$plh.css('left', '+=' + this.offLeft + 'px'); + } + + this.lastPos = pos; + } } - if (typeof this.onMoveClb === 'function') this.onMoveClb(e); + isFunction(onMoveClb) && onMoveClb(e); em && em.trigger('sorter:drag', { @@ -424,6 +473,15 @@ module.exports = Backbone.View.extend({ }); }, + isTextableActive(src, trg) { + return src && src.get('textable') && trg && trg.is('text'); + }, + + disableTextable() { + const { activeTextModel } = this; + activeTextModel && this.setContentEditable(activeTextModel, false); + }, + /** * Returns true if the elements is in flow, so is not in flow where * for example the component is with float:left @@ -512,20 +570,35 @@ module.exports = Backbone.View.extend({ return result; } + // 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; + // Check if the target could accept the source let droppable = trgModel.get('droppable'); droppable = droppable instanceof Backbone.Collection ? 1 : droppable; droppable = droppable instanceof Array ? droppable.join(', ') : droppable; result.dropInfo = droppable; droppable = isString(droppable) ? this.matches(src, droppable) : droppable; + droppable = + trgModel.is('text') && draggable && srcModel.get('textable') + ? 1 + : droppable; result.droppable = droppable; - // 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; + console.log( + `trgModel.is('text')`, + trgModel.is('text'), + `srcModel.get('textable')`, + srcModel.get('textable'), + 'droppable', + droppable, + 'draggable', + draggable + ); if (!droppable || !draggable) { result.valid = false; @@ -953,6 +1026,7 @@ module.exports = Backbone.View.extend({ this.dragHelper = null; } + this.disableTextable(); this.selectTargetModel(); this.toggleSortCursor(); @@ -967,7 +1041,7 @@ module.exports = Backbone.View.extend({ * @param {Object} pos Object with position coordinates * */ move(dst, src, pos) { - var em = this.em; + const { em, activeTextModel } = this; const srcEl = getElement(src); em && em.trigger('component:dragEnd:before', dst, srcEl, pos); // @depricated var warns = []; @@ -981,8 +1055,21 @@ module.exports = Backbone.View.extend({ var dropInfo = validResult.dropInfo; var dragInfo = validResult.dragInfo; var dropContent = this.dropContent; - droppable = - validResult.trgModel instanceof Backbone.Collection ? 1 : droppable; + const { trgModel } = validResult; + droppable = trgModel instanceof Backbone.Collection ? 1 : droppable; + const modelIsTextable = model.get('textable'); + const targetIsTextView = trgModel && trgModel.is('text'); + + console.log( + 'droppable', + droppable, + 'draggable', + draggable, + 'modelIsTextable', + modelIsTextable, + 'targetIsTextView', + targetIsTextView + ); if (targetCollection && droppable && draggable) { index = pos.method === 'after' ? index + 1 : index; @@ -993,7 +1080,7 @@ module.exports = Backbone.View.extend({ opts.temporary = 1; modelTemp = targetCollection.add({}, { ...opts }); - if (model) { + if (model.collection) { modelToDrop = model.collection.remove(model, { temporary: 1 }); } } else { @@ -1002,7 +1089,14 @@ module.exports = Backbone.View.extend({ opts.avoidUpdateStyle = 1; } - created = targetCollection.add(modelToDrop, opts); + if (targetIsTextView && modelIsTextable) { + const viewActive = activeTextModel.getView(); + activeTextModel.trigger('active'); + const { activeRte } = viewActive; + activeRte.insertHTML && activeRte.insertHTML(model.toHTML()); + } else { + created = targetCollection.add(modelToDrop, opts); + } if (!dropContent) { targetCollection.remove(modelTemp); @@ -1010,6 +1104,10 @@ module.exports = Backbone.View.extend({ this.dropContent = null; } + if (targetIsTextView && modelIsTextable) { + this.disableEditingOn(activeTextModel); // TODO disableEditingOn is used only here + } + // This will cause to recalculate children dimensions this.prevTarget = null; } else {