UNPKG

@squirrel-forge/ui-core

Version:

A collection of interface, classes, functions and abstracts made for the browser and babel compatible.

190 lines (164 loc) 4.72 kB
/** * Requires */ import { Exception, isPojo, mergeObject, requireUniqid } from '@squirrel-forge/ui-util'; /** * Element class states exception * @class * @extends Exception */ class ElementClassStatesException extends Exception {} /** * @typedef {Object|true} ElementClassStateDefinition - Component state definition * @property {undefined|null|string} classOn - CSS class to set when active * @property {undefined|null|Array<string>} unsets - Unset given states when this one becomes active */ /** * Element class states * @class */ export class ElementClassStates { /** * Class states * @private * @property * @type {null|Object} */ #states = null; /** * Auto unset reference data * @private * @property * @type {Object} */ #autoUnsetRefs = {}; /** * Default timeout delay * @private * @property * @type {number} */ #defaultTimeout = 600; /** * Constructor * @constructor * @param {null|Object} states - States map */ constructor( states = null ) { if ( !isPojo( states ) ) { throw new ElementClassStatesException( 'Argument states must be null or a plain Object' ); } this.#states = states; } /** * Direct states access * @public * @return {Object} - States data object */ get exposed() { return this.#states; } /** * Extend states * @public * @param {Object} states - States map * @return {void} */ extend( states ) { mergeObject( this.#states, states, true, true, true, false ); } /** * State active * @public * @param {string} name - State name * @param {HTMLElement} element - Element target * @return {boolean} - State is active */ is( name, element ) { const state = this.get( name ); if ( state.classOn ) { return element.classList.contains( state.classOn ); } return false; } /** * State defined * @public * @param {string} name - State name * @return {boolean} - State exists */ has( name ) { return !!this.#states[ name ]; } /** * Get state info * @public * @param {string} name - State name * @return {Object} - State info object */ get( name ) { const state = this.#states[ name ]; if ( !state ) throw new ElementClassStatesException( 'Unknown state: ' + name ); return state; } /** * Set state by name * @public * @param {string} name - State name * @param {HTMLElement} element - Element target * @return {void} */ set( name, element ) { const state = this.get( name ); // Unset states if ( state.unsets instanceof Array ) { for ( let i = 0; i < state.unsets.length; i++ ) { this.unset( state.unsets[ i ], element ); } } // Set state class if ( state.classOn ) element.classList.add( state.classOn ); // Callback on if ( state.callbackOn ) state.callbackOn( name, element, state ); // Automatic unset if ( state.autoUnset ) { // Get or create unique element id const state_unset_id = requireUniqid( element, 'ecs-', true ) + '-' + name; // Clear any existing timeout this.#clear_auto_unset( state_unset_id ); // Setup new timeout for remove const timeout = typeof state.autoUnset === 'number' ? state.autoUnset : this.#defaultTimeout; this.#autoUnsetRefs[ state_unset_id ] = window.setTimeout( () => { // Clear and unset state if reached this.#clear_auto_unset( state_unset_id ); this.unset( name, element ); }, timeout ); } } /** * Clear auto unset timeout * @private * @param {string} id - Element id * @return {void} */ #clear_auto_unset( id ) { if ( this.#autoUnsetRefs[ id ] ) { window.clearTimeout( this.#autoUnsetRefs[ id ] ); this.#autoUnsetRefs[ id ] = null; } } /** * Set state by name * @public * @param {string} name - State name * @param {HTMLElement} element - Element target * @return {void} */ unset( name, element ) { const state = this.get( name ); // Complex state options if ( state.classOn ) element.classList.remove( state.classOn ); // Callback off if ( state.callbackOff ) state.callbackOff( name, element, state ); } }