From f55b416cc86697cfdf8426bb6278ca344dec24fc Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sat, 13 Feb 2021 15:43:48 +0100 Subject: [PATCH] Added support for nestes symbols --- src/dom_components/model/Component.js | 59 ++++++++++++++++------ test/specs/dom_components/model/Symbols.js | 52 +++++++++++++++---- 2 files changed, 86 insertions(+), 25 deletions(-) diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index 81e30153e..5f263908b 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -622,9 +622,7 @@ const Component = Backbone.Model.extend(Styleable).extend( __isSymbolTop() { const parent = this.parent(); return ( - parent && - ((this.__isSymbol() && !parent.__isSymbol()) || - (this.__getSymbol() && !parent.__getSymbol())) + !parent || (parent && !parent.__isSymbol() && !parent.__getSymbol()) ); }, @@ -653,14 +651,10 @@ const Component = Backbone.Model.extend(Styleable).extend( }, __getSymbTop(opts) { - const isSymbol = this.__isSymbol(); let result = this; let parent = this.parent(opts); - while ( - parent && - (isSymbol ? parent.__isSymbol() : parent.__getSymbol()) - ) { + while (parent && (parent.__isSymbol() || parent.__getSymbol())) { result = parent; parent = parent.parent(opts); } @@ -710,16 +704,23 @@ const Component = Backbone.Model.extend(Styleable).extend( }); } else if (o.add) { // Add - const addedInstances = m.__getSymbToUp(toUpOpts); + let addedInstances = []; const isMainSymb = !!this.__getSymbols(); const toUp = this.__getSymbToUp(toUpOpts); + if (toUp.length) { + const addSymb = m.__getSymbol(); + addedInstances = + (addSymb ? addSymb.__getSymbols() : m.__getSymbols()) || []; + addedInstances = [...addedInstances]; + addedInstances.push(addSymb ? addSymb : m); + } !isTemp && this.__logSymbol('add', toUp, { opts: o, - addedInstances, + addedInstances: addedInstances.map(c => c.cid), added: m.cid }); - // Here, before appending new symbol, I have to ensure there are no previosly + // Here, before appending a new symbol, I have to ensure there are no previosly // created symbols (eg. used mainly when drag components around) toUp.forEach(symb => { const symbTop = symb.__getSymbTop(); @@ -729,15 +730,33 @@ const Component = Backbone.Model.extend(Styleable).extend( })[0]; const toAppend = symbPrev || m.clone({ symbol: 1, symbolInv: isMainSymb }); - symb.append(toAppend, { fromInstance: this, toAppend: m, ...o }); + symb.append(toAppend, { fromInstance: this, ...o }); }); } else { + // Allow removing single instances if (!m.__isSymbolTop()) { - // Allow removing single instances const toUp = m.__getSymbToUp(toUpOpts); !isTemp && this.__logSymbol('remove', toUp, { opts: o, removed: m.cid }); - toUp.forEach(symb => symb.remove({ fromInstance: m, ...o })); + toUp.forEach(symb => { + const opts = { fromInstance: m, ...o }; + // In case of nested symbols, I only need to propagate changes to its instances + if (symb.__isSymbolTop() && symb.__getSymbols()) { + const toUpInst = symb.__getSymbToUp({ + fromInstance: m, + ...toUpOpts + }); + this.__logSymbol('remove-inst', toUpInst, { + opts: o, + symbol: symb + }); + toUpInst.forEach(inst => { + inst.remove(opts); + }); + } else { + symb.remove(opts); + } + }); } } }, @@ -836,7 +855,17 @@ const Component = Backbone.Model.extend(Styleable).extend( * someComponent.append(otherComponent, { at: 0 }); */ append(components, opts = {}) { - const result = this.components().add(components, opts); + const compArr = isArray(components) ? components : [components]; + const toAppend = compArr.map(comp => { + if (isString(comp)) { + return comp; + } else { + // I have to remove components from the old container before adding them to a new one + comp.collection && comp.collection.remove(comp, { temporary: 1 }); + return comp; + } + }); + const result = this.components().add(toAppend, opts); return isArray(result) ? result : [result]; }, diff --git a/test/specs/dom_components/model/Symbols.js b/test/specs/dom_components/model/Symbols.js index aa303f6e9..626ed47c2 100644 --- a/test/specs/dom_components/model/Symbols.js +++ b/test/specs/dom_components/model/Symbols.js @@ -15,7 +15,7 @@ describe('Symbols', () => { return cloned; }; const simpleComp = '
Component
'; - const simpleComp2 = '
Component 2
'; + const simpleComp2 = '
Component 3
'; const compMultipleNodes = `
Component 1
Component 2
@@ -176,20 +176,18 @@ describe('Symbols', () => { test('Moving a new added component in the instance, will propagate the action in all symbols', () => { const added = comp.append(simpleComp)[0]; + expect(added.index()).toBe(compInitChild); const newChildLen = compInitChild + 1; added.move(comp, { at: 0 }); + expect(added.index()).toBe(0); // extra checks + expect(added.parent()).toBe(comp); const symbRef = added.__getSymbol(); // All symbols still have the same amount of components all.forEach(cmp => expect(cmp.components().length).toBe(newChildLen)); // All instances refer to the same symbol allInst.forEach(cmp => expect(getFirstInnSymbol(cmp)).toBe(symbRef)); // The moved symbol contains all its instances - expect( - symbol - .components() - .at(0) - .__getSymbols().length - ).toBe(allInst.length); + expect(getInnerComp(symbol).__getSymbols().length).toBe(allInst.length); }); test('Moving a new added component in the symbol, will propagate the action in all instances', () => { @@ -309,14 +307,48 @@ describe('Symbols', () => { expect(secComp.toHTML()).toBe(secSymbol.toHTML()); }); - test('Moving the instance, of the second symbol, inside the first symbol, propagates correctly to all first instances', () => { + test('Adding the instance, of the second symbol, inside the first symbol, propagates correctly to all first instances', () => { const added = symbol.append(secComp)[0]; // The added component is still the second instance expect(added).toBe(secComp); // The added component still has the reference to the second symbol expect(added.__getSymbol()).toBe(secSymbol); - // The added component is propogated to other instances (of the first symbol) - expect(added.__getSymbols().length).toBe(allInst.length); + // The main second symbol now has the reference to all its instances + const secInstans = secSymbol.__getSymbols(); + expect(secInstans.length).toBe(all.length); + // All instances still refer to the second symbol + secInstans.forEach(secInst => + expect(secInst.__getSymbol()).toBe(secSymbol) + ); + }); + + test('Adding the instance, of the second symbol, inside one of the first instances, propagates correctly to all first symbols', () => { + const added = comp.append(secComp)[0]; + // The added component is still the second instance + expect(added).toBe(secComp); + // The added component still has the reference to the second symbol + expect(added.__getSymbol()).toBe(secSymbol); + // The main second symbol now has the reference to all its instances + const secInstans = secSymbol.__getSymbols(); + expect(secInstans.length).toBe(all.length); + // All instances still refer to the second symbol + secInstans.forEach(secInst => + expect(secInst.__getSymbol()).toBe(secSymbol) + ); + }); + + test('Moving the second instance inside first instances, propagates correctly to all other first symbols', () => { + const added = comp.append(secComp)[0]; + expect(added.parent()).toBe(comp); // extra checks + expect(added.index()).toBe(compInitChild); + const secInstansArr = secSymbol.__getSymbols().map(i => i.cid); + expect(secInstansArr.length).toBe(all.length); + added.move(comp, { at: 0 }); + // After the move, the symbol still have the same references + const secInstansArr2 = secSymbol.__getSymbols().map(i => i.cid); + expect(secInstansArr2).toEqual(secInstansArr); + // All second instances refer to the same second symbol + all.forEach(c => expect(getFirstInnSymbol(c)).toBe(secSymbol)); }); }); });