diff --git a/src/controllers/add-component-controller.js b/src/controllers/add-component-controller.js index 6e4e343..723bd0a 100644 --- a/src/controllers/add-component-controller.js +++ b/src/controllers/add-component-controller.js @@ -3,9 +3,17 @@ var dom = require('@nymag/dom'), references = require('references'), utils = require('../services/utils'), createService = require('../services/create-service'), + statusService = require('../services/status-service'), proto = AddComponent.prototype; - +/** + * Controller which handles adding a component to a Space. + * Creates a pane, handles events when clicking an item in + * the pane using the `createService`. + * + * @param {Object} spaceParent + * @param {Function} callback + */ function AddComponent(spaceParent, callback) { this.parent = spaceParent; @@ -33,7 +41,11 @@ proto.launchPane = function () { }; /** - * [listItemClick description] + * The click handler for an item in the pane. + * Creats's a new component wrapped in the proper + * Logic and then invokes the callback passed in to + * the constructor. + * * @param {string} id * @return {Promise} */ @@ -68,14 +80,20 @@ proto.listItemClick = function (id) { }); }; +/** + * When a new component is added it should be focused on + * so that a user can begin editing it. + * + * @param {Element} targetEl + */ proto.makeNewComponentActive = function (targetEl) { - var logics = dom.findAll(this.parent, '.space-logic'); + var logics = utils.findAllLogic(this.parent); _.forEach(logics, (logic) => { - logic.classList.remove(references.spaceEditingClass); + statusService.removeEditing(logic); }); - targetEl.classList.add(references.spaceEditingClass); + statusService.setEditing(targetEl); }; module.exports = function (spaceParent, callback) { diff --git a/src/controllers/space-controller.js b/src/controllers/space-controller.js index 0aea94b..04e47a0 100644 --- a/src/controllers/space-controller.js +++ b/src/controllers/space-controller.js @@ -1,9 +1,9 @@ var dom = require('@nymag/dom'), _ = require('lodash'), - references = require('references'), utils = require('../services/utils'), selectorService = require('../services/selector'), saveService = require('../services/save-service'), + statusService = require('../services/status-service'), proto = SpaceController.prototype; function SpaceController(el, parent) { @@ -35,8 +35,8 @@ function SpaceController(el, parent) { return; } - isLogicElement = componentElement.classList.contains(references.spaceLogicClass); - parentIsLogic = componentElement.parentElement.classList.contains(references.spaceLogicClass); + isLogicElement = statusService.isLogic(componentElement); + parentIsLogic = statusService.isLogic(componentElement.parentElement); if (!isLogicElement && parentIsLogic) { saveService.onLogicWrappedSave.call(this, componentElement); @@ -61,12 +61,13 @@ proto.init = function () { * @returns {SpaceController} */ proto.findFirstActive = function () { - var activeChild = dom.find(this.el, '.space-logic-active'); + var activeChild = statusService.findActive(this.el), + lastActive = this.childrenLogics[this.childrenLogics.length - 1]; if (activeChild) { - activeChild.classList.add('space-logic-editing'); + statusService.setEditing(activeChild); } else if (!activeChild && this.childrenLogics.length) { - this.childrenLogics[this.childrenLogics.length - 1].classList.add('space-logic-active', 'space-logic-editing'); + statusService.setActiveAndEditing(lastActive); } return this; @@ -89,7 +90,7 @@ proto.onAddCallback = function (newEl) { * @returns {SpaceController} */ proto.updateChildrenCount = function () { - this.childrenLogics = dom.findAll(this.el, '.space-logic'); + this.childrenLogics = utils.findAllLogic(this.el); return this; }; @@ -163,7 +164,7 @@ proto.findLogicCount = function () { * @returns {SpaceController} */ proto.updateLogicCount = function (component) { - this.el = component ? dom.closest(component, '.clay-space') : this.el; + this.el = component ? dom.closest(component, '[data-space]') : this.el; this.findLogicCount() .findFirstActive(); @@ -177,7 +178,7 @@ proto.updateLogicCount = function (component) { */ proto.clearEditing = function () { _.each(this.childrenLogics, (logic) => { - logic.classList.remove('space-logic-editing'); + statusService.removeEditing(logic); }); return this; diff --git a/src/controllers/space-settings-controller.js b/src/controllers/space-settings-controller.js index 57a729b..947d491 100644 --- a/src/controllers/space-settings-controller.js +++ b/src/controllers/space-settings-controller.js @@ -6,6 +6,7 @@ var dom = require('@nymag/dom'), removeService = require('../services/remove-service'), createService = require('../services/create-service'), logicReadoutService = require('../services/logic-readout-service'), + statusService = require('../services/status-service'), proto = BrowseController.prototype; /** @@ -70,7 +71,7 @@ proto.addComponent = function () { * @param {Element} el */ proto.findChildrenMakeList = function (el) { - this.childComponents = dom.findAll(el, '.space-logic'); + this.childComponents = utils.findAllLogic(el); this.componentList = this.makeList(this.childComponents); }; @@ -81,7 +82,7 @@ proto.findChildrenMakeList = function (el) { */ proto.markActiveInList = function (listHtml) { _.each(this.childComponents, function (logicComponent) { - if (logicComponent.classList.contains(references.spaceEditingClass)) { + if (statusService.isEditing(logicComponent)) { dom.find(listHtml, '[data-item-id="' + logicComponent.getAttribute('data-uri') + '"]').classList.add('active'); } }); @@ -174,7 +175,7 @@ proto.reorder = function (id, newIndex, oldIndex) { */ proto.renderUpdatedSpace = function (resp) { var space = this.el, - spaceChildren = dom.findAll(this.el, '.space-logic'), + spaceChildren = utils.findAllLogic(this.el), spaceOnPage = dom.find(document, '[data-uri="' + this.spaceRef + '"]'); _.forEach(spaceChildren, function (child) { @@ -199,10 +200,10 @@ proto.listItemClick = function (id) { var newActive = dom.find(this.el, `[data-uri="${id}"]`); _.each(this.childComponents, function (el) { - el.classList.remove(references.spaceEditingClass); + statusService.removeEditing(el); }); - newActive.classList.add(references.spaceEditingClass); + statusService.setEditing(newActive); references.pane.close(); }; diff --git a/src/index.js b/src/index.js index de9a3a5..9aecabe 100644 --- a/src/index.js +++ b/src/index.js @@ -22,7 +22,8 @@ function updateSelector(el, options, parent) { } } -// Export the init entrypoint +window.kiln = window.kiln || {}; +window.kiln.plugins = window.kiln.plugins || {}; window.kiln.plugins['spaces-edit'] = function initSpaces() { window.kiln.on('add-selector', updateSelector); }; diff --git a/src/services/logic-readout-service.js b/src/services/logic-readout-service.js index ade8732..272e748 100644 --- a/src/services/logic-readout-service.js +++ b/src/services/logic-readout-service.js @@ -1,6 +1,11 @@ var _ = require('lodash'), dom = require('@nymag/dom'), - references = require('references'); + references = require('references'), + reservedAttributes = [ + 'logic', + 'logicActive', + 'logicEditing' + ]; // Attributes beginning with `logic` that are reserved for other parts of the application /** * Wrap logic groups in a div @@ -43,7 +48,7 @@ function logicReadouts(element) { logicString = ''; _.forIn(dataAttributes, function (value, key) { - if (_.startsWith(key, 'logic')) { + if (_.startsWith(key, 'logic') && !_.includes(reservedAttributes, key)) { logicString += createReadout(element, key, value); } }); diff --git a/src/services/references.js b/src/services/references.js index d84553a..9c318bf 100644 --- a/src/services/references.js +++ b/src/services/references.js @@ -10,9 +10,6 @@ referencesObj = _.assign({ spaceEdit: 'clay-space-edit', spacePrefix: 'clay-space', spaceClass: 'clay-space', - spaceLogicClass: 'space-logic', - spaceEditingClass: 'space-logic-editing', - spaceActiveClass: 'space-logic-active', dataAvailableSpaces: 'data-spaces-available', dataPaneTitle: 'data-space-browse-title', render: kilnServices.render, diff --git a/src/services/remove-service.js b/src/services/remove-service.js index 8565ca8..455f1f5 100644 --- a/src/services/remove-service.js +++ b/src/services/remove-service.js @@ -1,6 +1,8 @@ var dom = require('@nymag/dom'), + utils = require('./utils'), _ = require('lodash'), - references = require('references'); + references = require('references'), + statusService = require('./status-service'); /** * [removeLogic description] @@ -57,13 +59,13 @@ function removeIconClick(logic) { * @param {[type]} index [description] */ function findNextActive(index) { - this.childrenLogics = dom.findAll(this.el, '.space-logic'); + this.childrenLogics = utils.findAllLogic(this.el); this.findLogicCount(); if (this.childrenLogics[index]) { - this.childrenLogics[index].classList.add('space-logic-editing'); + statusService.setEditing(this.childrenLogics[index]); } else if (this.childrenLogics[index - 1]) { - this.childrenLogics[index - 1].classList.add('space-logic-editing'); + statusService.setEditing(this.childrenLogics[index - 1]); } else { if (window.confirm('You are removing the last component in this Space, this will remove the Space entirely from the page, is this ok?')) { removeSpace(this.el, this.parent); diff --git a/src/services/save-service.js b/src/services/save-service.js index 1fd62ce..193a9e4 100644 --- a/src/services/save-service.js +++ b/src/services/save-service.js @@ -1,7 +1,8 @@ var dom = require('@nymag/dom'), references = require('references'), createService = require('./create-service'), - selectorService = require('./selector'); + selectorService = require('./selector'), + statusService = require('./status-service'); function onLogicSave(logic, logicComponent) { var query = { currentUrl: window.location.href }; @@ -26,13 +27,18 @@ function onLogicSave(logic, logicComponent) { addComponentButton.addEventListener('click', selectorService.launchAddComponent.bind(null, newComponent, { ref: this.spaceRef }, this.parent)); - newComponent.classList.add(references.spaceEditingClass); - if (html.classList.contains(references.spaceActiveClass)) { - newComponent.classList.add(references.spaceActiveClass); + // Add the editing attribute + statusService.setEditing(newComponent); + // Was the original component active? Let's make + // sure the updated one is as well + if (statusService.isActive(html)) { + statusService.setActive(newComponent); } + // Close the pane references.pane.close(); + // Relaunch the pane selectorService.launchBrowsePane(this.el, { add: this.onAddCallback.bind(this), remove: this.onRemoveCallback.bind(this) diff --git a/src/services/select-space-parent.js b/src/services/select-space-parent.js index e995c0b..efcfbbc 100644 --- a/src/services/select-space-parent.js +++ b/src/services/select-space-parent.js @@ -2,7 +2,7 @@ var dom = require('@nymag/dom'), references = require('references'); function selectSpaceParent(el, e) { - var spaceParent = dom.closest(el, '.clay-space'), + var spaceParent = dom.closest(el, '[data-space]'), targetComponent = dom.closest(spaceParent.parentElement, '[data-uri]'); // Stop propagation to make sure the child diff --git a/src/services/selector.js b/src/services/selector.js index f717ffd..6a4bfed 100644 --- a/src/services/selector.js +++ b/src/services/selector.js @@ -5,6 +5,8 @@ var _ = require('lodash'), createService = require('./create-service'), removeService = require('./remove-service'), selectSpaceParent = require('./select-space-parent'), + statusService = require('./status-service'), + SpaceSettings = require('../controllers/space-settings-controller'); /** @@ -31,7 +33,7 @@ function addCreateSpaceButton(el, options, parent) { * @param {[type]} parent */ function launchAddComponent(element, options, parent) { - var spaceParent = dom.closest(element, '.clay-space'), + var spaceParent = dom.closest(element, '[data-space]'), availableComponents = spaceParent.getAttribute('data-components').split(','), paneContent = utils.createFilterableList(availableComponents, { click: createService.fakeAnAddToComponentList.bind(null, options, parent) @@ -47,7 +49,7 @@ function launchAddComponent(element, options, parent) { * @param {[type]} parent */ function addToComponentList(el, options, parent) { - var logics = el.classList.contains('space-logic') ? [el] : dom.findAll(el, '.space-logic'), + var logics = statusService.isLogic(el) ? [el] : utils.findAllLogic(el), bottom, addButton; @@ -155,6 +157,8 @@ function addRemoveButton(logic) { /** * Remove 'clay-space' from a component list * @param {Element} el + * + * TODO: Make the removal from component lists dynamic */ function stripSpaceFromComponentList(el) { var addButton = dom.find(el, '.selected-add'), diff --git a/src/services/status-service.js b/src/services/status-service.js new file mode 100644 index 0000000..87ca5c3 --- /dev/null +++ b/src/services/status-service.js @@ -0,0 +1,113 @@ +var dom = require('@nymag/dom'), + _ = require('lodash'), + ACTIVE = 'data-logic-active', + EDITING = 'data-logic-editing', + LOGIC = 'data-logic'; + +/** + * Add the active data attribute + * + * @param {Element} el + */ +function setActive(el) { + el.setAttribute(ACTIVE, ''); +} + +/** + * Add the editing data attribute + * + * @param {Element} el + */ +function setEditing(el) { + el.setAttribute(EDITING, ''); +} + +/** + * Add the active and editing data attribute + * + * @param {Element} el + */ +function setActiveAndEditing(el) { + setEditing(el); + setActive(el); +} + +/** + * Remove the editing data attribute + * + * @param {Element} el + */ +function removeEditing(el) { + el.removeAttribute(EDITING); +} + +/** + * Remove the active data attribute + * + * @param {Element} el + */ +function removeActive(el) { + el.removeAttribute(ACTIVE); +} + +/** + * Remove the active and editing data attribute + * + * @param {Element} el + */ +function removeActiveAndEditing(el) { + removeEditing(el); + removeActive(el); +} + +/** + * Find the first element with the active + * data attribute within an element + * + * @param {Element} el + * @return {Element} + */ +function findActive(el) { + return dom.find(el, `[${ACTIVE}]`); +} + +/** + * Test if a component has the active attribute + * + * @param {Element} el + * @return {Boolean} + */ +function isActive(el) { + return _.isString(el.getAttribute(ACTIVE)); +} + +/** + * Test if a component has the editing attribute + * + * @param {Element} el + * @return {Boolean} + */ +function isEditing(el) { + return _.isString(el.getAttribute(EDITING)); +} + +/** + * Test if a component has the logic attribute + * + * @param {Element} el + * @return {Boolean} + */ +function isLogic(el) { + return _.isString(el.getAttribute(LOGIC)); +} + +module.exports.setActive = setActive; +module.exports.setEditing = setEditing; +module.exports.setActiveAndEditing = setActiveAndEditing; +module.exports.removeActive = removeActive; +module.exports.removeEditing = removeEditing; +module.exports.removeActiveAndEditing = removeActiveAndEditing; +module.exports.findActive = findActive; +module.exports.isActive = isActive; +module.exports.isEditing = isEditing; +module.exports.isLogic = isLogic; diff --git a/src/services/utils.js b/src/services/utils.js index 1aa1a13..e9a50f8 100644 --- a/src/services/utils.js +++ b/src/services/utils.js @@ -1,4 +1,5 @@ -var _ = require('lodash'), +var dom = require('@nymag/dom'), + _ = require('lodash'), references = require('references'); /** @@ -69,8 +70,20 @@ function checkIfSpace(ref) { return _.includes(ref, references.spacePrefix); } +/** + * Return an array of all components with the + * `data-logic` attribute + * + * @param {Element} el + * @return {Array} + */ +function findAllLogic(el) { + return dom.findAll(el, '[data-logic]'); +} + module.exports.makeComponentListAttr = makeComponentListAttr; module.exports.spaceInComponentList = spaceInComponentList; module.exports.availableSpaces = availableSpaces; module.exports.createFilterableList = createFilterableList; module.exports.checkIfSpace = checkIfSpace; +module.exports.findAllLogic = findAllLogic; diff --git a/src/styleguide/styles.scss b/src/styleguide/styles.scss index 689120b..3f12c2c 100644 --- a/src/styleguide/styles.scss +++ b/src/styleguide/styles.scss @@ -1,13 +1,13 @@ $UI_PRIMARY: #B4A5BC; $UI_BUTTON: #694C79; -.clay-space > .component-selector, -.space-logic > .component-selector, -.clay-space > .space-logic { +[data-space] > .component-selector, +[data-logic] > .component-selector, +[data-space] > [data-logic] { display: none; } -.clay-space > .space-logic-editing { +[data-space] > [data-logic-editing] { display: block; }