Skip to content

Commit

Permalink
feat(esl-mixin-element): improve observation mechanism of mixin's obs…
Browse files Browse the repository at this point in the history
…erved attributes
  • Loading branch information
ala-n committed May 10, 2023
1 parent 6376721 commit e7ed2ab
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 26 deletions.
45 changes: 45 additions & 0 deletions src/modules/esl-mixin-element/ui/esl-mixin-attr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {ESLMixinRegistry} from './esl-mixin-registry';
import type {ESLMixinElement, ESLMixinElementInternal} from './esl-mixin-element';

let instance: ESLMixinAttributesObserver;
export class ESLMixinAttributesObserver {
protected observer = new MutationObserver(
(records: MutationRecord[]) => records.forEach(this.handleRecord, this)
);

constructor() {
if (instance) return instance;
// eslint-disable-next-line @typescript-eslint/no-this-alias
instance = this;
}

protected handleRecord(record: MutationRecord): void {
const name = record.attributeName;
const target = record.target as HTMLElement;
if (!name || !target) return;
const mixins = ESLMixinRegistry.getAll(target) as ESLMixinElementInternal[];
for (const mixin of mixins) {
const observed = (mixin.constructor as typeof ESLMixinElement).observedAttributes;
if (!observed.includes(record.attributeName)) return;
mixin.attributeChangedCallback(name, record.oldValue, target.getAttribute(name));
}
}

public observe(mixin: ESLMixinElement): void {
const {observedAttributes} = mixin.constructor as typeof ESLMixinElement;
if (!observedAttributes.length) return;
this.observer.observe(mixin.$host, {
attributes: true,
attributeFilter: observedAttributes,
attributeOldValue: true
});
}

public unobserve(mixin: ESLMixinElement): void {
this.observer.observe(mixin.$host, {
attributes: true,
attributeFilter: [],
attributeOldValue: true
});
}
}
31 changes: 5 additions & 26 deletions src/modules/esl-mixin-element/ui/esl-mixin-element.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {setAttr} from '../../esl-utils/dom/attr';
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 {ExportNs} from '../../esl-utils/environment/export-ns';

import {ESLMixinRegistry} from './esl-mixin-registry';
import {ESLMixinAttributesObserver} from './esl-mixin-attr';

import type {
ESLEventListener,
Expand All @@ -30,32 +31,19 @@ export class ESLMixinElement implements ESLBaseComponent, ESLDomElementRelated {
/** Event to indicate component significant state change that may affect other components state */
@prop('esl:refresh') public REFRESH_EVENT: string;

/** Additional attributes observer */
private _attr$$: MutationObserver;

public constructor(
public readonly $host: HTMLElement
) {}

/** Callback of mixin instance initialization */
protected connectedCallback(): void {
const {observedAttributes} = this.constructor as typeof ESLMixinElement;
if (observedAttributes.length) {
this._attr$$ = new MutationObserver(this._onAttrMutation.bind(this));
this._attr$$.observe(this.$host, {
attributes: true,
attributeFilter: observedAttributes,
attributeOldValue: true
});
}

(new ESLMixinAttributesObserver()).observe(this);
ESLEventUtils.subscribe(this);
}

/** Callback to execute on mixin instance destroy */
protected disconnectedCallback(): void {
if (this._attr$$) this._attr$$.disconnect();

(new ESLMixinAttributesObserver()).unobserve(this);
ESLEventUtils.unsubscribe(this);
}

Expand All @@ -65,15 +53,6 @@ export class ESLMixinElement implements ESLBaseComponent, ESLDomElementRelated {
*/
protected attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {}

/** Attribute change mutation record processor */
private _onAttrMutation(records: MutationRecord[]): void {
records.forEach(({attributeName, oldValue}: MutationRecord) => {
if (!attributeName) return;
const newValue = this.$$attr(attributeName);
this.attributeChangedCallback(attributeName, oldValue, newValue);
});
}

/** 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 */
Expand Down

0 comments on commit e7ed2ab

Please # to comment.