From d0f6a7c366f11ad241aca40bf869a5ea3e05d0ad Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Wed, 29 Dec 2021 13:41:12 +0100 Subject: [PATCH] Update style sectors on target change --- docs/modules/Style-manager.md | 21 ++++- src/style_manager/index.js | 123 +++++++++++++++----------- src/style_manager/view/SectorsView.js | 15 ++-- 3 files changed, 98 insertions(+), 61 deletions(-) diff --git a/docs/modules/Style-manager.md b/docs/modules/Style-manager.md index 85251799a..feac9fea9 100644 --- a/docs/modules/Style-manager.md +++ b/docs/modules/Style-manager.md @@ -434,8 +434,25 @@ For a more advanced usage you can rely on the [Style Manager API] to perform dif sm.removeSector('sector-id'); ``` -* Change target -* Get current selected target +* Managing the selected target, + ```js + // Select the first button in the current page + const btnCmp = editor.Pages.getSelected().getMainComponent().findType('button')[0]; + btnCmp && sm.select(btnCmp); + + // Add new property to the sector + sm.addProperty('sector-id', { + type: 'number', + property: 'min-width', + }); + + // Remove sector + sm.removeSector('sector-id'); + ``` + + + + ## Customization diff --git a/src/style_manager/index.js b/src/style_manager/index.js index 445aaab69..8ce886e6c 100644 --- a/src/style_manager/index.js +++ b/src/style_manager/index.js @@ -16,6 +16,7 @@ * ``` * ## Available Events * * `style:sector:add` - Sector added. The [Sector] is passed as an argument to the callback. + * * `style:target` - Target selection changed. The target (or `null` in case the target is deselected) is passed as an argument to the callback. * * ## Methods * * [getConfig](#getconfig) @@ -27,7 +28,9 @@ * * [getProperty](#getproperty) * * [getProperties](#getproperties) * * [removeProperty](#removeproperty) - * * [getModelToStyle](#getmodeltostyle) + * * [select](#select) + * * [getSelected](#getselected) + * * [getLastSelected](#getlastselected) * * [addType](#addtype) * * [getType](#gettype) * * [getTypes](#gettypes) @@ -55,6 +58,7 @@ export const evAll = 'style'; export const evPfx = `${evAll}:`; export const evPropUp = `${evPfx}property:update`; export const evLayerSelect = `${evPfx}layer:select`; +export const evTarget = `${evPfx}target`; export const evCustom = `${evPfx}custom`; const propDef = value => value || value === 0; @@ -72,6 +76,7 @@ export default () => { all: evAll, propertyUpdate: evPropUp, layerSelect: evLayerSelect, + target: evTarget, custom: evCustom, }, @@ -98,23 +103,27 @@ export default () => { this.builtIn = new PropertyFactory(); properties = new Properties(); sectors = new Sectors([], c); - this.model = new Model({ targets: [] }); + const model = new Model({ targets: [] }); + this.model = model; // Triggers for the selection refresh and properties const ev = 'component:toggled component:update:classes change:state change:device frame:resized selector:type'; const upAll = debounce(() => this.__upSel()); - this.model.listenTo(em, ev, upAll); + model.listenTo(em, ev, upAll); // Triggers only for properties (avoid selection refresh) const upProps = debounce(() => { this.__upProps(); this.__trgCustom(); }); - this.model.listenTo(em, 'styleable:change', upProps); + model.listenTo(em, 'styleable:change', upProps); // Triggers only custom event const trgCustom = debounce(() => this.__trgCustom()); - this.model.listenTo(em, evLayerSelect, trgCustom); + model.listenTo(em, evLayerSelect, trgCustom); + + // Other listeners + model.on('change:lastTarget', () => em.trigger(evTarget, this.getLastSelected())); return this; }, @@ -281,6 +290,58 @@ export default () => { return props ? props.remove(this.getProperty(sectorId, id)) : null; }, + /** + * Select new target. + * The target could be a Component, CSSRule, or a CSS selector string. + * @param {[Component]|[CSSRule]|String} target + * @returns {Array<[Component]|[CSSRule]>} Array containing selected Components or CSSRules + */ + select(target, opts = {}) { + const { em } = this; + const trgs = isArray(target) ? target : [target]; + const { stylable } = opts; + const cssc = em.get('CssComposer'); + let targets = []; + + trgs.filter(Boolean).forEach(target => { + let model = target; + + if (isString(target)) { + const rule = cssc.getRule(target) || cssc.setRule(target); + !isUndefined(stylable) && rule.set({ stylable }); + model = rule; + } + + targets.push(model); + }); + + targets = targets.map(t => this.getModelToStyle(t)); + const lastTarget = targets.slice().reverse()[0]; + const lastTargetParents = this.getParentRules(lastTarget, em.getState()); + this.model.set({ targets, lastTarget, lastTargetParents }); + // lastTarget && this.setTarget(lastTarget); // TODO to refactor + this.__upProps(opts); + + return targets; + }, + + /** + * Get the array of selected targets. + * @returns {Array<[Component]|[CSSRule]>} + */ + getSelected() { + return this.model.get('targets'); + }, + + /** + * Get the last selected target. + * By default, the Style Manager shows styles of the last selected target. + * @returns {[Component]|[CSSRule]|null} + */ + getLastSelected() { + return this.model.get('lastTarget') || null; + }, + /** * Get what to style inside Style Manager. If you select the component * without classes the entity is the Component itself and all changes will @@ -288,6 +349,7 @@ export default () => { * one or more classes, the function will return the corresponding CSS Rule * @param {Model} model * @return {Model} + * @private */ getModelToStyle(model, options = {}) { const { em } = this; @@ -352,10 +414,10 @@ export default () => { // Componente related rule if (cmp) { cmpRules = cssC.getRules(`#${cmp.getId()}`); - otherRules = cssC.getRules(sel.getSelectors().getFullName(optsSel)); + otherRules = sel ? cssC.getRules(sel.getSelectors().getFullName(optsSel)) : []; rules = otherRules.concat(cmpRules); } else { - cmpRules = cssC.getRules(`#${sel.getId()}`); + cmpRules = sel ? cssC.getRules(`#${sel.getId()}`) : []; otherRules = cssC.getRules(target.getSelectors().getFullName(optsSel)); rules = cmpRules.concat(otherRules); } @@ -454,7 +516,7 @@ export default () => { }, setTarget(target, opts) { - return SectView.setTarget(target, opts); + return SectView?.setTarget(target, opts); }, getTargets() { @@ -462,53 +524,10 @@ export default () => { return (propTarget && propTarget.targets) || []; }, - /** - * Select different target for the Style Manager. - * It could be a Component, CSSRule, or a string of any CSS selector - * @param {[Component]|[CSSRule]|String} target - * @return {Array<[Component]|[CSSRule]>} Array containing selected Components or CSSRules - * @private - */ - select(target, opts = {}) { - const { em } = this; - const trgs = isArray(target) ? target : [target]; - const { stylable } = opts; - const cssc = em.get('CssComposer'); - let targets = []; - - trgs.filter(Boolean).forEach(target => { - let model = target; - - if (isString(target)) { - const rule = cssc.getRule(target) || cssc.setRule(target); - !isUndefined(stylable) && rule.set({ stylable }); - model = rule; - } - - targets.push(model); - }); - - targets = targets.map(t => this.getModelToStyle(t)); - const lastTarget = targets.slice().reverse()[0]; - const lastTargetParents = this.getParentRules(lastTarget, em.getState()); - this.model.set({ targets, lastTarget, lastTargetParents }); - this.__upProps(opts); - - return targets; - }, - addStyleTargets(style, opts) { this.getSelected().map(t => t.addStyle(style, opts)); }, - getSelected() { - return this.model.get('targets'); - }, - - getLastSelected() { - return this.model.get('lastTarget') || null; - }, - getSelectedParents() { return this.model.get('lastTargetParents') || []; }, @@ -529,7 +548,9 @@ export default () => { el, collection: sectors, target: em, + em, config, + module: this, }); return SectView.render().el; }, diff --git a/src/style_manager/view/SectorsView.js b/src/style_manager/view/SectorsView.js index 57928d93f..f75f28a4f 100644 --- a/src/style_manager/view/SectorsView.js +++ b/src/style_manager/view/SectorsView.js @@ -13,6 +13,9 @@ export default Backbone.View.extend({ this.ppfx = config.pStylePrefix || ''; this.target = o.target || {}; this.config = config; + const { module, em } = o; + this.module = module; + this.em = em; // The target that will emit events for properties const target = {}; @@ -24,11 +27,9 @@ export default Backbone.View.extend({ body.removeChild(dummy); this.propTarget = target; const coll = this.collection; - const events = - 'component:toggled component:update:classes change:state change:device frame:resized'; this.listenTo(coll, 'add', this.addTo); this.listenTo(coll, 'reset', this.render); - this.listenTo(this.target, events, this.targetUpdated); + this.listenTo(em, module.events.target, this.targetUpdated); }, remove() { @@ -137,9 +138,7 @@ export default Backbone.View.extend({ const rules = em.get('CssComposer').getAll(); if (targetIsClass) { - rule = rules.filter( - rule => rule.get('selectors').getFullString() === target - )[0]; + rule = rules.filter(rule => rule.get('selectors').getFullString() === target)[0]; } if (!rule) { @@ -180,7 +179,7 @@ export default Backbone.View.extend({ properties: model.get('properties'), target, propTarget, - config + config, }).render().el; appendAtIndex(appendTo, rendered, opts.at); @@ -197,5 +196,5 @@ export default Backbone.View.extend({ $el.append(frag); $el.addClass(`${pfx}sectors ${ppfx}one-bg ${ppfx}two-color`); return this; - } + }, });