diff --git a/src/selector_manager/config/config.js b/src/selector_manager/config/config.js index 75e0f99c7..6f806b5f6 100644 --- a/src/selector_manager/config/config.js +++ b/src/selector_manager/config/config.js @@ -9,7 +9,7 @@ export default { // Default selectors selectors: [], - // States + // Default states states: [{ name: 'hover' }, { name: 'active' }, { name: 'nth-of-type(2n)' }], // Custom selector name escaping strategy, eg. diff --git a/src/selector_manager/index.js b/src/selector_manager/index.js index 288c2d9cc..26b0aa218 100644 --- a/src/selector_manager/index.js +++ b/src/selector_manager/index.js @@ -61,7 +61,7 @@ * @module SelectorManager */ -import { isString, isElement, isObject, isArray } from 'underscore'; +import { isString, debounce, isObject, isArray, isEmpty } from 'underscore'; import { isComponent, isRule } from 'utils/mixins'; import { Model } from 'common'; import Module from 'common/module'; @@ -119,13 +119,37 @@ export default () => { // Global selectors container this.all = new Selectors(config.selectors); + this.selected = new Selectors([], { em, config }); this.model = new Model({ _undo: true }); this.__initListen(); em.on('change:state', (m, value) => em.trigger('selector:state', value)); + const listenTo = + 'component:toggled component:update:classes styleManager:update change:state'; + this.model.listenTo(em, listenTo, () => this.__update()); return this; }, + __update: debounce(function() { + this.__trgCustom(); + }), + + __trgCustom() { + this.em.trigger(this.events.custom, this.__customData()); + }, + + __customData() { + const common = this.__getCommon(); + return { + sm: this, + common, + states: this.getStates(), + selected: this.getSelected(), + add: prop => this.__addToCommon(prop), // add selector to common selection + remove: '' // remove selector from selection + }; + }, + // postLoad() { // this.__postLoad(); // const { em, model } = this; @@ -136,6 +160,7 @@ export default () => { postRender() { this.__appendTo(); + this.__trgCustom(); }, select(value, opts = {}) { @@ -155,6 +180,10 @@ export default () => { return this; }, + getSelected() { + return this.em.get('StyleManager').getTargets(); + }, + addSelector(name, opts = {}, cOpts = {}) { let props = { ...opts }; @@ -296,13 +325,22 @@ export default () => { }, /** - * Get the current selector state + * Get the current selector state value * @returns {String} */ getState() { return this.em.getState(); }, + /** + * Get states + * @returns {Array} + * @private + */ + getStates() { + return this.config.states; + }, + /** * Get all selectors * @name getAll @@ -331,9 +369,11 @@ export default () => { const { em, selectorTags } = this; const config = this.getConfig(); const el = selectorTags && selectorTags.el; + this.selected.reset(selectors); this.selectorTags = new ClassTagsView({ el, - collection: new Selectors(selectors || [], { em, config }), + collection: this.selected, + module: this, config }); @@ -341,13 +381,75 @@ export default () => { }, destroy() { - const { selectorTags } = this; + const { selectorTags, model } = this; const all = this.getAll(); + model.stopListening(); all.stopListening(); all.reset(); selectorTags && selectorTags.remove(); this.em = {}; this.selectorTags = {}; + }, + + // __toSync(common = []) { + // const cmpFirst = this.getConfig().componentFirst; + // const cmp = this.em.getSelected(); + // let result = null; + + // if (cmp && cmpFirst && common.length) { + // const style = cmp.getStyle(); + // result = !isEmpty(style) ? style : null; + // } + + // return result; + // }, + + /** + * Get common selectors from the current selection. + * @return {Array} + * @private + */ + __getCommon() { + return this.__getCommonSelectors(this.em.getSelectedAll()); + }, + + __getCommonSelectors(components, opts = {}) { + const selectors = components + .map(cmp => cmp.getSelectors && cmp.getSelectors().getValid(opts)) + .filter(Boolean); + return this.__common(...selectors); + }, + + __common(...args) { + if (!args.length) return []; + if (args.length === 1) return args[0]; + if (args.length === 2) + return args[0].filter(item => args[1].indexOf(item) >= 0); + + return args + .slice(1) + .reduce((acc, item) => this.__common(acc, item), args[0]); + }, + + getSelected() { + return [...this.selected.models]; + }, + + addSelected(props) { + const added = this.add(props); + // TODO: target should be the one from StyleManager + this.em.getSelectedAll().forEach(target => { + target.getSelectors().add(added); + }); + // TODO: update selected collection + }, + + removeSelected(selector) { + this.em.getSelectedAll().forEach(trg => { + !selector.get('protected') && + trg && + trg.getSelectors().remove(selector); + }); } }; }; diff --git a/src/selector_manager/view/ClassTagView.js b/src/selector_manager/view/ClassTagView.js index e31caa491..c72e8dd4c 100644 --- a/src/selector_manager/view/ClassTagView.js +++ b/src/selector_manager/view/ClassTagView.js @@ -23,9 +23,10 @@ export default Backbone.View.extend({ 'focusout [data-tag-name]': 'endEditTag' }, - initialize(o) { + initialize(o = {}) { const config = o.config || {}; this.config = config; + this.module = o.module; this.coll = o.coll || null; this.pfx = config.stylePrefix || ''; this.ppfx = config.pStylePrefix || ''; @@ -97,11 +98,7 @@ export default Backbone.View.extend({ * @private */ removeTag() { - const { em, model } = this; - const targets = em && em.getSelectedAll(); - targets.forEach(sel => { - !model.get('protected') && sel && sel.getSelectors().remove(model); - }); + this.module.removeSelected(this.model); }, /** diff --git a/src/selector_manager/view/ClassTagsView.js b/src/selector_manager/view/ClassTagsView.js index 965e4e082..fd8f7cac8 100644 --- a/src/selector_manager/view/ClassTagsView.js +++ b/src/selector_manager/view/ClassTagsView.js @@ -66,6 +66,7 @@ export default Backbone.View.extend({ const { em } = this.config; const coll = this.collection; this.target = this.config.em; + this.module = o.module; this.em = em; const toList = 'component:toggled component:update:classes'; const toListCls = 'component:update:classes change:state'; @@ -212,27 +213,17 @@ export default Backbone.View.extend({ this.collection.reset(selectors); this.updateStateVis(trgs); - + this.module.__trgCustom(); return selectors; }, getCommonSelectors({ targets, opts = {} } = {}) { const trgs = targets || this.getTargets(); - const selectors = trgs - .map(tr => tr.getSelectors && tr.getSelectors().getValid(opts)) - .filter(i => i); - return this._commonSelectors(...selectors); + return this.module.__getCommonSelectors(trgs, opts); }, _commonSelectors(...args) { - if (!args.length) return []; - if (args.length === 1) return args[0]; - if (args.length === 2) - return args[0].filter(item => args[1].indexOf(item) >= 0); - - return args - .slice(1) - .reduce((acc, item) => this._commonSelectors(acc, item), args[0]); + return this.module.__common(...args); }, checkSync: debounce(function() { @@ -332,23 +323,12 @@ export default Backbone.View.extend({ * @param {Object} e * @private */ - addNewTag(label) { - const { em } = this; - - if (!label.trim()) return; - - if (em) { - const sm = em.get('SelectorManager'); - const model = sm.add({ label }); - - this.getTargets().forEach(target => { - target.getSelectors().add(model); - this.collection.add(model); - this.updateStateVis(); - }); - } - + addNewTag(value) { + const label = value.trim(); + if (!label) return; + this.module.addSelected({ label }); this.endNewTag(); + // this.updateStateVis(); // Check if required }, /** @@ -364,7 +344,8 @@ export default Backbone.View.extend({ const rendered = new ClassTagView({ model, config: this.config, - coll: this.collection + coll: this.collection, + module: this.module }).render().el; fragment ? fragment.appendChild(rendered) : classes.append(rendered); diff --git a/src/style_manager/index.js b/src/style_manager/index.js index eb75c804d..fa5abfe3d 100644 --- a/src/style_manager/index.js +++ b/src/style_manager/index.js @@ -416,6 +416,11 @@ export default () => { return SectView.setTarget(target, opts); }, + getTargets() { + const { propTarget } = SectView || {}; + return (propTarget && propTarget.targets) || []; + }, + getEmitter() { return SectView.propTarget; },