packages/esl/src/esl-mixin-element/ui/esl-mixin-element.ts
import {prop} from '../../esl-utils/decorators';import {setAttr} from '../../esl-utils/dom/attr';import {ExportNs} from '../../esl-utils/environment/export-ns';import {ESLEventUtils} from '../../esl-utils/dom/events';import {CSSClassUtils} from '../../esl-utils/dom/class'; import {ESLMixinRegistry} from './esl-mixin-registry';import {ESLMixinAttributesObserver} from './esl-mixin-attr'; import type { ESLEventListener, ESLListenerCriteria, ESLListenerDescriptor, ESLListenerHandler} from '../../esl-utils/dom/events';import type {ESLBaseComponent} from '../../esl-utils/abstract/component';import type {ESLDomElementRelated} from '../../esl-utils/abstract/dom-target'; /** * Base class for mixin elements. * Mixin elements attaches to the DOM element via attribute. * Allows multiple mixin elements per one DOM element */@ExportNs('Mixin')export class ESLMixinElement implements ESLBaseComponent, ESLDomElementRelated { /** Root attribute to identify mixin targets. Should contain dash in the name. */ static is: string; /** Additional observed attributes */ static observedAttributes: string[] = []; /** Event to indicate component significant state change that may affect other components state */ @prop('esl:refresh') public REFRESH_EVENT: string; public constructor( public readonly $host: HTMLElement ) {} /** Callback of mixin instance initialization */ protected connectedCallback(): void { ESLMixinAttributesObserver.observe(this); ESLEventUtils.subscribe(this); } /** Callback to execute on mixin instance destroy */ protected disconnectedCallback(): void { ESLMixinAttributesObserver.unobserve(this); ESLEventUtils.unsubscribe(this); } /** * Callback to handle changing of additional attributes. * Happens when attribute accessed for writing independently of the actual value change */ protected attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {} /** Subscribes (or resubscribes) all known descriptors that matches criteria */ public $$on(criteria: ESLListenerCriteria): ESLEventListener[]; /** Subscribes `handler` method marked with `@listen` decorator */ public $$on(handler: ESLListenerHandler): ESLEventListener[]; /** Subscribes `handler` function by the passed DOM event descriptor {@link ESLListenerDescriptor} or event name */Identical blocks of code found in 2 locations. Consider refactoring. public $$on<EType extends keyof ESLListenerEventMap>( event: EType | ESLListenerDescriptor<EType>, handler: ESLListenerHandler<ESLListenerEventMap[EType]> ): ESLEventListener[]; public $$on(event: any, handler?: any): ESLEventListener[] { return ESLEventUtils.subscribe(this, event, handler); } /** Unsubscribes event listener */ public $$off(...condition: ESLListenerCriteria[]): ESLEventListener[] { return ESLEventUtils.unsubscribe(this, ...condition); } /** * Gets or sets CSS classes for the `$host` * @param cls - CSS classes query {@link CSSClassUtils} * @param value - boolean to set CSS class(es) state or undefined to skip mutation * @returns current classes state or passed state */ public $$cls(cls: string, value?: boolean): boolean { if (value === undefined) return CSSClassUtils.has(this.$host, cls); CSSClassUtils.toggle(this.$host, cls, value); return value; } /** * Gets or sets attribute for the `$host`. * If the `value` param is undefined then skips mutation. * @param name - attribute name * @param value - string attribute value, boolean attribute state or `null` to delete attribute * @returns current attribute value or previous value for mutation */ public $$attr(name: string, value?: null | boolean | string): string | null { const prevValue = this.$host.getAttribute(name); if (value !== undefined) setAttr(this, name, value); return prevValue; } /** * Dispatches component custom event on the `$host`. * Uses 'esl:' prefix for event name, overridable to customize event namespaces. * @param eventName - event name * @param eventInit - custom event init. See {@link CustomEventInit} */ public $$fire(eventName: string, eventInit?: CustomEventInit): boolean { return ESLEventUtils.dispatch(this.$host, eventName, eventInit); } /** Register current mixin definition */ public static register(): void { (new ESLMixinRegistry()).register(this); } // Public Registry API public static readonly get = ESLMixinRegistry.get; public static readonly getAll = ESLMixinRegistry.getAll;} export type ESLMixinElementInternal = ESLMixinElement & { connectedCallback(): void; disconnectedCallback(): void; attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;}; export type ESLMixinElementConstructable = typeof ESLMixinElement & (new($root: HTMLElement) => ESLMixinElement);