From 1b3790bc91f704e15a0837038afa30b05d9a187f Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Tue, 16 Mar 2021 00:06:13 +0100 Subject: [PATCH] Update undo manager for components --- src/canvas/model/Frame.js | 19 ++++---- src/dom_components/index.js | 64 -------------------------- src/dom_components/model/Component.js | 32 ++++++++++++- src/dom_components/model/Components.js | 2 +- src/editor/model/Editor.js | 4 ++ src/undo_manager/index.js | 10 ++-- 6 files changed, 52 insertions(+), 79 deletions(-) diff --git a/src/canvas/model/Frame.js b/src/canvas/model/Frame.js index e54458b9e..4dfcbeb2f 100644 --- a/src/canvas/model/Frame.js +++ b/src/canvas/model/Frame.js @@ -1,5 +1,5 @@ import { Model } from 'backbone'; -import { result, forEach, isEmpty } from 'underscore'; +import { result, forEach, isEmpty, debounce } from 'underscore'; import { isComponent, isObject } from 'utils/mixins'; const keyAutoW = '__aw'; @@ -9,6 +9,7 @@ export default Model.extend({ defaults: () => ({ x: 0, y: 0, + changesCount: 0, attributes: {}, width: null, height: null, @@ -44,16 +45,15 @@ export default Model.extend({ !props.width && this.set(keyAutoW, 1); !props.height && this.set(keyAutoH, 1); - // this._initUm(); + this.getComponent()._initUm(); }, - _initUm() { - const um = this.em.get('UndoManager'); - const component = this.getComponent(); - if (!um) return; - um.add(component); - console.log({ component, um }); - }, + changesUp: debounce(function(opt = {}) { + if (opt.temporary || opt.noCount || opt.avoidStore) { + return; + } + this.set('changesCount', this.get('changesCount') + 1); + }), getComponent() { return this.get('component'); @@ -146,6 +146,7 @@ export default Model.extend({ const defaults = result(this, 'defaults'); delete obj.styles; + delete obj.changesCount; obj[keyAutoW] && delete obj.width; obj[keyAutoH] && delete obj.height; diff --git a/src/dom_components/index.js b/src/dom_components/index.js index 77803cc1e..1db5f67ed 100644 --- a/src/dom_components/index.js +++ b/src/dom_components/index.js @@ -304,70 +304,6 @@ export default () => { c.components && this.setComponents(c.components); }, - /** - * Do stuff after load - * @param {Editor} em - * @private - */ - postLoad(em) { - this.handleChanges(this.getWrapper(), null, { avoidStore: 1 }); - }, - - /** - * Handle component changes - * @private - */ - handleChanges(model, value, opts = {}) { - const comps = model.components(); - const um = em.get('UndoManager'); - const handleUpdates = em.handleUpdates.bind(em); - const handleChanges = this.handleChanges.bind(this); - const handleChangesColl = this.handleChangesColl.bind(this); - const handleRemoves = this.handleRemoves.bind(this); - um && um.add(model); - um && comps && um.add(comps); - const evn = 'change:style change:content change:attributes change:src'; - - [ - [model, evn, handleUpdates], - [model, 'change:components', handleChangesColl], - [comps, 'add', handleChanges], - [comps, 'remove reset', handleRemoves], - [model.get('classes'), 'add remove', handleUpdates] - ].forEach(els => { - em.stopListening(els[0], els[1], els[2]); - em.listenTo(els[0], els[1], els[2]); - }); - - !opts.avoidStore && handleUpdates('', '', opts); - comps.each(model => this.handleChanges(model, value, opts)); - }, - - handleChangesColl(model, coll) { - const um = em.get('UndoManager'); - if (um && coll instanceof Backbone.Collection) { - const handleChanges = this.handleChanges.bind(this); - const handleRemoves = this.handleRemoves.bind(this); - um.add(coll); - [ - [coll, 'add', handleChanges], - [coll, 'remove reset', handleRemoves] - ].forEach(els => { - em.stopListening(els[0], els[1], els[2]); - em.listenTo(els[0], els[1], els[2]); - }); - } - }, - - /** - * Triggered when some component is removed - * @private - * */ - handleRemoves(m, value, opt) { - const opts = opt || value; // in case of reset - em.handleUpdates(m, value, opts); - }, - /** * Load components from the passed object, if the object is empty will try to fetch them * autonomously from the selected storage diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index c09a2b0d0..3721e6bb9 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -122,7 +122,9 @@ const Component = Backbone.Model.extend(Styleable).extend( traits: ['id', 'title'], propagate: '', dmode: '', - toolbar: null + toolbar: null, + _undo: true, + _undoexc: ['status', 'open'] }, /** @@ -193,6 +195,7 @@ const Component = Backbone.Model.extend(Styleable).extend( this.listenTo(this, 'change:attributes', this.attrUpdated); this.listenTo(this, 'change:attributes:id', this._idUpdated); this.on('change:toolbar', this.__emitUpdateTlb); + this.on('change', this.__onChange); this.set('status', ''); this.views = []; @@ -211,6 +214,27 @@ const Component = Backbone.Model.extend(Styleable).extend( } }, + _initUm() { + const um = this.em.get('UndoManager'); + if (!um) return; + um.add(this); + um.add(this.components()); + }, + + __onChange(m, opts) { + const changed = this.changedAttributes(); + ['status', 'open', 'toolbar', 'traits'].forEach( + name => delete changed[name] + ); + // Propagate component prop changes + !isEmptyObj(changed) && this.__changesUp(opts); + }, + + __changesUp(opts) { + const { em, frame } = this; + [frame, em].forEach(md => md && md.changesUp(opts)); + }, + __emitUpdateTlb() { this.emitUpdate('toolbar'); }, @@ -720,10 +744,12 @@ const Component = Backbone.Model.extend(Styleable).extend( // This will propagate the change up to __upSymbProps child.set('classes', this.get('classes'), { fromInstance: this }); }); + this.__changesUp(opts); }, __upSymbComps(m, c, o) { - const { fromInstance } = o || c || {}; + const optUp = o || c || {}; + const { fromInstance } = optUp; const toUpOpts = { fromInstance }; const isTemp = m.opt.temporary; @@ -791,6 +817,8 @@ const Component = Backbone.Model.extend(Styleable).extend( }); } } + + this.__changesUp(optUp); }, initClasses(m, c, opts = {}) { diff --git a/src/dom_components/model/Components.js b/src/dom_components/model/Components.js index af530b5aa..498263631 100644 --- a/src/dom_components/model/Components.js +++ b/src/dom_components/model/Components.js @@ -252,7 +252,7 @@ export default Backbone.Collection.extend({ model.setStyle({}); model.addClass(name); } - + model._initUm(); this.__onAddEnd(); }, diff --git a/src/editor/model/Editor.js b/src/editor/model/Editor.js index 645da5bf4..197635f6a 100644 --- a/src/editor/model/Editor.js +++ b/src/editor/model/Editor.js @@ -271,6 +271,10 @@ export default Backbone.Model.extend({ }, 0); }, + changesUp(opts) { + this.handleUpdates(0, 0, opts); + }, + /** * Callback on component hover * @param {Object} Model diff --git a/src/undo_manager/index.js b/src/undo_manager/index.js index 8cd1c7994..7d8f72b13 100644 --- a/src/undo_manager/index.js +++ b/src/undo_manager/index.js @@ -36,6 +36,7 @@ export default () => { maximumStackLength: 500 }; const hasSkip = opts => opts.avoidStore || opts.noUndo; + const getChanged = obj => Object.keys(obj.changedAttributes()); return { name: 'UndoManager', @@ -54,14 +55,17 @@ export default () => { condition: object => { const hasUndo = object.get('_undo'); if (hasUndo) { + const undoExc = object.get('_undoexc'); + if (isArray(undoExc)) { + if (getChanged(object).some(chn => undoExc.indexOf(chn) >= 0)) + return false; + } if (isBoolean(hasUndo)) return true; if (isArray(hasUndo)) { - const changed = Object.keys(object.changedAttributes()); - if (changed.some(chn => hasUndo.indexOf(hasUndo) >= 0)) + if (getChanged(object).some(chn => hasUndo.indexOf(chn) >= 0)) return true; } } - // console.log('UndoType change', object.attributes, object.changedAttributes()); return false; }, on(object, v, opt = {}) {