UNPKG

carbon-components

Version:

The Carbon Design System is IBM’s open-source design system for products and experiences.

132 lines (120 loc) 4.78 kB
/** * Copyright IBM Corp. 2016, 2018 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ export default function (ToMix) { /** * Mix-in class to manage events associated with states. * @class EventedState */ class EventedState extends ToMix { /* eslint-disable jsdoc/check-param-names */ /** * The internal implementation for {@link EventedState#changeState `.changeState()`}, performing actual change in state. * @param {string} [state] The new state. Can be an omitted, which means toggling. * @param {object} [detail] * The object that should be put to event details that is fired before/after changing state. * Can have a `group` property, which specifies what state to be changed. * @param {EventedState~changeStateCallback} callback The callback called once changing state is finished or is canceled. * @private */ _changeState() { throw new Error( '_changeState() should be overridden to perform actual change in state.' ); } /** * Changes the state of this component. * @param {string} [state] The new state. Can be an omitted, which means toggling. * @param {object} [detail] * The object that should be put to event details that is fired before/after changing state. * Can have a `group` property, which specifies what state to be changed. * @param {EventedState~changeStateCallback} [callback] The callback called once changing state is finished or is canceled. */ changeState(...args) { const state = typeof args[0] === 'string' ? args.shift() : undefined; const detail = Object(args[0]) === args[0] && typeof args[0] !== 'function' ? args.shift() : undefined; const callback = typeof args[0] === 'function' ? args.shift() : undefined; if ( typeof this.shouldStateBeChanged === 'function' && !this.shouldStateBeChanged(state, detail) ) { if (callback) { callback(null, true); } return; } const data = { group: detail && detail.group, state, }; const eventNameSuffix = [data.group, state] .filter(Boolean) .join('-') .split('-') // Group or state may contain hyphen .map((item) => item[0].toUpperCase() + item.substr(1)) .join(''); const eventStart = new CustomEvent( this.options[`eventBefore${eventNameSuffix}`], { bubbles: true, cancelable: true, detail, } ); const fireOnNode = (detail && detail.delegatorNode) || this.element; const canceled = !fireOnNode.dispatchEvent(eventStart); if (canceled) { if (callback) { const error = new Error( `Changing state (${JSON.stringify(data)}) has been canceled.` ); error.canceled = true; callback(error); } } else { const changeStateArgs = [state, detail].filter(Boolean); this._changeState(...changeStateArgs, () => { fireOnNode.dispatchEvent( new CustomEvent(this.options[`eventAfter${eventNameSuffix}`], { bubbles: true, cancelable: true, detail, }) ); if (callback) { callback(); } }); } } /* eslint-enable jsdoc/check-param-names */ /** * Tests if change in state should happen or not. * Classes inheriting {@link EventedState `EventedState`} should override this function. * @function EventedState#shouldStateBeChanged * @param {string} [state] The new state. Can be an omitted, which means toggling. * @param {object} [detail] * The object that should be put to event details that is fired before/after changing state. * Can have a `group` property, which specifies what state to be changed. * @returns {boolean} * `false` if change in state shouldn't happen, e.g. when the given new state is the same as the current one. */ } /** * The callback called once changing state is finished or is canceled. * @callback EventedState~changeStateCallback * @param {Error} error * An error object with `true` in its `canceled` property if changing state is canceled. * Cancellation happens if the handler of a custom event, that is fired before changing state happens, * calls `.preventDefault()` against the event. * @param {boolean} keptState * `true` if the call to {@link EventedState#changeState `.changeState()`} didn't cause actual change in state. */ return EventedState; }